commit 948ef471062c8e5f3e24eb625b34726cecb7044e Author: upstream-mirror Date: Tue Sep 15 17:23:35 2026 +0500 snapshot of https://github.com/ggml-org/whisper.cpp.git @ da54572229bcf64ba367d96c7ef15770376c4280 (2026-09-15) diff --git a/.devops/cublas.Dockerfile b/.devops/cublas.Dockerfile new file mode 100644 index 0000000..1fae256 --- /dev/null +++ b/.devops/cublas.Dockerfile @@ -0,0 +1,28 @@ +ARG UBUNTU_VERSION=22.04 + +# This needs to generally match the container host's environment. +ARG CUDA_VERSION=11.7.1 + +# Target the CUDA build image +ARG BASE_CUDA_DEV_CONTAINER=nvidia/cuda:${CUDA_VERSION}-devel-ubuntu${UBUNTU_VERSION} + +FROM ${BASE_CUDA_DEV_CONTAINER} as build + +# Unless otherwise specified, we make a fat build. +ARG CUDA_DOCKER_ARCH=all + +RUN apt-get update && \ + apt-get install -y build-essential git cmake libsdl2-dev wget git + +WORKDIR /app + +COPY . . + +# Set nvcc architecture +ENV CUDA_DOCKER_ARCH=${CUDA_DOCKER_ARCH} +# Enable cuBLAS +ENV GGML_CUDA=1 + +RUN make base.en + +ENTRYPOINT ["/app/main"] diff --git a/.devops/main-cuda.Dockerfile b/.devops/main-cuda.Dockerfile new file mode 100644 index 0000000..0d065df --- /dev/null +++ b/.devops/main-cuda.Dockerfile @@ -0,0 +1,50 @@ +ARG UBUNTU_VERSION=22.04 +# This needs to generally match the container host's environment. +ARG CUDA_VERSION=13.0.0 +# Target the CUDA build image +ARG BASE_CUDA_DEV_CONTAINER=nvidia/cuda:${CUDA_VERSION}-devel-ubuntu${UBUNTU_VERSION} +# Target the CUDA runtime image +ARG BASE_CUDA_RUN_CONTAINER=nvidia/cuda:${CUDA_VERSION}-runtime-ubuntu${UBUNTU_VERSION} + +FROM ${BASE_CUDA_DEV_CONTAINER} AS build +WORKDIR /app + +# Unless otherwise specified, we make a fat build. +ARG CUDA_DOCKER_ARCH=all +# Set nvcc architecture +ENV CUDA_DOCKER_ARCH=${CUDA_DOCKER_ARCH} + +RUN apt-get update && \ + apt-get install -y build-essential libsdl2-dev wget cmake git \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/* + +# The build stage has no real GPU/driver, so the linker needs the +# forward-compat libcuda stub to resolve CUDA driver-API symbols. +# Ref: https://stackoverflow.com/a/53464012 +ENV CUDA_MAIN_VERSION=13.0 +ENV LD_LIBRARY_PATH /usr/local/cuda-${CUDA_MAIN_VERSION}/compat:$LD_LIBRARY_PATH + +COPY .. . +# Enable cuBLAS +RUN --mount=type=secret,id=HF_TOKEN,required=false,env=HF_TOKEN make base.en CMAKE_ARGS="-DGGML_CUDA=1 -DCMAKE_CUDA_ARCHITECTURES='75;80;86;90'" + +RUN find /app/build -name "*.o" -delete && \ + find /app/build -name "*.a" -delete && \ + rm -rf /app/build/CMakeFiles && \ + rm -rf /app/build/cmake_install.cmake && \ + rm -rf /app/build/_deps + +FROM ${BASE_CUDA_RUN_CONTAINER} AS runtime +WORKDIR /app + +RUN apt-get update && \ + apt-get install -y curl ffmpeg wget cmake git \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/* + +COPY --from=build /app /app +RUN du -sh /app/* +RUN find /app -type f -size +100M +ENV PATH=/app/build/bin:$PATH +ENTRYPOINT [ "bash", "-c" ] diff --git a/.devops/main-intel.Dockerfile b/.devops/main-intel.Dockerfile new file mode 100644 index 0000000..a0c04ad --- /dev/null +++ b/.devops/main-intel.Dockerfile @@ -0,0 +1,29 @@ +ARG ONEAPI_VERSION=2025.3.3-0-devel-ubuntu24.04 + +FROM intel/deep-learning-essentials:$ONEAPI_VERSION AS build +WORKDIR /app + +RUN apt-get update && \ + apt-get install -y build-essential libsdl2-dev wget cmake git \ + && rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/* + +COPY .. . +# Enable SYCL +ARG GGML_SYCL_F16=OFF +RUN --mount=type=secret,id=HF_TOKEN,required=false,env=HF_TOKEN \ + if [ "${GGML_SYCL_F16}" = "ON" ]; then \ + echo "GGML_SYCL_F16 is set" \ + && export OPT_SYCL_F16="-DGGML_SYCL_F16=ON"; \ + fi && \ + make base.en CMAKE_ARGS="-DGGML_SYCL=1 -DCMAKE_C_COMPILER=icx -DCMAKE_CXX_COMPILER=icpx ${OPT_SYCL_F16}" + +FROM intel/deep-learning-essentials:$ONEAPI_VERSION AS runtime +WORKDIR /app + +RUN apt-get update && \ + apt-get install -y curl ffmpeg libsdl2-dev wget cmake git \ + && rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/* + +COPY --from=build /app /app +ENV PATH=/app/build/bin:$PATH +ENTRYPOINT [ "bash", "-c" ] diff --git a/.devops/main-musa.Dockerfile b/.devops/main-musa.Dockerfile new file mode 100644 index 0000000..c683678 --- /dev/null +++ b/.devops/main-musa.Dockerfile @@ -0,0 +1,40 @@ +ARG UBUNTU_VERSION=22.04 +# This needs to generally match the container host's environment. +ARG MUSA_VERSION=rc4.2.0 +# Target the MUSA build image +ARG BASE_MUSA_DEV_CONTAINER=mthreads/musa:${MUSA_VERSION}-devel-ubuntu${UBUNTU_VERSION}-amd64 +# Target the MUSA runtime image +ARG BASE_MUSA_RUN_CONTAINER=mthreads/musa:${MUSA_VERSION}-runtime-ubuntu${UBUNTU_VERSION}-amd64 + +FROM ${BASE_MUSA_DEV_CONTAINER} AS build +WORKDIR /app + +RUN apt-get update && \ + apt-get install -y build-essential libsdl2-dev wget cmake git && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/* /tmp/* /var/tmp/* + +COPY .. . +# Enable muBLAS +RUN --mount=type=secret,id=HF_TOKEN,required=false,env=HF_TOKEN make base.en CMAKE_ARGS="-DGGML_MUSA=1" + +RUN find /app/build -name "*.o" -delete && \ + find /app/build -name "*.a" -delete && \ + rm -rf /app/build/CMakeFiles && \ + rm -rf /app/build/cmake_install.cmake && \ + rm -rf /app/build/_deps + +FROM ${BASE_MUSA_RUN_CONTAINER} AS runtime +WORKDIR /app + +RUN apt-get update && \ + apt-get install -y curl ffmpeg wget cmake git && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/* /tmp/* /var/tmp/* + +COPY --from=build /app/build/bin /app/build/bin +COPY --from=build /app/samples /app/samples +COPY --from=build /app/models /app/models + +ENV PATH=/app/build/bin:$PATH +ENTRYPOINT [ "bash", "-c" ] diff --git a/.devops/main-rocm.Dockerfile b/.devops/main-rocm.Dockerfile new file mode 100644 index 0000000..efe29e1 --- /dev/null +++ b/.devops/main-rocm.Dockerfile @@ -0,0 +1,81 @@ +# build stage +FROM registry.fedoraproject.org/fedora:44 AS build + +WORKDIR /app + +# ROCm 7.14 repository (TheRock release stream) +RUN <<'EOF' +tee /etc/yum.repos.d/rocm.repo </dev/null) + if [ -z "$CACHES" ]; then + echo "No caches found with key prefix: ${{ inputs.key }}" + exit 0 + fi + while read -r id key; do + echo "Deleting cache: $id ($key)" + gh cache delete "$id" + done <<< "$CACHES" diff --git a/.github/workflows/bindings-go.yml b/.github/workflows/bindings-go.yml new file mode 100644 index 0000000..91f869e --- /dev/null +++ b/.github/workflows/bindings-go.yml @@ -0,0 +1,22 @@ +name: Bindings Tests (Go) +on: + push: + paths: + - bindings/go/** + - include/whisper.h + pull_request: + paths: + - bindings/go/** + - include/whisper.h + +jobs: + ubuntu-22: + runs-on: ubuntu-22.04 + steps: + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 + with: + go-version: '^1.23' + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + - run: | + cd bindings/go + make test diff --git a/.github/workflows/bindings-ruby.yml b/.github/workflows/bindings-ruby.yml new file mode 100644 index 0000000..8cdb7a8 --- /dev/null +++ b/.github/workflows/bindings-ruby.yml @@ -0,0 +1,32 @@ +name: Bindings Tests (Ruby) + +on: + push: + branches: + - master + paths: + - bindings/ruby/** + - include/whisper.h + - examples/common-whisper.h + - ggml/include/ggml.h + + pull_request: + types: [opened, synchronize, reopened] + paths: + - bindings/ruby/** + - include/whisper.h + - examples/common-whisper.h + - ggml/include/ggml.h + +jobs: + ubuntu-22: + runs-on: ubuntu-22.04 + defaults: + run: + working-directory: bindings/ruby + steps: + - uses: ruby/setup-ruby@afeafc3d1ab54a631816aba4c914a0081c12ff2f # v1.310.0 + with: + ruby-version: '3.3' + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + - run: rake test diff --git a/.github/workflows/build-android.yml b/.github/workflows/build-android.yml new file mode 100644 index 0000000..571c358 --- /dev/null +++ b/.github/workflows/build-android.yml @@ -0,0 +1,80 @@ +name: CI (android) + +on: + workflow_dispatch: # allows manual triggering + push: + branches: + - master + paths: ['.github/workflows/build-android.yml', + '**/CMakeLists.txt', + '**/*.h', + '**/*.hpp', + '**/*.c', + '**/*.cpp', + '**/*.java'] + + pull_request: + types: [opened, synchronize, reopened] + paths-ignore: + - 'bindings/ruby/**' # handled by bindings-ruby.yml + - 'bindings/go/**' # handled by bindings-go.yml + - 'examples/addon.node/**' # handled by examples.yml + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }} + cancel-in-progress: true + +jobs: + android: + runs-on: ubuntu-22.04 + + steps: + - name: Clone + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + with: + path: whisper + + - name: Install Java + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 + with: + distribution: zulu + java-version: 21 + + - name: Setup Android SDK + uses: android-actions/setup-android@40fd30fb8d7440372e1316f5d1809ec01dcd3699 # v4.0.1 + + - name: Build + run: | + cd whisper/examples/whisper.android + ./gradlew assembleRelease --no-daemon + + - name: Build with external ggml + run: | + export PATH_TO_GGML=$PWD/ggml + cd whisper/examples/whisper.android + ./gradlew assembleRelease --no-daemon + + android_java: + runs-on: ubuntu-22.04 + + steps: + - name: Clone + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: set up JDK 11 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 + with: + java-version: '11' + distribution: 'temurin' + cache: gradle + + - name: Setup Android SDK + uses: android-actions/setup-android@40fd30fb8d7440372e1316f5d1809ec01dcd3699 # v4.0.1 + with: + cmdline-tools-version: 9.0 + + - name: Build + run: | + cd examples/whisper.android.java + chmod +x ./gradlew + ./gradlew assembleRelease diff --git a/.github/workflows/build-clang.yml b/.github/workflows/build-clang.yml new file mode 100644 index 0000000..396b097 --- /dev/null +++ b/.github/workflows/build-clang.yml @@ -0,0 +1,122 @@ +name: CI (clang) + +on: + workflow_dispatch: # allows manual triggering + push: + branches: + - master + paths: ['.github/workflows/build-clang.yml', + '**/CMakeLists.txt', + '**/Makefile', + '**/*.mk', + '**/*.cmake', + '**/*.in', + '**/*.h', + '**/*.hpp', + '**/*.c', + '**/*.cpp', + '**/*.cu', + '**/*.cuh', + '**/*.cl'] + + pull_request: + types: [opened, synchronize, reopened] + paths-ignore: + - 'bindings/ruby/**' # handled by bindings-ruby.yml + - 'bindings/go/**' # handled by bindings-go.yml + - 'examples/addon.node/**' # handled by examples.yml + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }} + cancel-in-progress: true + +env: + ubuntu_image: "ubuntu:22.04" + +jobs: + ubuntu-22-clang: + runs-on: ubuntu-22.04 + + strategy: + fail-fast: false + matrix: + build: [Debug, Release] + #arch: [linux/amd64, linux/arm64, linux/arm/v7, linux/ppc64le] + # TODO: arm/v7 disabled due to clang bug + # https://github.com/ggerganov/whisper.cpp/actions/runs/9657764109/job/26637633042?pr=2256#step:4:1990 + arch: [linux/amd64, linux/ppc64le] + + steps: + - name: Clone + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: Set CCACHE_DIR + run: echo "CCACHE_DIR=${{ runner.temp }}/ccache" >> $GITHUB_ENV + + - name: ccache + uses: ggml-org/ccache-action@v1.2.21 + with: + key: clang-${{ matrix.arch }}-${{ matrix.build }} + evict-old-files: 1d + save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4 + + - name: Build ${{ matrix.arch }} + run: | + docker run --platform ${{ matrix.arch }} --rm \ + -v ${{ github.workspace }}:/workspace \ + -v ${CCACHE_DIR}:${CCACHE_DIR} \ + -e CCACHE_DIR=${CCACHE_DIR} \ + -w /workspace ${{ env.ubuntu_image }} /bin/sh -c ' + set -e + export DEBIAN_FRONTEND=noninteractive + sed -i "s|archive.ubuntu.com|mirrors.kernel.org|g" /etc/apt/sources.list + sed -i "s|security.ubuntu.com|mirrors.kernel.org|g" /etc/apt/sources.list + + apt update + apt install -y clang build-essential cmake libsdl2-dev git ccache + cmake . -DWHISPER_SDL2=ON -DCMAKE_BUILD_TYPE=${{ matrix.build }} \ + -DCMAKE_CXX_COMPILER=clang++ \ + -DCMAKE_C_COMPILER=clang \ + -DCMAKE_C_COMPILER_LAUNCHER=ccache \ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ + -DGGML_NATIVE=OFF + make + ctest -L gh --output-on-failure' + + ubuntu-22-clang-arm64: + runs-on: ubuntu-22.04-arm + + strategy: + fail-fast: false + matrix: + build: [Debug, Release] + + steps: + - name: Clone + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: ccache + uses: ggml-org/ccache-action@v1.2.21 + with: + key: clang-arm64-${{ matrix.build }} + evict-old-files: 1d + save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y clang build-essential cmake libsdl2-dev git + + - name: Build and Test + run: | + cmake . -DWHISPER_SDL2=ON \ + -DCMAKE_BUILD_TYPE=${{ matrix.build }} \ + -DCMAKE_CXX_COMPILER=clang++ \ + -DCMAKE_C_COMPILER=clang \ + -DGGML_NATIVE=OFF \ + -DGGML_CPU_ARM_ARCH=armv8-a + make + ctest -L gh --output-on-failure diff --git a/.github/workflows/build-coreml.yml b/.github/workflows/build-coreml.yml new file mode 100644 index 0000000..8dedd78 --- /dev/null +++ b/.github/workflows/build-coreml.yml @@ -0,0 +1,65 @@ +name: CI (coreml) + +on: + workflow_dispatch: # allows manual triggering + push: + branches: + - master + tags: + - 'v*' + paths: ['.github/workflows/build-coreml.yml', + '**/CMakeLists.txt', + '**/*.h', + '**/*.hpp', + '**/*.c', + '**/*.cpp', + '**/*.swift', + '**/*.m', + '**/*.mm', + '**/*.metal'] + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }} + cancel-in-progress: true + +env: + BRANCH_NAME: ${{ github.head_ref || github.ref_name }} + +jobs: + coreml-base-en: + runs-on: macos-latest + + steps: + - name: Checkout with full history + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + with: + fetch-depth: 0 + + - name: Set environment variables + id: set_vars + run: | + BUILD_NUMBER=$(git rev-list --count HEAD) + SHORT_HASH=$(git rev-parse --short=7 HEAD) + if [[ "${{ github.ref_type }}" == "tag" ]]; then + TAG_NAME="${{ github.ref_name }}" + elif [[ "${{ env.BRANCH_NAME }}" == "master" ]]; then + TAG_NAME="b${BUILD_NUMBER}" + else + SAFE_NAME=$(echo "${{ env.BRANCH_NAME }}" | tr '/' '-') + TAG_NAME="${SAFE_NAME}-b${BUILD_NUMBER}-${SHORT_HASH}" + fi + echo "MODEL_NAME=base.en" >> $GITHUB_ENV + echo "GEN_MODEL_NAME=whisper-${TAG_NAME}-ggml-base.en-encoder.mlmodelc" >> $GITHUB_ENV + + - name: Download model + env: + HF_TOKEN: ${{ secrets.HF_TOKEN }} + run: | + ./models/download-ggml-model.sh ${{ env.MODEL_NAME }} + + - name: Generate CoreML model + run: | + python3.11 -m venv venv + source venv/bin/activate + pip install ane_transformers openai-whisper coremltools + ./models/generate-coreml-model.sh ${{ env.MODEL_NAME }} diff --git a/.github/workflows/build-cpu.yml b/.github/workflows/build-cpu.yml new file mode 100644 index 0000000..e2b7488 --- /dev/null +++ b/.github/workflows/build-cpu.yml @@ -0,0 +1,173 @@ +name: CI (cpu) + +on: + workflow_dispatch: # allows manual triggering + push: + branches: + - master + paths: ['.github/workflows/build-cpu.yml', + '**/CMakeLists.txt', + '**/Makefile', + '**/*.mk', + '**/*.cmake', + '**/*.in', + '**/*.h', + '**/*.hpp', + '**/*.c', + '**/*.cpp', + '**/*.cu', + '**/*.cuh', + '**/*.cl'] + + pull_request: + types: [opened, synchronize, reopened] + paths-ignore: + - 'bindings/ruby/**' # handled by bindings-ruby.yml + - 'bindings/go/**' # handled by bindings-go.yml + - 'examples/addon.node/**' # handled by examples.yml + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }} + cancel-in-progress: true + +# TODO: simplify the following jobs using a matrix +jobs: + ggml-ci-x64-cpu-low-perf: + runs-on: ubuntu-22.04 + + steps: + - name: Clone + id: checkout + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: ccache + uses: ggml-org/ccache-action@v1.2.21 + with: + key: ggml-ci-x64-cpu-low-perf + evict-old-files: 1d + save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + + - name: Dependencies + id: depends + run: | + sudo apt-get update + sudo apt-get install build-essential libcurl4-openssl-dev + + - name: Test + id: ggml-ci + env: + HF_TOKEN: ${{ secrets.HF_TOKEN }} + run: | + LLAMA_ARG_THREADS=$(nproc) GG_BUILD_LOW_PERF=1 bash ./ci/run.sh ./tmp/results ./tmp/mnt + + ggml-ci-arm64-cpu-low-perf: + runs-on: ubuntu-22.04-arm + + steps: + - name: Clone + id: checkout + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: ccache + uses: ggml-org/ccache-action@v1.2.21 + with: + key: ggml-ci-arm64-cpu-low-perf + evict-old-files: 1d + save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + + - name: Dependencies + id: depends + run: | + sudo apt-get update + sudo apt-get install build-essential libcurl4-openssl-dev + + - name: Test + id: ggml-ci + env: + HF_TOKEN: ${{ secrets.HF_TOKEN }} + run: | + LLAMA_ARG_THREADS=$(nproc) GG_BUILD_LOW_PERF=1 bash ./ci/run.sh ./tmp/results ./tmp/mnt + + ggml-ci-x64-cpu-high-perf: + runs-on: ubuntu-22.04 + + steps: + - name: Clone + id: checkout + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: ccache + uses: ggml-org/ccache-action@v1.2.21 + with: + key: ggml-ci-x64-cpu-high-perf + evict-old-files: 1d + save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + + - name: Dependencies + id: depends + run: | + sudo apt-get update + sudo apt-get install build-essential libcurl4-openssl-dev + + - name: Test + id: ggml-ci + env: + HF_TOKEN: ${{ secrets.HF_TOKEN }} + run: | + LLAMA_ARG_THREADS=$(nproc) bash ./ci/run.sh ./tmp/results ./tmp/mnt + + ggml-ci-arm64-cpu-high-perf: + runs-on: ubuntu-22.04-arm + + steps: + - name: Clone + id: checkout + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: ccache + uses: ggml-org/ccache-action@v1.2.21 + with: + key: ggml-ci-arm64-cpu-high-perf + evict-old-files: 1d + save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + + - name: Dependencies + id: depends + run: | + sudo apt-get update + sudo apt-get install build-essential libcurl4-openssl-dev + + - name: Test + id: ggml-ci + env: + HF_TOKEN: ${{ secrets.HF_TOKEN }} + run: | + LLAMA_ARG_THREADS=$(nproc) GG_BUILD_NO_SVE=1 GG_BUILD_NO_BF16=1 GG_BUILD_EXTRA_TESTS_0=1 bash ./ci/run.sh ./tmp/results ./tmp/mnt + + ggml-ci-arm64-cpu-high-perf-sve: + runs-on: ubuntu-22.04-arm + + steps: + - name: Clone + id: checkout + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: ccache + uses: ggml-org/ccache-action@v1.2.21 + with: + key: ggml-ci-arm64-cpu-high-perf-sve + evict-old-files: 1d + save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + + - name: Dependencies + id: depends + run: | + sudo apt-get update + sudo apt-get install build-essential libcurl4-openssl-dev + + - name: Test + id: ggml-ci + env: + HF_TOKEN: ${{ secrets.HF_TOKEN }} + run: | + LLAMA_ARG_THREADS=$(nproc) GG_BUILD_NO_BF16=1 GG_BUILD_EXTRA_TESTS_0=1 bash ./ci/run.sh ./tmp/results ./tmp/mnt diff --git a/.github/workflows/build-freebsd.yml b/.github/workflows/build-freebsd.yml new file mode 100644 index 0000000..64e78ad --- /dev/null +++ b/.github/workflows/build-freebsd.yml @@ -0,0 +1,47 @@ +name: CI (freebsd) + +on: + workflow_dispatch: # allows manual triggering + push: + branches: + - master + paths: ['.github/workflows/build-freebsd.yml', + '**/CMakeLists.txt', + '**/Makefile', + '**/*.mk', + '**/*.cmake', + '**/*.in', + '**/*.h', + '**/*.hpp', + '**/*.c', + '**/*.cpp'] + + pull_request: + types: [opened, synchronize, reopened] + paths-ignore: + - 'bindings/ruby/**' # handled by bindings-ruby.yml + - 'bindings/go/**' # handled by bindings-go.yml + - 'examples/addon.node/**' # handled by examples.yml + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }} + cancel-in-progress: true + +jobs: + freeBSD-latest: + runs-on: macos-13 + + steps: + - name: Clone + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: Build + uses: cross-platform-actions/action@fe0167d8082ac584754ef3ffb567fded22642c7d # v0.27.0 + with: + operating_system: freebsd + version: '14.2' + run: | + sudo pkg update + sudo pkg install -y gmake sdl2 cmake git + cmake -B build + cmake --build build --config Release diff --git a/.github/workflows/build-gcc.yml b/.github/workflows/build-gcc.yml new file mode 100644 index 0000000..53c1b2d --- /dev/null +++ b/.github/workflows/build-gcc.yml @@ -0,0 +1,167 @@ +name: CI (gcc) + +on: + workflow_dispatch: # allows manual triggering + push: + branches: + - master + paths: ['.github/workflows/build-gcc.yml', + '**/CMakeLists.txt', + '**/Makefile', + '**/*.mk', + '**/*.cmake', + '**/*.in', + '**/*.h', + '**/*.hpp', + '**/*.c', + '**/*.cpp', + '**/*.cu', + '**/*.cuh', + '**/*.cl'] + + pull_request: + types: [opened, synchronize, reopened] + paths-ignore: + - 'bindings/ruby/**' # handled by bindings-ruby.yml + - 'bindings/go/**' # handled by bindings-go.yml + - 'examples/addon.node/**' # handled by examples.yml + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }} + cancel-in-progress: true + +env: + ubuntu_image: "ubuntu:22.04" + +jobs: + ubuntu-22-gcc: + runs-on: ubuntu-22.04 + + strategy: + fail-fast: false + matrix: + build: [Debug, Release] + arch: [linux/amd64, linux/ppc64le] + + steps: + - name: Clone + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: Set CCACHE_DIR + run: echo "CCACHE_DIR=${{ runner.temp }}/ccache" >> $GITHUB_ENV + + - name: ccache + uses: ggml-org/ccache-action@v1.2.21 + with: + key: gcc-${{ matrix.arch }}-${{ matrix.build }} + evict-old-files: 1d + save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4 + + - name: Build ${{ matrix.arch }} + run: | + docker run --platform ${{ matrix.arch }} --rm \ + -v ${{ github.workspace }}:/workspace \ + -v ${CCACHE_DIR}:${CCACHE_DIR} \ + -e CCACHE_DIR=${CCACHE_DIR} \ + -w /workspace ${{ env.ubuntu_image }} /bin/sh -c ' + set -e + export DEBIAN_FRONTEND=noninteractive + sed -i "s|archive.ubuntu.com|mirrors.kernel.org|g" /etc/apt/sources.list + sed -i "s|security.ubuntu.com|mirrors.kernel.org|g" /etc/apt/sources.list + + apt update + apt install -y build-essential cmake libsdl2-dev git ccache + cmake . -DWHISPER_SDL2=ON -DCMAKE_BUILD_TYPE=${{ matrix.build }} \ + -DGGML_NATIVE=OFF \ + -DCMAKE_C_COMPILER_LAUNCHER=ccache \ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + make + ctest -L gh --output-on-failure' + + ubuntu-22-gcc-arm64: + runs-on: ubuntu-22.04-arm + + strategy: + fail-fast: false + matrix: + build: [Debug, Release] + + steps: + - name: Clone + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: ccache + uses: ggml-org/ccache-action@v1.2.21 + with: + key: gcc-arm64-${{ matrix.build }} + evict-old-files: 1d + save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y build-essential cmake libsdl2-dev git + + - name: Configure CMake + run: | + cmake . \ + -DWHISPER_SDL2=ON \ + -DCMAKE_BUILD_TYPE=${{ matrix.build }} \ + -DGGML_NATIVE=OFF \ + -DGGML_CPU_ARM_ARCH=armv8-a + + - name: Build and Test + run: | + make + ctest -L gh --output-on-failure + + ubuntu-22-gcc-arm-v7: + runs-on: ubuntu-22.04 + + strategy: + fail-fast: false + matrix: + build: [Debug, Release] + arch: [linux/arm/v7] + + steps: + - name: Clone + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: Set CCACHE_DIR + run: echo "CCACHE_DIR=${{ runner.temp }}/ccache" >> $GITHUB_ENV + + - name: ccache + uses: ggml-org/ccache-action@v1.2.21 + with: + key: gcc-${{ matrix.arch }}-${{ matrix.build }} + evict-old-files: 1d + save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4 + + - name: Build ${{ matrix.arch }} + run: | + docker run --platform ${{ matrix.arch }} --rm \ + -v ${{ github.workspace }}:/workspace \ + -v ${CCACHE_DIR}:${CCACHE_DIR} \ + -e CCACHE_DIR=${CCACHE_DIR} \ + -w /workspace ${{ env.ubuntu_image }} /bin/sh -c ' + set -e + export DEBIAN_FRONTEND=noninteractive + sed -i "s|archive.ubuntu.com|mirrors.kernel.org|g" /etc/apt/sources.list + sed -i "s|security.ubuntu.com|mirrors.kernel.org|g" /etc/apt/sources.list + + apt update + apt install -y build-essential cmake libsdl2-dev git ccache + cmake . -DWHISPER_SDL2=ON -DCMAKE_BUILD_TYPE=${{ matrix.build }} \ + -DGGML_NATIVE=OFF \ + -DGGML_CPU_ARM_ARCH=armv7-a+fp \ + -DCMAKE_C_COMPILER_LAUNCHER=ccache \ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + make + ctest -L gh --output-on-failure' diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml new file mode 100644 index 0000000..7db74d4 --- /dev/null +++ b/.github/workflows/build-macos.yml @@ -0,0 +1,73 @@ +name: CI (macOS) + +on: + workflow_dispatch: # allows manual triggering + push: + branches: + - master + paths: ['.github/workflows/build-macos.yml', + '**/CMakeLists.txt', + '**/Makefile', + '**/*.mk', + '**/*.cmake', + '**/*.in', + '**/*.h', + '**/*.hpp', + '**/*.c', + '**/*.cpp', + '**/*.cu', + '**/*.cuh', + '**/*.swift', + '**/*.m', + '**/*.mm', + '**/*.metal'] + + pull_request: + types: [opened, synchronize, reopened] + paths-ignore: + - 'bindings/ruby/**' # handled by bindings-ruby.yml + - 'bindings/go/**' # handled by bindings-go.yml + - 'examples/addon.node/**' # handled by examples.yml + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }} + cancel-in-progress: true + +jobs: + macOS-latest: + runs-on: macOS-latest + + strategy: + matrix: + destination: ['generic/platform=macOS', 'generic/platform=iOS', 'generic/platform=tvOS'] + + steps: + - name: Clone + id: checkout + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: ccache + uses: ggml-org/ccache-action@v1.2.21 + with: + key: macos-${{ matrix.destination }} + evict-old-files: 1d + save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + + - name: Dependencies + run: | + brew update + cmake --version + brew install sdl2 + + - name: Build + run: | + sysctl -a + cmake -B build -G Xcode \ + -DGGML_NATIVE=OFF \ + -DGGML_METAL_USE_BF16=ON \ + -DGGML_METAL_EMBED_LIBRARY=ON \ + -DWHISPER_BUILD_EXAMPLES=OFF \ + -DWHISPER_BUILD_TESTS=OFF \ + -DWHISPER_BUILD_SERVER=OFF \ + -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" + cmake --build build --config Release -j $(sysctl -n hw.logicalcpu) diff --git a/.github/workflows/build-quantize.yml b/.github/workflows/build-quantize.yml new file mode 100644 index 0000000..11a352e --- /dev/null +++ b/.github/workflows/build-quantize.yml @@ -0,0 +1,48 @@ +name: CI (quantize) + +on: + workflow_dispatch: # allows manual triggering + push: + branches: + - master + paths: ['.github/workflows/build-quantize.yml', + '**/CMakeLists.txt', + '**/*.h', + '**/*.hpp', + '**/*.c', + '**/*.cpp'] + + pull_request: + types: [opened, synchronize, reopened] + paths-ignore: + - 'bindings/ruby/**' # handled by bindings-ruby.yml + - 'bindings/go/**' # handled by bindings-go.yml + - 'examples/addon.node/**' # handled by examples.yml + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }} + cancel-in-progress: true + +jobs: + quantize: + runs-on: ubuntu-22.04 + + steps: + - name: Clone + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: ccache + uses: ggml-org/ccache-action@v1.2.21 + with: + key: quantize-ubuntu-22 + evict-old-files: 1d + save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + + - name: Test quantize + env: + HF_TOKEN: ${{ secrets.HF_TOKEN }} + run: | + ./models/download-ggml-model.sh tiny.en + cmake -B build -DGGML_NATIVE=OFF -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + cmake --build build --config Release + ./build/bin/whisper-quantize models/ggml-tiny.en.bin models/ggml-tiny.en-q4_0.bin q4_0 diff --git a/.github/workflows/build-sanitize.yml b/.github/workflows/build-sanitize.yml new file mode 100644 index 0000000..465faea --- /dev/null +++ b/.github/workflows/build-sanitize.yml @@ -0,0 +1,85 @@ +name: CI (sanitize) + +on: + workflow_dispatch: # allows manual triggering + push: + branches: + - master + paths: ['.github/workflows/build-sanitize.yml', + '**/CMakeLists.txt', + '**/Makefile', + '**/*.mk', + '**/*.cmake', + '**/*.h', + '**/*.hpp', + '**/*.c', + '**/*.cpp'] + + pull_request: + types: [opened, synchronize, reopened] + paths-ignore: + - 'bindings/ruby/**' + - 'bindings/go/**' + - 'examples/addon.node/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }} + cancel-in-progress: true + +jobs: + ubuntu-22-gcc-sanitized: + runs-on: ubuntu-22.04 + + continue-on-error: true + + strategy: + fail-fast: false + matrix: + sanitizer: [ADDRESS, THREAD, UNDEFINED] + + steps: + - name: Clone + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: ccache + uses: ggml-org/ccache-action@v1.2.21 + with: + key: sanitize-${{ matrix.sanitizer }} + evict-old-files: 1d + save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y build-essential cmake git + + - name: Build (undefined) + if: ${{ matrix.sanitizer == 'UNDEFINED' }} + run: | + cmake . -DCMAKE_BUILD_TYPE=Debug \ + -DWHISPER_SANITIZE_${{ matrix.sanitizer }}=ON \ + -DGGML_NATIVE=OFF \ + -DGGML_OPENMP=OFF + make + + - name: Build + if: ${{ matrix.sanitizer == 'ADDRESS' }} + run: | + cmake . -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DWHISPER_SANITIZE_${{ matrix.sanitizer }}=ON \ + -DGGML_NATIVE=OFF + make + + - name: Build (no OpenMP) + if: ${{ matrix.sanitizer == 'THREAD' }} + run: | + cmake . -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DWHISPER_SANITIZE_${{ matrix.sanitizer }}=ON \ + -DGGML_NATIVE=OFF \ + -DGGML_OPENMP=OFF + make + + - name: Test + if: ${{ matrix.sanitizer != 'UNDEFINED' }} + run: | + ctest -L gh --output-on-failure diff --git a/.github/workflows/build-self-hosted.yml b/.github/workflows/build-self-hosted.yml new file mode 100644 index 0000000..8daa9d4 --- /dev/null +++ b/.github/workflows/build-self-hosted.yml @@ -0,0 +1,259 @@ +name: CI (self-hosted) + +on: + workflow_dispatch: # allows manual triggering + push: + branches: + - master + paths: [ + '.github/workflows/build.yml', + '**/CMakeLists.txt', + '**/.cmake', + '**/*.h', + '**/*.hpp', + '**/*.c', + '**/*.cpp', + '**/*.cu', + '**/*.cuh', + '**/*.swift', + '**/*.m', + '**/*.mm', + '**/*.metal', + '**/*.comp' + ] + + pull_request: + types: [opened, synchronize, reopened] + paths: [ + '.github/workflows/build-self-hosted.yml', + '**/CMakeLists.txt', + '**/.cmake', + '**/*.h', + '**/*.hpp', + '**/*.c', + '**/*.cpp', + '**/*.cu', + '**/*.cuh', + '**/*.swift', + '**/*.m', + '**/*.mm', + '**/*.metal', + '**/*.comp' + ] + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }} + cancel-in-progress: true + +jobs: + gpu-cuda: + runs-on: [self-hosted, Linux, NVIDIA] + + steps: + - name: Clone + id: checkout + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: Test + id: ggml-ci + run: | + nvidia-smi + GG_BUILD_CUDA=1 bash ./ci/run.sh ~/results/whisper.cpp ~/mnt/whisper.cpp + + gpu-vulkan-nvidia-cm: + runs-on: [self-hosted, Linux, NVIDIA] + + steps: + - name: Clone + id: checkout + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: Test + id: ggml-ci + run: | + vulkaninfo --summary + GG_BUILD_VULKAN=1 GGML_VK_DISABLE_COOPMAT2=1 bash ./ci/run.sh ~/results/whisper.cpp ~/mnt/whisper.cpp + + gpu-vulkan-nvidia-cm2: + runs-on: [self-hosted, Linux, NVIDIA, COOPMAT2] + + steps: + - name: Clone + id: checkout + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: Test + id: ggml-ci + run: | + vulkaninfo --summary + GG_BUILD_VULKAN=1 bash ./ci/run.sh ~/results/whisper.cpp ~/mnt/whisper.cpp + + gpu-metal: + runs-on: [self-hosted, macOS, ARM64] + + steps: + - name: Clone + id: checkout + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: Test + id: ggml-ci + run: | + GG_BUILD_METAL=1 bash ./ci/run.sh ~/results/whisper.cpp ~/mnt/whisper.cpp + + gpu-vulkan: + runs-on: [self-hosted, macOS, ARM64] + + steps: + - name: Clone + id: checkout + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: Test + id: ggml-ci + run: | + vulkaninfo --summary + GG_BUILD_VULKAN=1 bash ./ci/run.sh ~/results/whisper.cpp ~/mnt/whisper.cpp + + npu-amd-windows: + runs-on: [self-hosted, Windows, X64, strix-halo, devlab-dispatch] + timeout-minutes: 60 + continue-on-error: true # advisory while the runner pool is new; revisit later + + env: + FLEXML_URL: https://github.com/lemonade-sdk/whisper.cpp-rocm/releases/download/deps/flexmlrt-1.7.0-win.zip + MODEL: base + + steps: + - name: Clone + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - uses: microsoft/setup-msbuild@v2 + + - name: Install CMake if not available + shell: powershell + run: | + $installed = Get-Command cmake -ErrorAction SilentlyContinue + if (-not $installed) { + $ver = "3.28.1" + $url = "https://github.com/Kitware/CMake/releases/download/v$ver/cmake-$ver-windows-x86_64.msi" + Invoke-WebRequest -Uri $url -OutFile cmake.msi + Start-Process msiexec.exe -ArgumentList "/i cmake.msi /quiet /norestart" -Wait + $p = "C:\Program Files\CMake\bin" + $env:PATH = "$p;$env:PATH" + echo $p >> $env:GITHUB_PATH + cmake --version + if ($LASTEXITCODE -ne 0) { Write-Error "CMake install failed"; exit 1 } + } else { cmake --version } + + - name: Download FlexML runtime + shell: powershell + run: | + Invoke-WebRequest -Uri "${{ env.FLEXML_URL }}" -OutFile flexmlrt.zip + if (-Not (Test-Path "flexmlrt.zip")) { Write-Error "flexmlrt.zip not downloaded"; exit 1 } + if ((Get-Item "flexmlrt.zip").Length -eq 0) { Write-Error "flexmlrt.zip is empty"; exit 1 } + tar xf flexmlrt.zip + if ($LASTEXITCODE -ne 0) { Write-Error "Extraction failed"; exit 1 } + if (-not (Test-Path "flexmlrt")) { Write-Error "No flexmlrt directory after extraction"; exit 1 } + + - name: Setup FlexML, configure and build + shell: cmd + run: | + cd flexmlrt + call setup.bat + if errorlevel 1 ( echo ERROR: FlexML setup.bat failed & exit /b 1 ) + cd .. + cmake -B build -A x64 -DCMAKE_BUILD_TYPE=Release -DWHISPER_VITISAI=ON + if errorlevel 1 ( echo ERROR: CMake configure failed & exit /b 1 ) + cmake --build build --config Release -j + if errorlevel 1 ( echo ERROR: Build failed & exit /b 1 ) + + - name: Copy FlexML DLLs to build output + shell: powershell + run: | + foreach ($d in "flexmlrt/bin", "flexmlrt/lib") { + if (Test-Path "$d/*.dll") { Copy-Item "$d/*.dll" "build/bin/Release/" -Force } + } + if (-not (Test-Path "build/bin/Release/flexmlrt.dll")) { + Write-Error "flexmlrt.dll not staged next to binaries"; exit 1 + } + + - name: Download ggml model + shell: cmd + run: | + call models\download-ggml-model.cmd %MODEL% models + if not exist models\ggml-%MODEL%.bin ( echo ERROR: model download failed & exit /b 1 ) + + - name: Download NPU encoder cache + shell: cmd + run: | + .\models\download-vitisai-model.cmd %MODEL% + if not exist models\ggml-%MODEL%-encoder-vitisai.rai ( echo ERROR: VitisAI encoder cache download failed & exit /b 1 ) + + - name: Run NPU smoke test + shell: cmd + run: | + build\bin\Release\whisper-cli.exe -m models\ggml-%MODEL%.bin -f samples\jfk.wav > vitisai.log 2>&1 + type vitisai.log + findstr /I /C:"vitisai" vitisai.log || ( echo ERROR: no VitisAI activity - encoder likely fell back to CPU & exit /b 1 ) + findstr /I /C:"ask not what your country" vitisai.log || ( echo ERROR: incorrect transcription & exit /b 1 ) + + - name: Upload smoke test log + if: always() + uses: actions/upload-artifact@v4 + with: + name: vitisai-smoke-log-windows + path: vitisai.log + + npu-amd-linux: + runs-on: [self-hosted, Linux, X64, strix-halo, devlab-dispatch] + timeout-minutes: 60 + continue-on-error: true # advisory while the runner pool is new; revisit later + + env: + FLEXML_LINUX_URL: https://github.com/lemonade-sdk/whisper.cpp-rocm/releases/download/deps/flexmlrt-1.8.0-linux.tar.gz + MODEL: base + + steps: + - name: Clone + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: Verify NPU device + run: | + lsmod | grep -q amdxdna || { echo "ERROR: amdxdna driver not loaded"; exit 1; } + ls /dev/accel/accel* || { echo "ERROR: no NPU accel device node"; exit 1; } + + - name: Download FlexML runtime (Linux) + run: | + curl -L --fail -o flexmlrt.tar.gz "$FLEXML_LINUX_URL" + tar xf flexmlrt.tar.gz + source flexmlrt/setup.sh + echo "FlexmlRT_DIR=$PWD/flexmlrt/share/cmake/FlexmlRT" >> $GITHUB_ENV + echo "LD_LIBRARY_PATH=$PWD/flexmlrt/lib:$LD_LIBRARY_PATH" >> $GITHUB_ENV + + - name: Configure and build + run: | + cmake -B build -DCMAKE_BUILD_TYPE=Release -DWHISPER_VITISAI=ON + cmake --build build --config Release -j $(nproc) + + - name: Download ggml model + run: | + ./models/download-ggml-model.sh $MODEL + + - name: Download NPU encoder cache + run: | + sh ./models/download-vitisai-model.sh $MODEL + [ -f "models/ggml-$MODEL-encoder-vitisai.rai" ] || { echo "ERROR: VitisAI encoder cache download failed"; exit 1; } + + - name: Run NPU smoke test + run: | + ./build/bin/whisper-cli -m "models/ggml-$MODEL.bin" -f samples/jfk.wav 2>&1 | tee vitisai.log + grep -qi "vitisai" vitisai.log || { echo "ERROR: no VitisAI activity - CPU fallback?"; exit 1; } + grep -qi "ask not what your country" vitisai.log || { echo "ERROR: incorrect transcription"; exit 1; } + + - name: Upload smoke test log + if: always() + uses: actions/upload-artifact@v4 + with: + name: vitisai-smoke-log-linux + path: vitisai.log diff --git a/.github/workflows/build-sycl.yml b/.github/workflows/build-sycl.yml new file mode 100644 index 0000000..be7d032 --- /dev/null +++ b/.github/workflows/build-sycl.yml @@ -0,0 +1,150 @@ +name: CI (sycl) + +on: + workflow_dispatch: # allows manual triggering + push: + branches: + - master + paths: ['.github/workflows/build-sycl.yml', + '**/CMakeLists.txt', + '**/Makefile', + '**/*.mk', + '**/*.cmake', + '**/*.in', + '**/*.h', + '**/*.hpp', + '**/*.c', + '**/*.cpp', + '**/*.cu', + '**/*.cuh', + '**/*.cl'] + + pull_request: + types: [opened, synchronize, reopened] + paths-ignore: + - 'bindings/ruby/**' # handled by bindings-ruby.yml + - 'bindings/go/**' # handled by bindings-go.yml + - 'examples/addon.node/**' # handled by examples.yml + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }} + cancel-in-progress: true + +jobs: + ubuntu-22-cmake-sycl: + runs-on: ubuntu-22.04 + + strategy: + fail-fast: false + matrix: + dwhisper_sycl: [ON] + dcmake_c_compiler: [icx] + dcmake_cxx_compiler: [icpx] + arch: [linux/amd64, linux/arm64, linux/arm/v7, linux/ppc64le] + + continue-on-error: true + + steps: + - name: Clone + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: add oneAPI to apt + shell: bash + run: | + cd /tmp + wget https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB + sudo apt-key add GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB + rm GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB + sudo add-apt-repository "deb https://apt.repos.intel.com/oneapi all main" + + - name: install oneAPI dpcpp compiler + shell: bash + run: | + sudo apt update + sudo apt install intel-oneapi-compiler-dpcpp-cpp + + - name: install oneAPI MKL library + shell: bash + run: | + sudo apt install intel-oneapi-mkl-devel + + - name: ccache + uses: ggml-org/ccache-action@v1.2.21 + with: + key: sycl-${{ matrix.arch }} + evict-old-files: 1d + save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + + - name: Build + id: cmake_build + env: + CCACHE_SLOPPINESS: time_macros + CCACHE_NODIRECT: 1 + run: | + source /opt/intel/oneapi/setvars.sh + export CCACHE_COMPILERCHECK="string:$(icpx --version 2>&1 | head -1)" + mkdir build + cd build + cmake -DGGML_SYCL=ON -DGGML_NATIVE=OFF -DCMAKE_C_COMPILER=icx -DCMAKE_CXX_COMPILER=icpx \ + -DCMAKE_C_COMPILER_LAUNCHER=ccache \ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache .. + cmake --build . --config Release -j $(nproc) + + ubuntu-22-cmake-sycl-fp16: + runs-on: ubuntu-22.04 + + strategy: + fail-fast: false + matrix: + dwhisper_sycl: [ON] + dcmake_c_compiler: [icx] + dcmake_cxx_compiler: [icpx] + arch: [linux/amd64, linux/arm64, linux/arm/v7, linux/ppc64le] + + continue-on-error: true + + steps: + - name: Clone + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: add oneAPI to apt + shell: bash + run: | + cd /tmp + wget https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB + sudo apt-key add GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB + rm GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB + sudo add-apt-repository "deb https://apt.repos.intel.com/oneapi all main" + + - name: install oneAPI dpcpp compiler + shell: bash + run: | + sudo apt update + sudo apt install intel-oneapi-compiler-dpcpp-cpp + + - name: install oneAPI MKL library + shell: bash + run: | + sudo apt install intel-oneapi-mkl-devel + + - name: ccache + uses: ggml-org/ccache-action@v1.2.21 + with: + key: sycl-fp16-${{ matrix.arch }} + evict-old-files: 1d + save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + + - name: Build + id: cmake_build + env: + CCACHE_SLOPPINESS: time_macros + CCACHE_NODIRECT: 1 + run: | + source /opt/intel/oneapi/setvars.sh + export CCACHE_COMPILERCHECK="string:$(icpx --version 2>&1 | head -1)" + mkdir build + cd build + cmake -DGGML_SYCL_F16=ON -DGGML_NATIVE=OFF -DCMAKE_C_COMPILER=icx -DCMAKE_CXX_COMPILER=icpx \ + -DCMAKE_C_COMPILER_LAUNCHER=ccache \ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache .. + cmake --build . --config Release -j $(nproc) diff --git a/.github/workflows/build-vad.yml b/.github/workflows/build-vad.yml new file mode 100644 index 0000000..d34a96e --- /dev/null +++ b/.github/workflows/build-vad.yml @@ -0,0 +1,50 @@ +name: CI (vad) + +on: + workflow_dispatch: # allows manual triggering + push: + branches: + - master + paths: ['.github/workflows/build-vad.yml', + '**/CMakeLists.txt', + '**/*.h', + '**/*.hpp', + '**/*.c', + '**/*.cpp'] + + pull_request: + types: [opened, synchronize, reopened] + paths-ignore: + - 'bindings/ruby/**' # handled by bindings-ruby.yml + - 'bindings/go/**' # handled by bindings-go.yml + - 'examples/addon.node/**' # handled by examples.yml + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }} + cancel-in-progress: true + +jobs: + vad: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: ccache + uses: ggml-org/ccache-action@v1.2.21 + with: + key: vad-ubuntu-latest + evict-old-files: 1d + save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + + - name: Build + shell: bash + run: | + cmake -B build -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DGGML_NATIVE=OFF + cmake --build build --config Release + + - name: Test + shell: bash + run: | + ctest -R ^test-vad$ --test-dir build --output-on-failure -VV diff --git a/.github/workflows/build-wasm.yml b/.github/workflows/build-wasm.yml new file mode 100644 index 0000000..c17a44a --- /dev/null +++ b/.github/workflows/build-wasm.yml @@ -0,0 +1,65 @@ +name: CI (wasm) + +on: + workflow_dispatch: # allows manual triggering + push: + branches: + - master + paths: ['.github/workflows/build-wasm.yml', + '**/CMakeLists.txt', + '**/Makefile', + '**/*.mk', + '**/*.cmake', + '**/*.in', + '**/*.h', + '**/*.hpp', + '**/*.c', + '**/*.cpp'] + + pull_request: + types: [opened, synchronize, reopened] + paths-ignore: + - 'bindings/ruby/**' # handled by bindings-ruby.yml + - 'bindings/go/**' # handled by bindings-go.yml + - 'examples/addon.node/**' # handled by examples.yml + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }} + cancel-in-progress: true + +jobs: + emscripten: + runs-on: ubuntu-22.04 + + strategy: + matrix: + build: [Release] + + steps: + - name: Clone + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: Setup emsdk + uses: emscripten-core/setup-emsdk@6ab9eb1bda2574c4ddb79809fc9247783eaf9021 # v14 + + - name: Verify + run: emcc -v + + - name: ccache + uses: ggml-org/ccache-action@v1.2.21 + with: + key: wasm-ubuntu-22 + evict-old-files: 1d + save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + + - name: Build + env: + CCACHE_SLOPPINESS: time_macros,include_file_mtime,include_file_ctime + CCACHE_COMPILERCHECK: content + run: | + emcmake cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.build }} \ + -DCMAKE_C_COMPILER_LAUNCHER=ccache \ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ + "-DCMAKE_C_FLAGS=-ffile-prefix-map=$EMSDK=/emsdk" \ + "-DCMAKE_CXX_FLAGS=-ffile-prefix-map=$EMSDK=/emsdk" + cmake --build build -j $(nproc) diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml new file mode 100644 index 0000000..76b7a73 --- /dev/null +++ b/.github/workflows/build-windows.yml @@ -0,0 +1,74 @@ +name: CI (windows) + +on: + workflow_dispatch: # allows manual triggering + push: + branches: + - master + paths: ['.github/workflows/build-windows.yml', + '**/CMakeLists.txt', + '**/Makefile', + '**/*.mk', + '**/*.cmake', + '**/*.in', + '**/*.h', + '**/*.hpp', + '**/*.c', + '**/*.cpp', + '**/*.cu', + '**/*.cuh', + '**/*.cl'] + + pull_request: + types: [opened, synchronize, reopened] + paths-ignore: + - 'bindings/ruby/**' # handled by bindings-ruby.yml + - 'bindings/go/**' # handled by bindings-go.yml + - 'examples/addon.node/**' # handled by examples.yml + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }} + cancel-in-progress: true + +jobs: + windows-msys2: + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + include: + - { sys: UCRT64, env: ucrt-x86_64, compiler: gcc, build: Release } + - { sys: CLANG64, env: clang-x86_64, compiler: clang, build: Release } + + steps: + - name: Clone + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: Setup ${{ matrix.sys }} + uses: msys2/setup-msys2@cafece8e6baf9247cf9b1bf95097b0b983cc558d # v2 + with: + update: true + msystem: ${{matrix.sys}} + install: >- + mingw-w64-${{matrix.env}}-${{matrix.compiler}} + mingw-w64-${{matrix.env}}-cmake + mingw-w64-${{matrix.env}}-SDL2 + mingw-w64-${{matrix.env}}-openblas + + - name: Build using CMake + shell: msys2 {0} + run: | + cmake -B build -DWHISPER_SDL2=ON + cmake --build build --config ${{ matrix.build }} -j $(nproc) + + - name: Clean after building using CMake + shell: msys2 {0} + run: | + rm -rf build + + - name: Build using CMake w/ OpenBLAS + shell: msys2 {0} + run: | + cmake -B build -DGGML_BLAS=ON -DGGML_BLAS_VENDOR=OpenBLAS + cmake --build build --config ${{ matrix.build }} -j $(nproc) diff --git a/.github/workflows/close-issue.yml b/.github/workflows/close-issue.yml new file mode 100644 index 0000000..449feac --- /dev/null +++ b/.github/workflows/close-issue.yml @@ -0,0 +1,28 @@ +name: Mark inactive issues stale +on: + schedule: + - cron: "42 0 * * *" + +# Fine-grant permission +# https://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authentication#modifying-the-permissions-for-the-github_token +permissions: + issues: write + +jobs: + mark-issues: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - uses: actions/stale@v10 + with: + exempt-issue-labels: "refactor,help wanted,good first issue,research,bug,roadmap" + days-before-issue-stale: 30 + days-before-issue-close: -1 + stale-issue-label: "stale" + stale-issue-message: "This issue was marked as stale because it has been inactive for 30 days." + days-before-pr-stale: -1 + days-before-pr-close: -1 + operations-per-run: 10000 + repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/deploy-examples-wasm.yml b/.github/workflows/deploy-examples-wasm.yml new file mode 100644 index 0000000..55df147 --- /dev/null +++ b/.github/workflows/deploy-examples-wasm.yml @@ -0,0 +1,97 @@ +name: Examples WASM +on: + push: + branches: ["master"] + + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + deploy-wasm-github-pages: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: Setup Pages + uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5 + + - name: Setup emsdk + uses: emscripten-core/setup-emsdk@6ab9eb1bda2574c4ddb79809fc9247783eaf9021 # v14 + + - name: Build WASM Examples + # Enable for real build later in whisper.cpp + run: | + mkdir -p build-em && cd build-em + emcmake cmake .. -DCMAKE_BUILD_TYPE=Release + make -j + + - name: Create staging directory + run: mkdir -p staging + + - name: Create .nojekyll file in staging directory + run: touch staging/.nojekyll + + - name: Copy application files + run: | + build_dir=build-em/bin + + ls ${build_dir} + + # command.wasm + target_dir=staging/command.wasm + mkdir -p ${target_dir} + cp ${build_dir}/command.wasm/{index.html,command.js,helpers.js} ${target_dir} + cp ${build_dir}/libcommand.js ${target_dir} + + # bench.wasm + target_dir=staging/bench.wasm + mkdir -p ${target_dir} + cp ${build_dir}/bench.wasm/{index.html,bench.js,helpers.js} ${target_dir} + cp ${build_dir}/libbench.js ${target_dir} + + # stream.wasm + target_dir=staging/stream.wasm + mkdir -p ${target_dir} + cp ${build_dir}/stream.wasm/{index.html,stream.js,helpers.js} ${target_dir} + cp ${build_dir}/libstream.js ${target_dir} + + # wchess.wasm + target_dir=staging/wchess.wasm + mkdir -p ${target_dir} + cp -r ${build_dir}/wchess.wasm/{index.html,css,img,js} ${target_dir} + cp ${build_dir}/wchess.wasm.js ${target_dir} + + # whisper.wasm (this will be the main example page) + target_dir=staging + mkdir -p ${target_dir} + cp ${build_dir}/whisper.wasm/{index.html,main.js,helpers.js} ${target_dir} + cp ${build_dir}/libmain.js ${target_dir} + + # Copy Cross-Origin Isolation service worker + cp -v examples/coi-serviceworker.js staging/ + + - name: List files in staging directory (for debugging) + run: | + echo "Files in staging directory:" + find staging -type f | sort + + - name: Upload artifact + uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4 + with: + path: ./staging + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..2d95e1a --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,73 @@ +name: Publish Docker image + +on: + workflow_dispatch: # allows manual triggering + schedule: + # Rebuild daily rather than on every push because it is expensive + - cron: '12 4 * * *' + +jobs: + push_to_registry: + name: Push Docker image to Docker Hub + + runs-on: ${{ matrix.config.runs_on }} + env: + COMMIT_SHA: ${{ github.sha }} + strategy: + fail-fast: false + matrix: + config: + - { tag: "main", dockerfile: ".devops/main.Dockerfile", platform: "linux/amd64", runs_on: "ubuntu-24.04" } + - { tag: "main-arm64", dockerfile: ".devops/main.Dockerfile", platform: "linux/arm64", runs_on: "ubuntu-24.04-arm" } + - { tag: "main-musa", dockerfile: ".devops/main-musa.Dockerfile", platform: "linux/amd64", runs_on: "ubuntu-24.04" } + - { tag: "main-intel", dockerfile: ".devops/main-intel.Dockerfile", platform: "linux/amd64", runs_on: "ubuntu-24.04" } + - { tag: "main-cuda", dockerfile: ".devops/main-cuda.Dockerfile", platform: "linux/amd64", runs_on: "ubuntu-24.04" } + - { tag: "main-vulkan", dockerfile: ".devops/main-vulkan.Dockerfile", platform: "linux/amd64", runs_on: "ubuntu-24.04" } + - { tag: "main-vulkan-arm64", dockerfile: ".devops/main-vulkan.Dockerfile", platform: "linux/arm64", runs_on: "ubuntu-24.04-arm" } + + steps: + - name: Check out the repo + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4 + + - name: Log in to Docker Hub + uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Free up disk space + run: | + sudo apt-get remove -y '^dotnet-.*' '^llvm-.*' '^mysql-.*' '^postgresql-.*' + sudo apt-get autoremove -y + sudo apt-get autoclean + + sudo rm -rf /usr/share/dotnet + sudo rm -rf /usr/local/lib/android + sudo rm -rf /opt/ghc + sudo rm -rf /opt/hostedtoolcache/CodeQL + + docker system prune -af + + df -h + + - name: Generate tags + id: tags + run: | + TAGS="ghcr.io/${{ github.repository }}:${{ matrix.config.tag }}" + TAGS="$TAGS,ghcr.io/${{ github.repository }}:${{ matrix.config.tag }}-${{ env.COMMIT_SHA }}" + echo "tags=$TAGS" >> $GITHUB_OUTPUT + + - name: Build and push Docker image (tagged) + uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7 + with: + context: . + push: true + platforms: ${{ matrix.config.platform }} + tags: ${{ steps.tags.outputs.tags }} + file: ${{ matrix.config.dockerfile }} + secrets: | + HF_TOKEN=${{ secrets.HF_TOKEN }} diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml new file mode 100644 index 0000000..0f8f0a0 --- /dev/null +++ b/.github/workflows/examples.yml @@ -0,0 +1,53 @@ +name: Examples Tests +on: + push: + branches: + - master + paths: + - examples/addon.node/** + - include/whisper.h + pull_request: + paths: + - examples/addon.node/** + - include/whisper.h + +jobs: + addon_node-ubuntu-22: + runs-on: ubuntu-22.04 + strategy: + matrix: + node-version: [ 20.x, 22.x ] + steps: + - name: Clone + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: Dependencies + run: | + sudo apt-get update + sudo apt-get install build-essential git + sudo apt-get install cmake + sudo apt-get install libsdl2-dev + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 + with: + node-version: ${{ matrix.node-version }} + cache: 'npm' + cache-dependency-path: examples/addon.node/package-lock.json + + - name: Install package.json dependencies + working-directory: ./examples/addon.node + run: npm install + + - name: Compile addon.node + run: npx cmake-js compile -T addon.node -B Release + + - name: Download test model + env: + HF_TOKEN: ${{ secrets.HF_TOKEN }} + run: | + bash ./models/download-ggml-model.sh base.en + - name: Test + run: | + cd examples/addon.node + npm run test diff --git a/.github/workflows/make-release.yml b/.github/workflows/make-release.yml new file mode 100644 index 0000000..451c3a5 --- /dev/null +++ b/.github/workflows/make-release.yml @@ -0,0 +1,88 @@ +name: Make Release + +on: + workflow_dispatch: + inputs: + commit: + description: 'Commit SHA to release (empty = branch HEAD)' + required: false + default: '' + type: string + dry_run: + description: 'Dry run - validate without creating the tag' + required: true + type: boolean + default: true + +env: + GH_TOKEN: ${{ github.token }} + +permissions: + contents: write + +jobs: + make-release: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + with: + ref: ${{ inputs.commit != '' && inputs.commit || github.ref_name }} + fetch-depth: 0 + ssh-key: ${{ secrets.DEPLOY_KEY_RELEASE }} + + - name: Run release checks + id: checks + run: bash scripts/make-release-checks.sh ${{ github.event.inputs.dry_run == 'true' && '--dry-run' || '' }} + env: + GITHUB_REPOSITORY: ${{ github.repository }} + RELEASE_BRANCH: ${{ github.ref_name }} + + - name: Create release tag + if: ${{ github.event.inputs.dry_run == 'false' }} + run: | + VERSION="${{ steps.checks.outputs.version }}" + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git tag -a "${VERSION}" -m "Release ${VERSION}" + git push origin "${VERSION}" + echo "Created and pushed tag ${VERSION}" + + - name: Generate release description + id: desc + run: bash scripts/make-release-desc.sh "${{ steps.checks.outputs.version }}" + env: + GITHUB_REPOSITORY: ${{ github.repository }} + + - name: Create release + if: ${{ github.event.inputs.dry_run == 'false' }} + uses: ggml-org/action-create-release@v1 + env: + GITHUB_TOKEN: ${{ github.token }} + with: + tag_name: ${{ steps.checks.outputs.version }} + prerelease: false + # TODO: enrich the body of the release with more information + body: | + ## Overview + + New version has been released. + + ${{ steps.desc.outputs.nightly }} + **More info:** [dist : releases and versioning of ggml-org projects](https://github.com/ggml-org/ggml/discussions/1579) + + ## ${{ steps.desc.outputs.changelog_title }} + + ${{ steps.desc.outputs.changelog }} + + - name: Dry run summary + if: ${{ github.event.inputs.dry_run == 'true' }} + run: | + if [[ "${{ steps.checks.outputs.checks_passed }}" == "true" ]]; then + echo "Dry run complete - all checks passed." + echo "Would have created tag: ${{ steps.checks.outputs.version }}" + else + echo "::error::Dry run found release check failures. A release tag would not be created." + exit 1 + fi diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..e72f2c7 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,941 @@ +name: Release + +# Builds dev releases (b* tags) when manually dispatched with create_release: true. +# Run this as a verification build before making a release (see make-release.yml). +# Pushing a v* tag does not trigger this workflow; releases are made via make-release.yml. + +on: + workflow_dispatch: + inputs: + create_release: + description: 'Create new developer release' + required: true + type: boolean + pre_release_tag: + description: 'Pre-release tag name' + required: false + type: string + +env: + BRANCH_NAME: ${{ github.head_ref || github.ref_name }} + VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" + WHISPER_BUILD_IS_DEV: OFF + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: write # for creating release + id-token: write + attestations: write + +jobs: + determine-tag: + runs-on: ubuntu-latest + outputs: + tag_name: ${{ steps.tag.outputs.name }} + should_release: ${{ steps.tag.outputs.should_release }} + + steps: + - name: Checkout with full history + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + with: + fetch-depth: 0 + + - name: Determine tag name + id: tag + shell: bash + run: | + BUILD_NUMBER=$(git rev-list --count HEAD) + SHORT_HASH=$(git rev-parse --short=7 HEAD) + CUSTOM_TAG="${{ github.event.inputs.pre_release_tag }}" + SHOULD_RELEASE="false" + + echo "Raw values:" + echo "BUILD_NUMBER: $BUILD_NUMBER" + echo "SHORT_HASH: $SHORT_HASH" + echo "BRANCH_NAME: ${{ env.BRANCH_NAME }}" + echo "CUSTOM_TAG: $CUSTOM_TAG" + + if [[ -n "$CUSTOM_TAG" ]]; then + echo "Using custom tag" + TAG_NAME="${CUSTOM_TAG}" + SHOULD_RELEASE="true" + elif [[ "${{ env.BRANCH_NAME }}" == "master" ]]; then + echo "Using master branch format" + TAG_NAME="b${BUILD_NUMBER}" + SHOULD_RELEASE="false" + else + echo "Using non-master branch format" + SAFE_NAME=$(echo "${{ env.BRANCH_NAME }}" | tr '/' '-') + TAG_NAME="${SAFE_NAME}-b${BUILD_NUMBER}-${SHORT_HASH}" + SHOULD_RELEASE="false" + fi + + if [[ "${{ github.event.inputs.create_release }}" == "true" ]]; then + echo "Manual release requested" + SHOULD_RELEASE="true" + fi + + echo "Final tag name: $TAG_NAME" + echo "Should release: $SHOULD_RELEASE" + echo "name=$TAG_NAME" >> $GITHUB_OUTPUT + echo "should_release=$SHOULD_RELEASE" >> $GITHUB_OUTPUT + + ubuntu-cpu: + runs-on: ${{ matrix.os }} + needs: determine-tag + if: ${{ needs.determine-tag.outputs.should_release == 'true' }} + + strategy: + matrix: + include: + - build: x64 + os: ubuntu-22.04 + - build: arm64 + os: ubuntu-22.04-arm + + steps: + - name: Clone + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: ccache + uses: ggml-org/ccache-action@v1.2.21 + with: + key: release-${{ matrix.os }}-cpu + evict-old-files: 1d + + - name: Dependencies + run: | + sudo apt-get update + sudo apt-get install -y build-essential cmake + + - name: Build + run: | + cmake -B build \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_RPATH='$ORIGIN' \ + -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON \ + -DGGML_BACKEND_DL=ON \ + -DGGML_NATIVE=OFF \ + -DWHISPER_BUILD_IS_DEV=${{ env.WHISPER_BUILD_IS_DEV }} \ + ${{ matrix.build == 'x64' && '-DGGML_CPU_ALL_VARIANTS=ON' || '-DGGML_CPU_ARM_ARCH=armv8-a' }} + cmake --build build --config Release -j $(nproc) + + - name: Pack artifacts + run: | + cp LICENSE ./build/bin/ + tar -czvf whisper-bin-ubuntu-${{ matrix.build }}.tar.gz \ + --transform "s,^\.,whisper-bin-ubuntu-${{ matrix.build }}," \ + -C ./build/bin . + + - name: Upload artifacts + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + with: + path: whisper-bin-ubuntu-${{ matrix.build }}.tar.gz + name: whisper-bin-ubuntu-${{ matrix.build }}.tar.gz + + windows: + runs-on: windows-latest + needs: determine-tag + + strategy: + matrix: + build: [Release] + arch: [Win32, x64] + sdl2: [ON] + include: + - arch: Win32 + s2arc: x86 + jnaPath: win32-x86 + - arch: x64 + s2arc: x64 + jnaPath: win32-x86-64 + - sdl2: ON + s2ver: 2.28.5 + + steps: + - name: Clone + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@6fb02220983dee41ce7ae257b6f4d8f9bf5ed4ce # v2 + + - name: Fetch SDL2 and set SDL2_DIR + if: matrix.sdl2 == 'ON' + run: | + C:/msys64/usr/bin/wget.exe -qO sdl2.zip https://github.com/libsdl-org/SDL/releases/download/release-${{ matrix.s2ver }}/SDL2-devel-${{ matrix.s2ver }}-VC.zip + 7z x sdl2.zip + echo "SDL2_DIR=$env:GITHUB_WORKSPACE/SDL2-${{ matrix.s2ver }}/cmake" >> $env:GITHUB_ENV + + - name: Configure + run: > + cmake -S . -B ./build -A ${{ matrix.arch }} + -DCMAKE_BUILD_TYPE=${{ matrix.build }} + -DBUILD_SHARED_LIBS=ON + -DWHISPER_SDL2=${{ matrix.sdl2 }} + -DGGML_NATIVE=OFF + -DWHISPER_BUILD_IS_DEV=${{ env.WHISPER_BUILD_IS_DEV }} + ${{ matrix.arch == 'x64' && '-DGGML_BACKEND_DL=ON -DGGML_CPU_ALL_VARIANTS=ON' || '-DGGML_BMI2=OFF' }} + + - name: Build + run: | + cd ./build + msbuild ALL_BUILD.vcxproj -t:build -p:configuration=${{ matrix.build }} -p:platform=${{ matrix.arch }} + + - name: Copy SDL2.dll + if: matrix.sdl2 == 'ON' + run: copy "$env:SDL2_DIR/../lib/${{ matrix.s2arc }}/SDL2.dll" build/bin/${{ matrix.build }} + + - name: Upload SDL2.dll + if: matrix.sdl2 == 'ON' + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + with: + name: ${{ matrix.s2arc }}_SDL2.dll + path: build/bin/${{ matrix.build }}/SDL2.dll + + - name: Upload whisper dll + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + with: + name: whisper_${{ matrix.arch }}.dll + path: build/bin/${{ matrix.build }}/whisper.dll + + - name: Upload ggml dll + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + with: + name: ggml_${{ matrix.arch }}.dll + path: build/bin/${{ matrix.build }}/ggml.dll + overwrite: true + + - name: Upload ggml base dll + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + with: + name: ggml_base_${{ matrix.arch }}.dll + path: build/bin/${{ matrix.build }}/ggml-base.dll + + - name: Upload ggml cpu dll + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + with: + name: ggml_cpu_${{ matrix.arch }}.dll + path: build/bin/${{ matrix.build }}/ggml-cpu.dll + + - name: Pack bin artifacts + shell: pwsh + run: | + Compress-Archive -Path "build/bin/${{ matrix.build }}" -DestinationPath "whisper-bin-${{ matrix.arch }}.zip" + + - name: Upload binaries + if: ${{ matrix.sdl2 == 'ON' && needs.determine-tag.outputs.should_release == 'true' }} + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + with: + name: whisper-bin-${{ matrix.arch }}.zip + path: whisper-bin-${{ matrix.arch }}.zip + + windows-blas: + runs-on: windows-latest + needs: determine-tag + + strategy: + matrix: + build: [Release] + arch: [Win32, x64] + blas: [ON] + sdl2: [ON] + blasver: [0.3.29] + include: + - arch: Win32 + s2arc: x86 + blasfile: x86 + - arch: x64 + s2arc: x64 + blasfile: x64_64 + - sdl2: ON + s2ver: 2.28.5 + + steps: + - name: Clone + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: Export GitHub Actions cache environment variables + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); + core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); + + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@6fb02220983dee41ce7ae257b6f4d8f9bf5ed4ce # v2 + + - name: Install OpenBLAS and pkgconfiglite + if: matrix.blas == 'ON' + run: | + Invoke-WebRequest "https://github.com/OpenMathLib/OpenBLAS/releases/download/v${{matrix.blasver}}/OpenBLAS-${{matrix.blasver}}_${{matrix.blasfile}}.zip" -OutFile "OpenBLAS-${{matrix.blasver}}.zip" + Expand-Archive "OpenBLAS-${{matrix.blasver}}.zip" -DestinationPath "OpenBLAS-${{matrix.blasver}}" + choco install pkgconfiglite + + - name: Fetch SDL2 and set SDL2_DIR + if: matrix.sdl2 == 'ON' + run: | + C:/msys64/usr/bin/wget.exe -qO sdl2.zip https://github.com/libsdl-org/SDL/releases/download/release-${{ matrix.s2ver }}/SDL2-devel-${{ matrix.s2ver }}-VC.zip + 7z x sdl2.zip + echo "SDL2_DIR=$env:GITHUB_WORKSPACE/SDL2-${{ matrix.s2ver }}/cmake" >> $env:GITHUB_ENV + + - name: Configure + run: > + cmake -S . -B ./build -A ${{ matrix.arch }} + -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" + -DCMAKE_BUILD_TYPE=${{ matrix.build }} + -DGGML_BLAS=${{ matrix.blas }} + -DGGML_BLAS_VENDOR=OpenBLAS + -DBLAS_LIBRARIES="$env:GITHUB_WORKSPACE/OpenBLAS-${{matrix.blasver}}/lib/libopenblas.lib" + -DBLAS_INCLUDE_DIRS="$env:GITHUB_WORKSPACE/OpenBLAS-${{matrix.blasver}}/include" + -DWHISPER_SDL2=${{ matrix.sdl2 }} + -DGGML_NATIVE=OFF + -DWHISPER_BUILD_IS_DEV=${{ env.WHISPER_BUILD_IS_DEV }} + ${{ matrix.arch == 'x64' && '-DGGML_BACKEND_DL=ON -DGGML_CPU_ALL_VARIANTS=ON' || '-DGGML_BMI2=OFF' }} + + - name: Build + run: | + cd ./build + msbuild ALL_BUILD.vcxproj -t:build -p:configuration=${{ matrix.build }} -p:platform=${{ matrix.arch }} + + - name: Copy openblas.dll + if: matrix.blas == 'ON' + run: copy "$env:GITHUB_WORKSPACE/OpenBLAS-${{matrix.blasver}}/bin/libopenblas.dll" build/bin/${{ matrix.build }} + + - name: Copy SDL2.dll + if: matrix.sdl2 == 'ON' + run: copy "$env:SDL2_DIR/../lib/${{ matrix.s2arc }}/SDL2.dll" build/bin/${{ matrix.build }} + + - name: Pack bin artifacts + shell: pwsh + run: | + Compress-Archive -Path "build/bin/${{ matrix.build }}" -DestinationPath "whisper-blas-bin-${{ matrix.arch }}.zip" + + - name: Upload binaries + if: ${{ matrix.blas == 'ON' && matrix.sdl2 == 'ON' && needs.determine-tag.outputs.should_release == 'true' }} + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + with: + name: whisper-blas-bin-${{ matrix.arch }}.zip + path: whisper-blas-bin-${{ matrix.arch }}.zip + + windows-cuda: + runs-on: windows-2022 + needs: determine-tag + strategy: + fail-fast: false + matrix: + build: [Release] + arch: [x64] + cublas: [ON] + sdl2: [ON] + cuda-toolkit: [12.4.0, 11.8.0] + include: + - arch: x64 + sdl2: ON + sdl2_ver: 2.28.5 + steps: + - name: Clone repository + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: Install Ninja + id: install_ninja + run: | + choco install ninja + + - name: Install ccache + uses: ggml-org/ccache-action@v1.2.21 + with: + key: ${{ github.job }}-${{ matrix.cuda-toolkit }}-${{ matrix.build }} + evict-old-files: 5d + + - name: Install Cuda Toolkit 11.8.0 + if: ${{ matrix.cuda-toolkit == '11.8.0' }} + run: | + $CUDA_VERSION = ${{ matrix.cuda-toolkit }} + $CUDA_TOOLKIT_DIR = "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v$CUDA_VERSION" + $CUDA_DOWNLOAD = "https://developer.download.nvidia.com/compute/cuda/redist" + + # Components versions + $CUDART_VER = "11.8.89" + $NVCC_VER = "11.8.89" + $NVRTC_VER = "11.8.89" + $CUBLAS_VER = "11.8.1.74" + $NVTX_VER = "11.8.86" + $VS_VER = "11.8.86" + $NVPROF_VER = "11.8.87" + $CCCL_VER = "11.8.89" + + # Create the directory where the CUDA Toolkit will be installed + mkdir -p $CUDA_TOOLKIT_DIR + + # Install unzip to extract the downloaded files + choco install unzip -y + + # Download all the required components + curl -O "$CUDA_DOWNLOAD/cuda_cudart/windows-x86_64/cuda_cudart-windows-x86_64-${CUDART_VER}-archive.zip" + curl -O "$CUDA_DOWNLOAD/cuda_nvcc/windows-x86_64/cuda_nvcc-windows-x86_64-${NVCC_VER}-archive.zip" + curl -O "$CUDA_DOWNLOAD/cuda_nvrtc/windows-x86_64/cuda_nvrtc-windows-x86_64-${NVRTC_VER}-archive.zip" + curl -O "$CUDA_DOWNLOAD/libcublas/windows-x86_64/libcublas-windows-x86_64-${CUBLAS_VER}-archive.zip" + curl -O "$CUDA_DOWNLOAD/cuda_nvtx/windows-x86_64/cuda_nvtx-windows-x86_64-${NVTX_VER}-archive.zip" + curl -O "$CUDA_DOWNLOAD/visual_studio_integration/windows-x86_64/visual_studio_integration-windows-x86_64-${VS_VER}-archive.zip" + curl -O "$CUDA_DOWNLOAD/cuda_nvprof/windows-x86_64/cuda_nvprof-windows-x86_64-${NVPROF_VER}-archive.zip" + curl -O "$CUDA_DOWNLOAD/cuda_cccl/windows-x86_64/cuda_cccl-windows-x86_64-${CCCL_VER}-archive.zip" + + # Extract all the downloaded files to the CUDA Toolkit directory + unzip '*.zip' -d $CUDA_TOOLKIT_DIR + + # Copy all the extracted files to the main CUDA Toolkit directory + xcopy "$CUDA_TOOLKIT_DIR\cuda_cudart-windows-x86_64-${CUDART_VER}-archive\*" "$CUDA_TOOLKIT_DIR" /E /I /H /Y + xcopy "$CUDA_TOOLKIT_DIR\cuda_nvcc-windows-x86_64-${NVCC_VER}-archive\*" "$CUDA_TOOLKIT_DIR" /E /I /H /Y + xcopy "$CUDA_TOOLKIT_DIR\cuda_nvrtc-windows-x86_64-${NVRTC_VER}-archive\*" "$CUDA_TOOLKIT_DIR" /E /I /H /Y + xcopy "$CUDA_TOOLKIT_DIR\libcublas-windows-x86_64-${CUBLAS_VER}-archive\*" "$CUDA_TOOLKIT_DIR" /E /I /H /Y + xcopy "$CUDA_TOOLKIT_DIR\cuda_nvtx-windows-x86_64-${NVTX_VER}-archive\*" "$CUDA_TOOLKIT_DIR" /E /I /H /Y + xcopy "$CUDA_TOOLKIT_DIR\cuda_nvprof-windows-x86_64-${NVPROF_VER}-archive\*" "$CUDA_TOOLKIT_DIR" /E /I /H /Y + xcopy "$CUDA_TOOLKIT_DIR\cuda_cccl-windows-x86_64-${CCCL_VER}-archive\*" "$CUDA_TOOLKIT_DIR" /E /I /H /Y + xcopy "$CUDA_TOOLKIT_DIR\visual_studio_integration-windows-x86_64-${VS_VER}-archive\*" "$CUDA_TOOLKIT_DIR" /E /I /H /Y + + # Visual Studio integration + xcopy "$CUDA_TOOLKIT_DIR\visual_studio_integration-windows-x86_64-${VS_VER}-archive\visual_studio_integration\MSBuildExtensions\*" "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Microsoft\VC\v170\BuildCustomizations" /E /I /H /Y + + # Set environment variables + echo "$CUDA_TOOLKIT_DIR\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + echo "$CUDA_TOOLKIT_DIR\libnvvp" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + echo "CUDA_PATH=$CUDA_TOOLKIT_DIR" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 + echo "CUDA_PATH_V11_8=$CUDA_TOOLKIT_DIR" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 + + - name: Install Cuda Toolkit 12.4.0 + if: ${{ matrix.cuda-toolkit == '12.4.0' }} + run: | + $CUDA_VERSION = ${{ matrix.cuda-toolkit }} + $CUDA_TOOLKIT_DIR = "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v$CUDA_VERSION" + $CUDA_DOWNLOAD = "https://developer.download.nvidia.com/compute/cuda/redist" + + # Components versions + $CUDART_VER = "12.4.127" + $NVCC_VER = "12.4.131" + $NVRTC_VER = "12.4.127" + $CUBLAS_VER = "12.4.5.8" + $NVTX_VER = "12.4.127" + $PROFILER_VER = "12.4.127" + $VS_VER = "12.4.127" + $NVPROF_VER = "12.4.128" + $CCCL_VER = "12.4.127" + + # Create the directory where the CUDA Toolkit will be installed + mkdir -p $CUDA_TOOLKIT_DIR + + # Install unzip to extract the downloaded files + choco install unzip -y + + # Download all the required components + curl -O "$CUDA_DOWNLOAD/cuda_cudart/windows-x86_64/cuda_cudart-windows-x86_64-${CUDART_VER}-archive.zip" + curl -O "$CUDA_DOWNLOAD/cuda_nvcc/windows-x86_64/cuda_nvcc-windows-x86_64-${NVCC_VER}-archive.zip" + curl -O "$CUDA_DOWNLOAD/cuda_nvrtc/windows-x86_64/cuda_nvrtc-windows-x86_64-${NVRTC_VER}-archive.zip" + curl -O "$CUDA_DOWNLOAD/libcublas/windows-x86_64/libcublas-windows-x86_64-${CUBLAS_VER}-archive.zip" + curl -O "$CUDA_DOWNLOAD/cuda_nvtx/windows-x86_64/cuda_nvtx-windows-x86_64-${NVTX_VER}-archive.zip" + curl -O "$CUDA_DOWNLOAD/cuda_profiler_api/windows-x86_64/cuda_profiler_api-windows-x86_64-${PROFILER_VER}-archive.zip" + curl -O "$CUDA_DOWNLOAD/visual_studio_integration/windows-x86_64/visual_studio_integration-windows-x86_64-${VS_VER}-archive.zip" + curl -O "$CUDA_DOWNLOAD/cuda_nvprof/windows-x86_64/cuda_nvprof-windows-x86_64-${NVPROF_VER}-archive.zip" + curl -O "$CUDA_DOWNLOAD/cuda_cccl/windows-x86_64/cuda_cccl-windows-x86_64-${CCCL_VER}-archive.zip" + + # Extract all the downloaded files to the CUDA Toolkit directory + unzip -q '*.zip' -d $CUDA_TOOLKIT_DIR + + # Copy all the extracted files to the main CUDA Toolkit directory + xcopy "$CUDA_TOOLKIT_DIR\cuda_cudart-windows-x86_64-${CUDART_VER}-archive\*" "$CUDA_TOOLKIT_DIR" /E /I /H /Y + xcopy "$CUDA_TOOLKIT_DIR\cuda_nvcc-windows-x86_64-${NVCC_VER}-archive\*" "$CUDA_TOOLKIT_DIR" /E /I /H /Y + xcopy "$CUDA_TOOLKIT_DIR\cuda_nvrtc-windows-x86_64-${NVRTC_VER}-archive\*" "$CUDA_TOOLKIT_DIR" /E /I /H /Y + xcopy "$CUDA_TOOLKIT_DIR\libcublas-windows-x86_64-${CUBLAS_VER}-archive\*" "$CUDA_TOOLKIT_DIR" /E /I /H /Y + xcopy "$CUDA_TOOLKIT_DIR\cuda_nvtx-windows-x86_64-${NVTX_VER}-archive\*" "$CUDA_TOOLKIT_DIR" /E /I /H /Y + xcopy "$CUDA_TOOLKIT_DIR\cuda_nvprof-windows-x86_64-${NVPROF_VER}-archive\*" "$CUDA_TOOLKIT_DIR" /E /I /H /Y + xcopy "$CUDA_TOOLKIT_DIR\cuda_cccl-windows-x86_64-${CCCL_VER}-archive\*" "$CUDA_TOOLKIT_DIR" /E /I /H /Y + xcopy "$CUDA_TOOLKIT_DIR\cuda_profiler_api-windows-x86_64-${PROFILER_VER}-archive\*" "$CUDA_TOOLKIT_DIR" /E /I /H /Y + xcopy "$CUDA_TOOLKIT_DIR\visual_studio_integration-windows-x86_64-${VS_VER}-archive\*" "$CUDA_TOOLKIT_DIR" /E /I /H /Y + + # Visual Studio integration + xcopy "$CUDA_TOOLKIT_DIR\visual_studio_integration-windows-x86_64-${VS_VER}-archive\visual_studio_integration\MSBuildExtensions\*" "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Microsoft\VC\v170\BuildCustomizations" /E /I /H /Y + + # Set environment variables + echo "$CUDA_TOOLKIT_DIR\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + echo "$CUDA_TOOLKIT_DIR\libnvvp" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + echo "CUDA_PATH=$CUDA_TOOLKIT_DIR" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 + echo "CUDA_PATH_V12_2=$CUDA_TOOLKIT_DIR" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 + + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@6fb02220983dee41ce7ae257b6f4d8f9bf5ed4ce # v2 + + - name: Install 7-Zip + run: choco install 7zip -y + + - name: Fetch SDL2 and set SDL2_DIR + if: matrix.sdl2 == 'ON' + run: | + Invoke-WebRequest -Uri https://github.com/libsdl-org/SDL/releases/download/release-${{ matrix.sdl2_ver }}/SDL2-devel-${{ matrix.sdl2_ver }}-VC.zip -OutFile sdl2.zip + 7z x sdl2.zip + echo "SDL2_DIR=${{ github.workspace }}\SDL2-${{ matrix.sdl2_ver }}\cmake" | Out-File -FilePath $env:GITHUB_ENV -Append + echo "${{ github.workspace }}\SDL2-${{ matrix.sdl2_ver }}\cmake" > SDL2_PATH.txt + + - name: Install cmake + run: choco install cmake + + - name: Build Project + shell: cmd + run: | + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" + cmake --version + where cmake + if "${{ matrix.cuda-toolkit }}" == "11.8.0" ( + set CUDA_FLAGS=-allow-unsupported-compiler -D_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH -D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR + ) else ( + set CUDA_FLAGS= + ) + cmake -S . -B build -G "Ninja Multi-Config" ^ + -DCMAKE_BUILD_TYPE=${{ matrix.build }} ^ + -DGGML_CUDA=${{ matrix.cublas }} ^ + -DWHISPER_SDL2=${{ matrix.sdl2 }} ^ + -DSDL2_DIR="%SDL2_DIR%" ^ + -DCMAKE_POLICY_VERSION_MINIMUM=3.5 ^ + -DCMAKE_CUDA_FLAGS="%CUDA_FLAGS%" ^ + -DGGML_BACKEND_DL=ON ^ + -DGGML_NATIVE=OFF ^ + -DWHISPER_BUILD_IS_DEV=${{ env.WHISPER_BUILD_IS_DEV }} ^ + -DGGML_CPU_ALL_VARIANTS=ON + set /A NINJA_JOBS=%NUMBER_OF_PROCESSORS%-1 + cmake --build build --config ${{ matrix.build }} -j %NUMBER_OF_PROCESSORS% + + - name: Check ccache status after build + run: | + ccache --show-stats + + - name: Copy CUDA DLLs + run: | + Get-ChildItem "$env:CUDA_PATH\bin\" -Filter "*.dll" | + Copy-Item -Destination "build/bin/${{ matrix.build }}" + + - name: Copy SDL2.dll + if: matrix.sdl2 == 'ON' + run: copy "$env:SDL2_DIR/../lib/${{ matrix.arch }}/SDL2.dll" build/bin/${{ matrix.build }} + + - name: Pack bin artifacts + shell: pwsh + run: | + Compress-Archive -Path "build/bin/${{ matrix.build }}" -DestinationPath "whisper-bin-win-cuda-${{ matrix.cuda-toolkit }}-${{ matrix.arch }}.zip" + + - name: Upload binaries + if: ${{ needs.determine-tag.outputs.should_release == 'true' }} + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + with: + name: whisper-bin-win-cuda-${{ matrix.cuda-toolkit }}-${{ matrix.arch }}.zip + path: whisper-bin-win-cuda-${{ matrix.cuda-toolkit }}-${{ matrix.arch }}.zip + + windows-arm64-cpu: + runs-on: windows-2022 + needs: determine-tag + + strategy: + matrix: + build: [Release] + + steps: + - name: Clone + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: Install Ninja + run: | + choco install ninja + + - name: ccache + uses: ggml-org/ccache-action@v1.2.21 + with: + key: release-windows-arm64-cpu + evict-old-files: 1d + + - name: Configure and Build + shell: cmd + run: | + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64_arm64 + cmake -S . -B build -G "Ninja Multi-Config" ^ + -D CMAKE_TOOLCHAIN_FILE=cmake/arm64-windows-llvm.cmake ^ + -DBUILD_SHARED_LIBS=ON ^ + -DGGML_NATIVE=OFF ^ + -DGGML_BACKEND_DL=ON ^ + -DWHISPER_SDL2=OFF ^ + -DWHISPER_BUILD_IS_DEV=${{ env.WHISPER_BUILD_IS_DEV }} + cmake --build build --config ${{ matrix.build }} + + - name: Copy OpenMP runtime DLL + shell: pwsh + run: | + $vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" + $vsInstallPath = & $vswhere -latest -products '*' -requires Microsoft.VisualStudio.Component.VC.Tools.ARM64 -property installationPath + if (-not $vsInstallPath) { + Write-Error "Could not find a Visual Studio install with the ARM64 MSVC toolset" + exit 1 + } + $redistVersion = Get-ChildItem "$vsInstallPath\VC\Redist\MSVC" -Directory | + Where-Object { $_.Name -match '^\d+\.\d+\.\d+$' } | + Sort-Object Name -Descending | Select-Object -First 1 + $ompDll = "$vsInstallPath\VC\Redist\MSVC\$($redistVersion.Name)\debug_nonredist\arm64\Microsoft.VC143.OpenMP.LLVM\libomp140.aarch64.dll" + if (-not (Test-Path $ompDll)) { + Write-Error "libomp140.aarch64.dll not found at $ompDll" + exit 1 + } + Copy-Item $ompDll "build\bin\${{ matrix.build }}\" -Force + + - name: Pack bin artifacts + shell: pwsh + run: | + Compress-Archive -Path "build/bin/${{ matrix.build }}" -DestinationPath "whisper-bin-win-cpu-arm64.zip" + + - name: Upload binaries + if: ${{ needs.determine-tag.outputs.should_release == 'true' }} + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + with: + name: whisper-bin-win-cpu-arm64.zip + path: whisper-bin-win-cpu-arm64.zip + + windows-arm64-opencl-adreno: + runs-on: windows-2022 + needs: determine-tag + + strategy: + matrix: + build: [Release] + + steps: + - name: Clone + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: Install Ninja + run: | + choco install ninja + + - name: Install OpenCL Headers and Libs + run: | + git clone https://github.com/KhronosGroup/OpenCL-Headers + cd OpenCL-Headers + cmake -B build ` + -DBUILD_TESTING=OFF ` + -DOPENCL_HEADERS_BUILD_TESTING=OFF ` + -DOPENCL_HEADERS_BUILD_CXX_TESTS=OFF ` + -DCMAKE_INSTALL_PREFIX="$env:RUNNER_TEMP/opencl-arm64-release" + cmake --build build --target install + cd .. + git clone https://github.com/KhronosGroup/OpenCL-ICD-Loader + cd OpenCL-ICD-Loader + cmake -B build-arm64-release ` + -A arm64 ` + -DCMAKE_PREFIX_PATH="$env:RUNNER_TEMP/opencl-arm64-release" ` + -DCMAKE_INSTALL_PREFIX="$env:RUNNER_TEMP/opencl-arm64-release" + cmake --build build-arm64-release --target install --config release + + - name: ccache + uses: ggml-org/ccache-action@v1.2.21 + with: + key: release-windows-arm64-opencl-adreno + evict-old-files: 1d + + - name: Configure and Build + shell: cmd + run: | + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64_arm64 + cmake -S . -B build -G "Ninja Multi-Config" ^ + -D CMAKE_TOOLCHAIN_FILE=cmake/arm64-windows-llvm.cmake ^ + -DCMAKE_PREFIX_PATH="%RUNNER_TEMP%/opencl-arm64-release" ^ + -DBUILD_SHARED_LIBS=ON ^ + -DGGML_NATIVE=OFF ^ + -DGGML_BACKEND_DL=ON ^ + -DGGML_OPENCL=ON ^ + -DGGML_OPENCL_USE_ADRENO_KERNELS=ON ^ + -DWHISPER_SDL2=OFF ^ + -DWHISPER_BUILD_IS_DEV=${{ env.WHISPER_BUILD_IS_DEV }} + cmake --build build --config ${{ matrix.build }} + + - name: Copy OpenMP runtime DLL + shell: pwsh + run: | + $vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" + $vsInstallPath = & $vswhere -latest -products '*' -requires Microsoft.VisualStudio.Component.VC.Tools.ARM64 -property installationPath + if (-not $vsInstallPath) { + Write-Error "Could not find a Visual Studio install with the ARM64 MSVC toolset" + exit 1 + } + $redistVersion = Get-ChildItem "$vsInstallPath\VC\Redist\MSVC" -Directory | + Where-Object { $_.Name -match '^\d+\.\d+\.\d+$' } | + Sort-Object Name -Descending | Select-Object -First 1 + $ompDll = "$vsInstallPath\VC\Redist\MSVC\$($redistVersion.Name)\debug_nonredist\arm64\Microsoft.VC143.OpenMP.LLVM\libomp140.aarch64.dll" + if (-not (Test-Path $ompDll)) { + Write-Error "libomp140.aarch64.dll not found at $ompDll" + exit 1 + } + Copy-Item $ompDll "build\bin\${{ matrix.build }}\" -Force + + - name: Pack bin artifacts + shell: pwsh + run: | + Compress-Archive -Path "build/bin/${{ matrix.build }}" -DestinationPath "whisper-bin-win-opencl-adreno-arm64.zip" + + - name: Upload binaries + if: ${{ needs.determine-tag.outputs.should_release == 'true' }} + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + with: + name: whisper-bin-win-opencl-adreno-arm64.zip + path: whisper-bin-win-opencl-adreno-arm64.zip + + windows-arm64-cuda: + runs-on: windows-2022 + needs: determine-tag + + strategy: + matrix: + build: [Release] + cuda-toolkit: ['13.4'] + + steps: + - name: Clone repository + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: Install Ninja + run: | + choco install ninja + + - name: Install ccache + uses: ggml-org/ccache-action@v1.2.21 + with: + key: ${{ github.job }}-${{ matrix.cuda-toolkit }}-${{ matrix.build }} + evict-old-files: 5d + + # nvcc cross-compiles for ARM64 from this x64 runner using the ARM64-targeting + # host cl.exe; the CUDA runtime/cublas libs still need their own ARM64 archives + # since the standard x64 CUDA packages don't ship ARM64 binaries. + - name: Install Cuda Toolkit 13.4 (ARM64 target) + run: | + $CUDA_TOOLKIT_DIR = "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4" + $CUDA_DOWNLOAD = "https://developer.download.nvidia.com/compute/cuda/redist" + + # Components versions + $CCCL_VER = "13.3.4.2.1" + $CRT_VER = "13.4.59" + $NVCC_VER = "13.4.59" + $NVVM_VER = "13.4.59" + $CUDART_VER = "13.4.49" + $CUBLAS_VER = "13.7.0.27" + + mkdir -p $CUDA_TOOLKIT_DIR + choco install unzip -y + + curl -O "$CUDA_DOWNLOAD/cccl/windows-x86_64/cccl-windows-x86_64-${CCCL_VER}-archive.zip" + curl -O "$CUDA_DOWNLOAD/cuda_crt/windows-x86_64/cuda_crt-windows-x86_64-${CRT_VER}-archive.zip" + curl -O "$CUDA_DOWNLOAD/cuda_nvcc/windows-x86_64/cuda_nvcc-windows-x86_64-${NVCC_VER}-archive.zip" + curl -O "$CUDA_DOWNLOAD/libnvvm/windows-x86_64/libnvvm-windows-x86_64-${NVVM_VER}-archive.zip" + curl -O "$CUDA_DOWNLOAD/cuda_cudart/windows-arm64/cuda_cudart-windows-arm64-${CUDART_VER}-archive.zip" + curl -O "$CUDA_DOWNLOAD/libcublas/windows-arm64/libcublas-windows-arm64-${CUBLAS_VER}-archive.zip" + + unzip '*.zip' -d $CUDA_TOOLKIT_DIR + + xcopy "$CUDA_TOOLKIT_DIR\cccl-windows-x86_64-${CCCL_VER}-archive\*" "$CUDA_TOOLKIT_DIR" /E /I /H /Y + xcopy "$CUDA_TOOLKIT_DIR\cuda_crt-windows-x86_64-${CRT_VER}-archive\*" "$CUDA_TOOLKIT_DIR" /E /I /H /Y + xcopy "$CUDA_TOOLKIT_DIR\cuda_nvcc-windows-x86_64-${NVCC_VER}-archive\*" "$CUDA_TOOLKIT_DIR" /E /I /H /Y + xcopy "$CUDA_TOOLKIT_DIR\libnvvm-windows-x86_64-${NVVM_VER}-archive\*" "$CUDA_TOOLKIT_DIR" /E /I /H /Y + xcopy "$CUDA_TOOLKIT_DIR\cuda_cudart-windows-arm64-${CUDART_VER}-archive\*" "$CUDA_TOOLKIT_DIR" /E /I /H /Y + xcopy "$CUDA_TOOLKIT_DIR\libcublas-windows-arm64-${CUBLAS_VER}-archive\*" "$CUDA_TOOLKIT_DIR" /E /I /H /Y + + echo "$CUDA_TOOLKIT_DIR\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + echo "CUDA_PATH=$CUDA_TOOLKIT_DIR" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 + + - name: Configure and Build + shell: cmd + run: | + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64_arm64 + cmake -S . -B build -G "Ninja Multi-Config" ^ + -D CMAKE_TOOLCHAIN_FILE=cmake/arm64-windows-llvm-cuda.cmake ^ + -DCMAKE_BUILD_TYPE=${{ matrix.build }} ^ + -DGGML_CUDA=ON ^ + -DCMAKE_POLICY_VERSION_MINIMUM=3.5 ^ + -DGGML_BACKEND_DL=ON ^ + -DGGML_NATIVE=OFF ^ + -DWHISPER_SDL2=OFF ^ + -DWHISPER_BUILD_IS_DEV=${{ env.WHISPER_BUILD_IS_DEV }} + cmake --build build --config ${{ matrix.build }} + + - name: Copy CUDA runtime DLLs + run: | + Get-ChildItem "$env:CUDA_PATH\bin\arm64" -Filter "cudart64_*.dll" | Copy-Item -Destination "build/bin/${{ matrix.build }}" + Get-ChildItem "$env:CUDA_PATH\bin\arm64" -Filter "cublas64_*.dll" | Copy-Item -Destination "build/bin/${{ matrix.build }}" + Get-ChildItem "$env:CUDA_PATH\bin\arm64" -Filter "cublasLt64_*.dll" | Copy-Item -Destination "build/bin/${{ matrix.build }}" + + - name: Copy OpenMP runtime DLL + shell: pwsh + run: | + $vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" + $vsInstallPath = & $vswhere -latest -products '*' -requires Microsoft.VisualStudio.Component.VC.Tools.ARM64 -property installationPath + if (-not $vsInstallPath) { + Write-Error "Could not find a Visual Studio install with the ARM64 MSVC toolset" + exit 1 + } + $redistVersion = Get-ChildItem "$vsInstallPath\VC\Redist\MSVC" -Directory | + Where-Object { $_.Name -match '^\d+\.\d+\.\d+$' } | + Sort-Object Name -Descending | Select-Object -First 1 + $ompDll = "$vsInstallPath\VC\Redist\MSVC\$($redistVersion.Name)\debug_nonredist\arm64\Microsoft.VC143.OpenMP.LLVM\libomp140.aarch64.dll" + if (-not (Test-Path $ompDll)) { + Write-Error "libomp140.aarch64.dll not found at $ompDll" + exit 1 + } + Copy-Item $ompDll "build\bin\${{ matrix.build }}\" -Force + + - name: Pack bin artifacts + shell: pwsh + run: | + Compress-Archive -Path "build/bin/${{ matrix.build }}" -DestinationPath "whisper-bin-win-cuda-${{ matrix.cuda-toolkit }}-arm64.zip" + + - name: Upload binaries + if: ${{ needs.determine-tag.outputs.should_release == 'true' }} + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + with: + name: whisper-bin-win-cuda-${{ matrix.cuda-toolkit }}-arm64.zip + path: whisper-bin-win-cuda-${{ matrix.cuda-toolkit }}-arm64.zip + + ios-xcode-build: + runs-on: macos-latest + needs: determine-tag + + strategy: + matrix: + build: [Release] + + steps: + - name: Checkout code + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: Configure + run: | + cp models/for-tests-ggml-base.en.bin models/ggml-base.en.bin + mkdir models/ggml-base.en-encoder.mlmodelc + + - name: Build + id: cmake_build + run: | + sysctl -a + mkdir build + cd build + cmake -G Xcode .. \ + -DGGML_METAL_USE_BF16=ON \ + -DGGML_METAL_EMBED_LIBRARY=ON \ + -DWHISPER_BUILD_IS_DEV=${{ env.WHISPER_BUILD_IS_DEV }} \ + -DWHISPER_BUILD_EXAMPLES=OFF \ + -DWHISPER_BUILD_TESTS=OFF \ + -DWHISPER_BUILD_SERVER=OFF \ + -DCMAKE_SYSTEM_NAME=iOS \ + -DCMAKE_OSX_DEPLOYMENT_TARGET=14.0 \ + -DCMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM=ggml + cmake --build . --config Release -j $(sysctl -n hw.logicalcpu) -- CODE_SIGNING_ALLOWED=NO + + - name: xcodebuild for swift package + id: xcodebuild + run: | + ./build-xcframework.sh + + - name: Build objc example + run: xcodebuild -project examples/whisper.objc/whisper.objc.xcodeproj -scheme whisper.objc -configuration ${{ matrix.build }} -sdk iphoneos CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO FRAMEWORK_FOLDER_PATH=./build-ios build + + - name: Build swiftui example + run: xcodebuild -project examples/whisper.swiftui/whisper.swiftui.xcodeproj -scheme WhisperCppDemo -configuration ${{ matrix.build }} -sdk iphoneos CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY= -destination 'generic/platform=iOS' FRAMEWORK_FOLDER_PATH=./build-ios build + + - name: Pack artifacts + id: pack_artifacts + run: | + zip --symlinks -r whisper-${{ needs.determine-tag.outputs.tag_name }}-xcframework.zip build-apple/whisper.xcframework + + - name: Upload artifacts + if: ${{ needs.determine-tag.outputs.should_release == 'true' }} + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + with: + path: whisper-${{ needs.determine-tag.outputs.tag_name }}-xcframework.zip + name: whisper-${{ needs.determine-tag.outputs.tag_name }}-xcframework.zip + + release: + if: ${{ github.event.inputs.create_release == 'true' || github.event.inputs.pre_release_tag != '' }} + + runs-on: ubuntu-latest + + needs: + - determine-tag + - ubuntu-cpu + - ios-xcode-build + - windows + - windows-blas + - windows-cuda + - windows-arm64-cpu + - windows-arm64-opencl-adreno + - windows-arm64-cuda + + steps: + - name: Clone + id: checkout + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + with: + fetch-depth: 0 + ssh-key: ${{ secrets.DEPLOY_KEY_RELEASE }} + + # Downloads all the artifacts from the previous jobs + - name: Download artifacts + id: download-artifact + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7 + with: + path: ./artifact + merge-multiple: true + + - name: Move artifacts + id: move_artifacts + run: | + mkdir -p ./release + mv -v ./artifact/*.zip ./release 2>/dev/null || true + mv -v ./artifact/*.tar.gz ./release 2>/dev/null || true + + - name: Attest release artifacts + uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4 + with: + subject-path: 'release/*' + + - name: Create and push git tag + run: | + TAG="${{ needs.determine-tag.outputs.tag_name }}" + if git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null 2>&1; then + echo "Tag ${TAG} already exists, skipping creation" + else + git tag "${TAG}" + git push origin "${TAG}" + fi + + - name: Create release + id: create_release + uses: ggml-org/action-create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ needs.determine-tag.outputs.tag_name }} + prerelease: true + + - name: Upload release + id: upload_release + uses: actions/github-script@ffc2c79a5b2490bd33e0a41c1de74b877714d736 # v3 + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + const path = require('path'); + const fs = require('fs'); + const release_id = '${{ steps.create_release.outputs.id }}'; + for (let file of await fs.readdirSync('./release')) { + if (path.extname(file) === '.zip' || file.endsWith('.tar.gz')) { + console.log('uploadReleaseAsset', file); + await github.repos.uploadReleaseAsset({ + owner: context.repo.owner, + repo: context.repo.repo, + release_id: release_id, + name: file, + data: await fs.readFileSync(`./release/${file}`) + }); + } + } diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7a98228 --- /dev/null +++ b/.gitignore @@ -0,0 +1,70 @@ +*.o +*.a +*.d +.cache/ +.coreml/ +.test/ +.venv/ +.vs/ +.vscode/ +.DS_Store +.vimspector.json +/CMakeSettings.json +/CMakeUserPresets.json +/talk-llama.dSYM/ + +build/ +build-*/ +build_*/ +tmp/ + +# SPM +.build/ +.swiftpm +*.metallib + +ggml-metal-embed.metal +ggml-metal-embed.metal.tmp + +/main +/stream +/command +/talk +/talk-llama +/bench +/quantize +/server +/lsp + +arm_neon.h +sync.sh +libwhisper.a +libwhisper.so +compile_commands.json + +examples/arm_neon.h +examples/whisper.objc/whisper.objc.xcodeproj/xcshareddata +examples/whisper.objc/whisper.objc.xcodeproj/xcuserdata/ +examples/whisper.objc/whisper.objc.xcodeproj/project.xcworkspace/xcuserdata + +extra/bench-gg.txt + +models/*.mlmodel +models/*.mlmodelc +models/*.mlpackage +models/*-encoder-openvino.xml +models/*-encoder-openvino-cache/ +bindings/java/.gradle/ +bindings/java/.idea/ +.idea/ + +benchmark_results.csv +cmake-build-debug/ +.cxx/ +.gradle/ +local.properties +.log +.exe + +# AGENTS +.pi/SYSTEM.md diff --git a/.pi/gg/SYSTEM.md b/.pi/gg/SYSTEM.md new file mode 100644 index 0000000..4788308 --- /dev/null +++ b/.pi/gg/SYSTEM.md @@ -0,0 +1,30 @@ +You are a coding agent. Here are some very important rules that you must follow: + +General: +- Be very precise and concise when writing code, comments, explanations, etc. +- If an inline comment exceeds 2 lines, replace it with: `// note: TODO LATER` +- PR and commit titles format: ` : `. Lookup recents for examples +- Don't try to build or run the code unless you are explicitly asked to do so +- Use the `gh` CLI tool when querying PRs, issues, or other GitHub resources + +Coding: +- When in doubt, always refer to the CONTRIBUTING.md file of the project +- In `test-backend-ops.cpp`, do not mention specific backends (e.g. Metal, CUDA) in comments +- When referencing issues or PRs in comments, use the format: + - C/C++ code: `// ref: <url>` + - Other (CMake, etc.): `# ref: <url>` + +Pull requests (PRs): +- New branch names are prefixed with "gg/" +- Before opening a pull request, ask the user to confirm the description +- Don't explicitly wrap lines in the PR description (each paragraph and bullet is a single line) +- When creating a pull request, look for the repository's PR template and follow it +- For the AI usage disclosure section, write "YES. pi:llama.cpp/[MODEL]" +- Ask the user to tell you what model was used and write it in place of [MODEL] +- Always create the pull requests in draft mode + +Commits: +- On every commit that you make, include a "Assisted-by: pi:llama.cpp/[MODEL]" tag +- Do not explicitly set the git author in commits - rely on the default git config +- Always use `--no-gpg-sign` when committing +- Never `git push` without explicit confirmation from the user diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..f34f324 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,102 @@ +# Instructions for whisper.cpp + +> [!IMPORTANT] +> This project does **not** accept pull requests that are fully or predominantly AI-generated. AI tools may be utilized solely in an assistive capacity. +> +> Read more: [CONTRIBUTING.md](CONTRIBUTING.md) + +AI assistance is permissible only when the majority of the code is authored by a human contributor, with AI employed exclusively for corrections or to expand on verbose modifications that the contributor has already conceptualized (see examples below). + +--- + +## Guidelines for Contributors Using AI + +whisper.cpp is built by humans, for humans. Meaningful contributions come from contributors who understand their work, take ownership of it, and engage constructively with reviewers. + +Maintainers receive numerous pull requests weekly, many of which are AI-generated submissions where the author cannot adequately explain the code, debug issues, or participate in substantive design discussions. Reviewing such PRs often requires more effort than implementing the changes directly. + +**A pull request represents a long-term commitment.** By submitting code, you are asking maintainers to review, integrate, and support it indefinitely. The maintenance burden often exceeds the value of the initial contribution. + +Most maintainers already have access to AI tools. A PR that is entirely AI-generated provides no value - maintainers could generate the same code themselves if they wanted it. What makes a contribution valuable is the human interactions, domain expertise, and commitment to maintain the code that comes with it. + +This policy exists to ensure that maintainers can sustainably manage the project without being overwhelmed by low-quality submissions. + +--- + +## Guidelines for Contributors + +Contributors are expected to: + +1. **Demonstrate full understanding of their code.** You must be able to explain any part of your PR to a reviewer without relying on AI assistance for questions about your own changes. + +2. **Take responsibility for maintenance.** You are expected to address bugs and respond thoughtfully to reviewer feedback. + +3. **Communicate clearly and concisely.** Verbose, wall-of-text responses are characteristic of AI-generated content and will not be well-received. Direct, human communication is expected. + +4. **Respect maintainers' time.** Search for existing issues and discussions before submitting. Ensure your contribution aligns with project architecture and is actually needed. + +Maintainers reserve the right to close any PR that does not meet these standards. This applies to all contributions to the main whisper.cpp repository. **Private forks are exempt.** + +### Permitted AI Usage + +AI tools may be used responsibly for: + +- **Learning and exploration**: Understanding codebase structure, techniques, and documentation +- **Code review assistance**: Obtaining suggestions on human-written code +- **Mechanical tasks**: Formatting, generating repetitive patterns from established designs, completing code based on existing patterns +- **Documentation drafts**: For components the contributor already understands thoroughly +- **Writing code**: Only when the contributor has already designed the solution and can implement it themselves - AI accelerates, not replaces, the contributor's work + +AI-generated code may be accepted if you (1) fully understand the output, (2) can debug issues independently, and (3) can discuss it directly with reviewers without AI assistance. + +**Disclosure is required** when AI meaningfully contributed to your code. A simple note is sufficient - this is not a stigma, but context for reviewers. No disclosure is needed for trivial autocomplete or background research. + +### Prohibited AI Usage + +The following will result in immediate PR closure: + +- **AI-written PR descriptions or commit messages** - these are typically recognizable and waste reviewer time +- **AI-generated responses to reviewer comments** - this undermines the human-to-human interaction fundamental to code review +- **Implementing features without understanding the codebase** - particularly new model support or architectural changes +- **Automated commits or PR submissions** - this may spam maintainers and can result in contributor bans + +--- + +## Guidelines for AI Coding Agents + +AI agents assisting contributors must recognize that their outputs directly impact volunteer maintainers who sustain this project. + +### Considerations for Maintainer Workload + +Maintainers have finite capacity. Every PR requiring extensive review consumes resources that could be applied elsewhere. Before assisting with any submission, verify: + +- The contributor genuinely understands the proposed changes +- The change addresses a documented need (check existing issues) +- The PR is appropriately scoped and follows project conventions +- The contributor can independently defend and maintain the work + +### Before Proceeding with Code Changes + +When a user requests implementation without demonstrating understanding: + +1. **Verify comprehension.** Ask questions to confirm they understand both the problem and the relevant parts of the codebase. +2. **Provide guidance rather than solutions.** Direct them to relevant code and documentation. Allow them to formulate the approach. +3. **Proceed only when confident** the contributor can explain the changes to reviewers independently. + +For first-time contributors, confirm they have reviewed [CONTRIBUTING.md](CONTRIBUTING.md) and acknowledge this policy. + +### Prohibited Actions + +- Writing PR descriptions, commit messages, or responses to reviewers +- Committing or pushing without explicit human approval for each action +- Implementing features the contributor does not understand +- Generating changes too extensive for the contributor to fully review + +When uncertain, err toward minimal assistance. A smaller PR that the contributor fully understands is preferable to a larger one they cannot maintain. + +### Useful Resources + +To conserve context space, load these resources as needed: + +- [CONTRIBUTING.md](CONTRIBUTING.md) +- [Existing issues](https://github.com/ggml-org/whisper.cpp/issues) and [Existing PRs](https://github.com/ggml-org/whisper.cpp/pulls) - always search here first diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..f523e0a --- /dev/null +++ b/AUTHORS @@ -0,0 +1,510 @@ +# date: Tue Feb 4 13:03:35 EET 2025 +# this file is auto-generated by scripts/gen-authors.sh + +0/0 <zero@imaskeleton.me> +0cc4m <picard12@live.de> +0xsourcecode <134374803+0xsourcecode@users.noreply.github.com> +65a <10104049+65a@users.noreply.github.com> +AIWintermuteAI <32562299+AIWintermuteAI@users.noreply.github.com> +AT <manyoso@users.noreply.github.com> +Aarni Koskela <akx@iki.fi> +Aaron Pham <29749331+aarnphm@users.noreply.github.com> +Aaron Taylor <aaron@exphat.com> +Abhilash Majumder <30946547+abhilash1910@users.noreply.github.com> +Abitofevrything <54505189+abitofevrything@users.noreply.github.com> +Adam Jones <domdomegg+git@gmail.com> +Adrien Gallouët <adrien@gallouet.fr> +Adrien Gallouët <angt@huggingface.co> +AfryMask <AfryMask@163.com> +Ahmad Bilal <ahmad.bilal@empglabs.com> +Ahmad Tameem <113388789+Tameem-10xE@users.noreply.github.com> +AidanBeltonS <87009434+AidanBeltonS@users.noreply.github.com> +AidanBeltonS <aidan.belton@codeplay.com> +Akarshan Biswas <akarshan.biswas@gmail.com> +Akarshan Biswas <akarshanbiswas@fedoraproject.org> +Akash Mahajan <akash7190@gmail.com> +Akash Mahajan <akashmjn@stanford.edu> +Al Hoang <3811822-hoanga@users.noreply.gitlab.com> +Alan <unknown> +Albert Jin <albert.jin@gmail.com> +Alberto Cabrera Pérez <alberto.cabrera@codeplay.com> +Alberto Cabrera Pérez <alberto.cabrera@intel.com> +Aleksander Andrzejewski <18704749+aleksanderandrzejewski@users.noreply.github.com> +Alex Azarov <alex@azarov.by> +Alex Bacart <13940752+alex-bacart@users.noreply.github.com> +Alex Evgrashin <aevgrashin@yandex.ru> +Alex O'Connell <35843486+acon96@users.noreply.github.com> +Alexandr Graschenkov <alexandr.graschenkov91@gmail.com> +Alexandru Mariuti <alex@mariuti.com> +Alexey Kharlamov <alexey@kharlamov.biz> +Alfredo Montesinos <alfredo.montesinos@g.austincc.edu> +Ali Alameh <ali.alameh@isae.edu.lb> +Alter <0x7c48@gmail.com> +Ananta Bastola <anantarajbastola@gmail.com> +Andreas Kieslinger <47689530+aendk@users.noreply.github.com> +Andreas Lubbe <git@lubbe.org> +Andreu Huguet <andreuhuguet@gmail.com> +Andrew Huynh <a5thuynh@gmail.com> +Andrew Minh Nguyen <40281306+amqdn@users.noreply.github.com> +Andrew S <andrews54757@gmail.com> +Andy Maloney <asmaloney@gmail.com> +Anton Kostin <masguit42@users.noreply.github.com> +Artyom Mezin <psycho.fading@gmail.com> +Asad Memon <asad.lionpk@gmail.com> +Ashraful Islam <ashraful.meche@gmail.com> +AsukaMinato <asukaminato@nyan.eu.org> +AustinMroz <austinmroz@utexas.edu> +Avik Sengupta <avik@sengupta.net> +Bader-eddine Ouaich <49657842+baderouaich@users.noreply.github.com> +Baffin Lee <baffinlee@gmail.com> +Ben Ashbaugh <ben.ashbaugh@intel.com> +Ben Nortier <bjnortier@gmail.com> +Benjamin Heiniger <benjamin.heiniger@bluewin.ch> +Bernhard M. Wiedemann <githubbmwprimary@lsmod.de> +Binozo <70137898+Binozo@users.noreply.github.com> +Bo-Yi Wu <appleboy.tw@gmail.com> +Boris Bliznioukov <blib@mail.com> +Borislav Stanimirov <b.stanimirov@abv.bg> +Brad Murray <59848399+bradmurray-dt@users.noreply.github.com> +Brian Murray <brian@bmurray.ca> +CRD716 <crd716@gmail.com> +Canis Lupus <Canis-UK@users.noreply.github.com> +Carlos Zoido <mrgalleta@gmail.com> +Carolinabanana <140120812+Carolinabanana@users.noreply.github.com> +CarterLi999 <664681047@qq.com> +ChangSeok Oh <shivamidow@users.noreply.github.com> +Changyeon Kim <cyzero.kim@samsung.com> +Chaoqun <27287694+OpenWaygate@users.noreply.github.com> +Charles Xu <63788048+chaxu01@users.noreply.github.com> +Charles Xu <charles.xu@arm.com> +Chen Xi <xi2.chen@intel.com> +Chen Xi <xixichen08@foxmail.com> +Chenguang Li <87689256+noemotiovon@users.noreply.github.com> +Chia-Hsiang Cheng <88014292+garychia@users.noreply.github.com> +Chidi Williams <williamschidi1@gmail.com> +Chris Elrod <elrodc@gmail.com> +Christian <12550267+iceychris@users.noreply.github.com> +Christian Kastner <ckk@kvr.at> +Clifford Heath <clifford.heath@gmail.com> +Clint Herron <hanclinto@gmail.com> +Colin <github@whoisc.cc> +Conrad Kramer <conrad@conradkramer.com> +Corey Earwood <iamcgn+github@gmail.com> +CrispStrobe <154636388+CrispStrobe@users.noreply.github.com> +DAN™ <dranger003@gmail.com> +DGdev91 <DGdev91@users.noreply.github.com> +Damian Czaja <trojan295@protonmail.com> +Dan Johansson <164997844+eddnjjn@users.noreply.github.com> +Dan Johansson <dan.johansson@arm.com> +Daniel Bevenius <daniel.bevenius@gmail.com> +Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com> +Daniel Ziegenberg <daniel@ziegenberg.at> +Daniele <57776841+daniandtheweb@users.noreply.github.com> +Dave <dave-fl@users.noreply.github.com> +Dave Airlie <airlied@gmail.com> +Dave Airlie <airlied@redhat.com> +Daven Sanassy <daven@vochlea.co.uk> +David <dnhkng@gmail.com> +David Thorpe <djt@mutablelogic.com> +DavidKorczynski <david@adalogics.com> +Davidson Francis <davidsondfgl@gmail.com> +Dener Stassun <denerstassun@gmail.com> +Dibakar Gope <dibakar.gope@arm.com> +Didzis Gosko <didzis@users.noreply.github.com> +Diego Devesa <slarengh@gmail.com> +Digipom <admin@digipom.com> +Dimo <dimo@ieee.org> +Djip007 <3705339+Djip007@users.noreply.github.com> +Djip007 <djip.perois@free.fr> +Dody Suria Wijaya <dodysw@gmail.com> +Dou Xinpeng <15529241576@163.com> +Dou Xinpeng <81913537+Dou-Git@users.noreply.github.com> +Dr. Tom Murphy VII Ph.D <499244+tom7@users.noreply.github.com> +Duncan McConnell <ddmcconnell4@gmail.com> +Egor Egorov <me@egorfine.com> +Elkana Bardugo <ttv200@gmail.com> +Emmanuel Schmidbauer <eschmidbauer@gmail.com> +Engininja2 <139037756+Engininja2@users.noreply.github.com> +Eric Curtin <ericcurtin17@gmail.com> +Eric Swanson <eswanson@alloscomp.com> +Eric Tendian <erictendian@gmail.com> +Eric Zhang <34133756+EZForever@users.noreply.github.com> +Erik Scholz <Green-Sky@users.noreply.github.com> +Evan Jones <evan.q.jones@gmail.com> +Evan Martin <evan.martin@gmail.com> +Eve <139727413+netrunnereve@users.noreply.github.com> +Evgeny Kuznetsov <evgeny@kuznetsov.md> +F1L1P <78918286+F1L1Pv2@users.noreply.github.com> +Faisal Zaghloul <quic_fzaghlou@quicinc.com> +Fangjun Kuang <csukuangfj@gmail.com> +Felix <stenbackfelix@gmail.com> +Finn Voorhees <finnvoorhees@gmail.com> +FirstTimeEZ <179362031+FirstTimeEZ@users.noreply.github.com> +FlippFuzz <41221030+FlippFuzz@users.noreply.github.com> +Frankie Robertson <frankier@users.noreply.github.com> +Gang Chen <goncha@gmail.com> +Gavin Cai <gavin1818@hotmail.com> +George Hindle <george@georgehindle.com> +Georgi Gerganov <ggerganov@gmail.com> +Gilad S <7817232+giladgd@users.noreply.github.com> +Gilad S <giladgd@users.noreply.github.com> +Gilad S. <7817232+giladgd@users.noreply.github.com> +GitAritron <103900385+GitAritron@users.noreply.github.com> +GiviMAD <GiviMAD@users.noreply.github.com> +Gleicon Moraes <gleicon@gmail.com> +Gregor Jasny <gjasny@googlemail.com> +Guillaume Wenzek <gwenzek@users.noreply.github.com> +HY. Kelvin Lee <34256578+hykelvinlee42@users.noreply.github.com> +Halalaluyafail3 <55773281+Halalaluyafail3@users.noreply.github.com> +Hang <bebound@gmail.com> +Haus1 <haus.xda@gmail.com> +Herman Semenov <GermanAizek@yandex.ru> +HimariO <dsfhe49854@gmail.com> +Hong Bo PENG <penghb@cn.ibm.com> +Hrishikesh Barman <geekodour@users.noreply.github.com> +Hugo <hugo@whynothugo.nl> +Ian Bicking <ian@ianbicking.org> +Ian Bull <irbull@eclipsesource.com> +Ihar Hrachyshka <ihrachys@redhat.com> +Ikko Ashimine <eltociear@gmail.com> +Ikko Eltociear Ashimine <eltociear@gmail.com> +InconsolableCellist <23345188+InconsolableCellist@users.noreply.github.com> +Ismatulla Mansurov <47342870+sapoepsilon@users.noreply.github.com> +Ivan <nekotekina@gmail.com> +Ivan Filipov <159561759+vanaka11@users.noreply.github.com> +Ivan Gorin <ivangorin21@gmail.com> +Ivo von Putzer Reibegg <ivo.putzer@gmail.com> +JJ <103335846+computerscienceiscool@users.noreply.github.com> +Jack Mousseau <jmousseau@users.noreply.github.com> +JacobLinCool <jacoblincool@gmail.com> +Jakub Ráček <blizzcz@gmail.com> +Jared Van Bortel <jared@nomic.ai> +Jay Binks <jaybinks@gmail.com> +Jayant <jayantyadav202@gmail.com> +Jeff Bolz <jbolz@nvidia.com> +Jeroen Mostert <jeroen.mostert@cm.com> +Jhen-Jie Hong <developer@jhen.me> +Jhen-Jie Hong <iainst0409@gmail.com> +JidongZhang-THU <1119708529@qq.com> +Jo Liss <joliss42@gmail.com> +Joe Todd <joe.todd@codeplay.com> +Johan <jr.raffin@gmail.com> +Johannes Gäßler <johannesg@5d6.de> +John Balis <phobossystems@gmail.com> +JohnnyB <jboero@users.noreply.github.com> +Jonathan Soo <jcsoo@agora.com> +Jonno <1160532+razodactyl@users.noreply.github.com> +Joonas Pihlajamaa <joonas.pihlajamaa@iki.fi> +Jose <34888496+Jerry-Master@users.noreply.github.com> +Josh Bleecher Snyder <josharian@gmail.com> +Josscii <jossciiweiyi@gmail.com> +Judd <foldl@users.noreply.github.com> +Jumper775 <78500318+jumpers775@users.noreply.github.com> +Jun Hee Yoo <contact.jhyoo@gmail.com> +Junil Kim <logyourself@gmail.com> +Justina Cho <justcho5@gmail.com> +Justine Tunney <jtunney@gmail.com> +Justine Tunney <jtunney@mozilla.com> +KITAITI Makoto <KitaitiMakoto@gmail.com> +KP Kaiser <kirk@zothcorp.com> +Kamilake <exjang0@gmail.com> +Karol Kontny <82021046+kkontny@users.noreply.github.com> +Karthick <j.karthic2004@gmail.com> +Kartik Saranathan <278928+Kartiku@users.noreply.github.com> +Kasumi <90275229+kasumi-1@users.noreply.github.com> +Kawrakow <48489457+ikawrakow@users.noreply.github.com> +Kendrick Taylor <kendrick@circuitsix.com> +Kevin Brothaler <admin@digipom.com> +Kevin Gibbons <bakkot@gmail.com> +Konosuke Sakai <konosuke@konosuke.work> +Konstantin Zhuravlyov <konstantin.zhuravlyov@amd.com> +Kreijstal <rainb@tfwno.gf> +Kylin <56434533+KyL0N@users.noreply.github.com> +LBlue <153975653+lbluep@users.noreply.github.com> +Larry Battle <larry.battle.tech@gmail.com> +Laytan Laats <laytanlaats@hotmail.com> +Leo Moll <leo.moll@yeasoft.com> +Lexevolution <31176843+Lexevolution@users.noreply.github.com> +LittleLoli <26589867+WhichWho@users.noreply.github.com> +Lucas Zanek <57494138+LucasZNK@users.noreply.github.com> +Luis Herrera <herrera-luis@users.noreply.github.com> +Lukas Rist <glaslos@gmail.com> +M. A. Ali <73258591+MightyStud@users.noreply.github.com> +M. Eren Akbiyik <erenakbiyik@gmail.com> +Ma Mingfei <mingfei.ma@intel.com> +Maciek <maciek.mab122@gmail.com> +Mahesh Madhav <67384846+heshpdx@users.noreply.github.com> +Marcin Mielniczuk <marmistrz.dev@zoho.eu> +Mark Karpelès <MagicalTux@users.noreply.github.com> +Mark Zhuang <zhuangqiubin@gmail.com> +Markus Tavenrath <mtavenrath@users.noreply.github.com> +Martin Delille <martin@delille.org> +Martin Warnaar <martinwarnaar@gmail.com> +Masaya, Kato <62578291+msy-kato@users.noreply.github.com> +Matheus de Sousa <23645013+keyehzy@users.noreply.github.com> +Mathieu Baudier <mbaudier@argeo.org> +Mathijs de Bruin <mathijs@mathijsfietst.nl> +Matija Pevec <mightymatth@users.noreply.github.com> +Matt Stephenson <mstephenson6@users.noreply.github.com> +Max Krasnyansky <max.krasnyansky@gmail.com> +Max Krasnyansky <quic_maxk@quicinc.com> +Maximiliano Levi <8160966+maxilevi@users.noreply.github.com> +Meng, Hengyu <hengyu.meng@intel.com> +Mengqing Cao <cmq0113@163.com> +Michael Podvitskiy <podvitskiymichael@gmail.com> +Michael Rienstra <mrienstra@gmail.com> +Mikhail Grigorev <sleuthhound@gmail.com> +Mohammadreza Hendiani <hendiani.mohammadreza@gmail.com> +Mohit Agarwal <mohit@sdf.org> +Molly Sophia <mollysophia379@gmail.com> +Murilo Santana <mvrilo@gmail.com> +NETZkultur GmbH <mulholland@netzkultur.de> +Natsu <chino@hotococoa.moe> +Neil Chudleigh <nchudleigh@users.noreply.github.com> +Neo Zhang <14088817+arthw@users.noreply.github.com> +Neo Zhang Jianyu <jianyu.zhang@intel.com> +Neuman Vong <neuman.vong@gmail.com> +Nicholai Tukanov <nicholaitukanov@gmail.com> +Nicholas Albion <nalbion@yahoo.com> +Nico Bosshard <nico@bosshome.ch> +Nicolò Scipione <nicolo.scipione@codeplay.com> +Niels Mayer <Niels.Mayer@gmail.com> +Nikita Sarychev <42014488+sARY77@users.noreply.github.com> +Nikolaj Olsson <nikse.dk@gmail.com> +Okabintaro <103938900+Okabintaro@users.noreply.github.com> +Oleg Sidorov <me@whitebox.io> +Oleg Sidorov <oleg@sidorov.nl> +Olivier Chafik <ochafik@users.noreply.github.com> +Ondrej Kokes <ondrej.kokes@gmail.com> +Ouadie EL FAROUKI <ouadie.elfarouki@codeplay.com> +PAB <pierreantoine.bannier@gmail.com> +Paul Tsochantaris <ptsochantaris@icloud.com> +Pedro Probst <pprobst@insiberia.net> +Peng <hzp1024@qq.com> +Peter <peter277@users.noreply.github.com> +Philipp Zabel <philipp.zabel@gmail.com> +Philippe Normand <phil@base-art.net> +Philippe Normand <philn@igalia.com> +Plamen Minev <pacominev@gmail.com> +Prashant Vithule <119530321+Vithulep@users.noreply.github.com> +Przemysław Pawełczyk <przemoc@gmail.com> +Qianhe Chen <54462604+chenqianhe@users.noreply.github.com> +R0CKSTAR <xiaodong.ye@mthreads.com> +R0CKSTAR <yeahdongcn@gmail.com> +Radoslav Gerganov <rgerganov@gmail.com> +Radosław Gryta <radek.gryta@gmail.com> +Rahul Vadhyar <107788610+RahulVadhyar@users.noreply.github.com> +Raiya Araki <83504221+rai62@users.noreply.github.com> +Reinforce-II <fate@eastal.com> +Reinis Muiznieks <muiznieks.reinis@gmail.com> +RelatedTitle <r3latedtitle@gmail.com> +Rémy Oudompheng <oudomphe@phare.normalesup.org> +RhinoDevel <RhinoDevel@users.noreply.github.com> +Rich Jones <miserlou@gmail.com> +Robert Ormandi <52251610+ormandi@users.noreply.github.com> +Robin <robin.xw@hotmail.com> +Roddur Dasgupta <roddurd@gmail.com> +Roland Rabien <figbug@gmail.com> +Romain Biessy <romain.biessy@codeplay.com> +Ronsor <ronsor@ronsor.pw> +Rotem Dan <rotemdan@gmail.com> +Ryan Hitchman <hitchmanr@gmail.com> +Ryan Metcalfe <107415876+RyanMetcalfeInt8@users.noreply.github.com> +RyanChang <ftes90015@gmail.com> +SRHMorris <69468379+SRHMorris@users.noreply.github.com> +SXX <sxx1136965276@gmail.com> +Sacha Arbonel <sacha.arbonel@hotmail.fr> +Salman Faroz <stsfaroz@gmail.com> +Salvatore Mesoraca <s.mesoraca16@gmail.com> +Sam <49637763+Onlyartist9@users.noreply.github.com> +Sam Pullara <spullara@gmail.com> +Samuel Durante <44513615+samueldurantes@users.noreply.github.com> +Sanchit Gandhi <93869735+sanchit-gandhi@users.noreply.github.com> +Sandro Hanea <40202887+sandrohanea@users.noreply.github.com> +Sergio López <slp@redhat.com> +Sergio López <slp@sinrega.org> +Shanshan Shen <467638484@qq.com> +Shijie <821898965@qq.com> +Shupei Fan <dymarkfan@outlook.com> +Siddharth Ramakrishnan <srr2141@columbia.edu> +Sigbjørn Skjæret <sigbjorn.skjaeret@scala.com> +Simon Moisselin <simon.moisstoll@gmail.com> +Sindre Sorhus <sindresorhus@gmail.com> +Slava Primenko <primenko.s@gmail.com> +Srihari-mcw <96763064+Srihari-mcw@users.noreply.github.com> +Stavros Panakakis <53979866+Stavrospanakakis@users.noreply.github.com> +Stefan Sydow <s.sydow@heinlein-video.de> +Stefan Sydow <stefan@sydow.email> +Syahmi Azhar <prsyahmi@gmail.com> +Syed Jafri <syedjafri97@gmail.com> +Sơn Phan Trung <phantrungson17@gmail.com> +Taisei Mima <bhbstar.me@gmail.com> +Takeshi Inoue <inoue.takeshi@gmail.com> +Tamotsu Takahashi <ttakah+github@gmail.com> +Taras Glek <taras@thegp.com> +Tauseef Mohiuddin <35351464+tauseefmohammed2@users.noreply.github.com> +Thamster <Thamster@users.noreply.github.com> +Thijs Raymakers <thijs@raymakers.nl> +Thomas Fitzsimmons <fitzsim@fitzsim.org> +Tiago Fassoni <tiagofassoni@users.noreply.github.com> +Tienshiao Ma <tienshiao@tienshiao.org> +Tim Miller <drasticactions@users.noreply.github.com> +Timothy Cronin <40186632+4imothy@users.noreply.github.com> +Tobrun <tobrun.van.nuland@gmail.com> +Todd <taf2@users.noreply.github.com> +Toliver <teejae@gmail.com> +Tong Li <31761981+litongjava@users.noreply.github.com> +Tony Wasserka <4840017+neobrain@users.noreply.github.com> +Topping1 <78745143+Topping1@users.noreply.github.com> +Travis Cline <travis.cline@gmail.com> +UEXTM.com <84163508+uextm@users.noreply.github.com> +UsernamesLame <156965854+UsernamesLame@users.noreply.github.com> +Vadim Peretokin <vperetokin@hey.com> +Valentin Gosu <1454649+valenting@users.noreply.github.com> +Vin Misra <vinith@alum.mit.edu> +Vulcan <93451215+trholding@users.noreply.github.com> +WhiteOlivierus <36532695+WhiteOlivierus@users.noreply.github.com> +William Tambellini <william.tambellini@gmail.com> +William Tambellini <wtambellini@sdl.com> +Wilson Silva <wilson.dsigns@gmail.com> +Xiang (Kevin) Li <kevinli020508@gmail.com> +Xiao-Yong Jin <jinxiaoyong@gmail.com> +XiaotaoChen <chenxiaotao1234@gmail.com> +Xingchen Song(宋星辰) <xingchensong1996@163.com> +Xinpeng Dou <81913537+Dou-Git@users.noreply.github.com> +Xuan Son Nguyen <thichthat@gmail.com> +Yajing Tang <phillis@google.com> +Yang Shen <aplshenyang@gmail.com> +Yunès <jean.baptiste.yunes@free.fr> +Yuri Khrustalev <ykhrustalev@users.noreply.github.com> +Yusuf Redžić <48274562+redzic@users.noreply.github.com> +ZaBlazzingZephyrus <119159668+blazingzephyr@users.noreply.github.com> +Zhenwei Jin <109658203+kylo5aby@users.noreply.github.com> +Zhiyuan Li <lizhiyuan@uniartisan.com> +Zhiyuan Li <uniartisan2017@gmail.com> +Zigfrid Zvezdin <ziggerZZ@gmail.com> +Zollner <24618122+Zolliner@users.noreply.github.com> +a3sh <38979186+A3shTnT@users.noreply.github.com> +ag2s20150909 <19373730+ag2s20150909@users.noreply.github.com> +agray3 <agray3@users.noreply.github.com> +ai-at-home <149282006+ai-at-home@users.noreply.github.com> +aldorof <aldorof@users.noreply.github.com> +alonfaraj <alonfaraj@gmail.com> +amd-dwang <dong.wang@amd.com> +amritahs-ibm <amritahs@linux.vnet.ibm.com> +andypayne <apayne@gmail.com> +ardfork <134447697+ardfork@users.noreply.github.com> +arizhih <40765267+arizhih@users.noreply.github.com> +automaticcat <daogiatuank54@gmail.com> +bandoti <141645996+bandoti@users.noreply.github.com> +be-next <jerome.ramette@gmail.com> +bert hubert <bert@hubertnet.nl> +billyct <billy_allen@126.com> +bmwl <brian.marshall@tolko.com> +bobqianic <129547291+bobqianic@users.noreply.github.com> +bocytko <bocytko+github@gmail.com> +boolemancer <48014766+boolemancer@users.noreply.github.com> +boolemancer <boolemancer@gmail.com> +bradmit <151883577+bradmit@users.noreply.github.com> +brunofaustino <b.fa.amorim@gmail.com> +bssrdf <merlintiger@hotmail.com> +byte-6174 <88070277+byte-6174@users.noreply.github.com> +cdosoftei <ciprian.dosoftei@gmail.com> +clach04 <Chris.Clark@actian.com> +compilade <113953597+compilade@users.noreply.github.com> +compilade <git@compilade.net> +conradg <conradjgodfrey@gmail.com> +crummyh <elijah@crums.us> +ddpasa <112642920+ddpasa@users.noreply.github.com> +denersc <denerstassun@gmail.com> +dscripka <dscripka@users.noreply.github.com> +duthils <duthils@duthils.net> +ecneladis <ecneladis@users.noreply.github.com> +faker <nspyia2002@gmail.com> +fitzsim <fitzsim@fitzsim.org> +fj-y-saito <85871716+fj-y-saito@users.noreply.github.com> +fraxy-v <65565042+fraxy-v@users.noreply.github.com> +genevera (she/her) <genevera@users.noreply.github.com> +geniusnut <geniusnut@gmail.com> +gilbertgong <gilbert.gong@gmail.com> +gn64 <yukikaze.jp@gmail.com> +goldwaving <77494627+goldwaving@users.noreply.github.com> +greeshmay <greeshmay@gmail.com> +haopeng <657407891@qq.com> +hipudding <huafengchun@gmail.com> +hsinhoyeh <yhh92u@gmail.com> +hydai <z54981220@gmail.com> +iamthad <thadeus.j.fleming@gmail.com> +issixx <46835150+issixx@users.noreply.github.com> +james wolf <contractorwolf@hotmail.com> +jdomke <28772296+jdomke@users.noreply.github.com> +jettoblack <jettoblack@gmail.com> +jiez <373447296@qq.com> +joecryptotoo <80373433+joecryptotoo@users.noreply.github.com> +jorismertz <35079666+jorismertz@users.noreply.github.com> +junchao-loongson <68935141+junchao-loongson@users.noreply.github.com> +junkfood <69683722+JunkFood02@users.noreply.github.com> +jwijffels <jwijffels@bnosac.be> +k.h.lai <adrian.k.h.lai@outlook.com> +kamranjon <kamranjon@gmail.com> +katsu560 <katsu560oo-@docomo.ne.jp> +kennethge <57784063+kenneth-ge@users.noreply.github.com> +keyehzy <msamuel@aluno.puc-rio.br> +kunnis <kunnis@users.noreply.github.com> +l3utterfly <gc.pthzfoldr@gmail.com> +leejet <leejet714@gmail.com> +leo-pony <nengjunma@outlook.com> +lhez <quic_lih@quicinc.com> +litong <31761981+litongjava@users.noreply.github.com> +liuwei-git <14815172+liuwei-git@users.noreply.github.com> +lnyan <lkwq007@gmail.com> +luoyu-intel <yu.luo@intel.com> +m.bell <m.bell@techsmith.com> +mahorozte <41834471+mahorozte@users.noreply.github.com> +mashizora <30516315+mashizora@users.noreply.github.com> +matt23654 <matthew.webber@protonmail.com> +matteo <matteogeniaccio@yahoo.it> +mgrachten <maarten@grachten.eu> +mkiol <mkiol@users.noreply.github.com> +mky_coder <47767389+mkycoder@users.noreply.github.com> +novag <7754358+novag@users.noreply.github.com> +pajowu <pajowu@pajowu.de> +pengxin99 <pengxin.yuan@intel.com> +petterreinholdtsen <pere-github@hungry.com> +polarmoon <90010972+polarmoon@users.noreply.github.com> +rlapray <lapray.romain@gmail.com> +sandrohanea <40202887+sandrohanea@users.noreply.github.com> +semiformal-net <84111142+semiformal-net@users.noreply.github.com> +shibukazu <61775791+shibukazu@users.noreply.github.com> +shikokuchuo <53399081+shikokuchuo@users.noreply.github.com> +slaren <slarengh@gmail.com> +slashlib <slashlib@users.noreply.github.com> +snadampal <87143774+snadampal@users.noreply.github.com> +someone13574 <81528246+someone13574@users.noreply.github.com> +st-gr <38470677+st-gr@users.noreply.github.com> +stduhpf <stephduh@live.fr> +stormofice <58337328+stormofice@users.noreply.github.com> +texmex76 <40733439+texmex76@users.noreply.github.com> +thefinaldegree <thefinaldegree@gmail.com> +thewh1teagle <61390950+thewh1teagle@users.noreply.github.com> +toboil-features <160222185+toboil-features@users.noreply.github.com> +trixirt <trix@redhat.com> +ulatekh <ulatekh@yahoo.com> +undef <undefdev@gmail.com> +uvos <devnull@uvos.xyz> +uvos <philipp@uvos.xyz> +valVk <valVk@users.noreply.github.com> +venkr <venkateshrameshkumar+1@gmail.com> +vicalloy <zbirder@gmail.com> +wangshuai09 <391746016@qq.com> +woachk <24752637+woachk@users.noreply.github.com> +xctan <axunlei@gmail.com> +xdrudis <xavierdrudis@yahoo.es> +yuri@FreeBSD <yuri@FreeBSD> +zhangjixiong <code.zjx@gmail.com> +zhentaoyu <zhentao.yu@intel.com> +zhouwg <6889919+zhouwg@users.noreply.github.com> +zhouwg <zhouwg2000@gmail.com> +谢乃闻 <sienaiwun@users.noreply.github.com> +布客飞龙 <562826179@qq.com> +Артём Земляк <azemlyak@smart-consulting.ru> diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..4c049f3 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,324 @@ +cmake_minimum_required(VERSION 3.5) # for add_link_options and implicit target directories. +project("whisper.cpp" C CXX) +include(CheckIncludeFileCXX) + +### whisper.cpp version +set(WHISPER_VERSION_MAJOR 1) +set(WHISPER_VERSION_MINOR 9) +set(WHISPER_VERSION_PATCH 4) +set(WHISPER_VERSION_BASE "${WHISPER_VERSION_MAJOR}.${WHISPER_VERSION_MINOR}.${WHISPER_VERSION_PATCH}") + +# whether this is a development/nightly build +# set this to OFF when making a release from a release tag (vX.Y.Z) +# ref: https://github.com/ggml-org/ggml/discussions/1579 +option(WHISPER_BUILD_IS_DEV "whisper: dev build" ON) + +if (WHISPER_BUILD_IS_DEV) + set(WHISPER_VERSION "${WHISPER_VERSION_BASE}-dev") +else() + # TODO: check that the current commit is tagged correctly according to the version specified above + set(WHISPER_VERSION "${WHISPER_VERSION_BASE}") +endif() + +message(STATUS "whisper.cpp version: ${WHISPER_VERSION}") + +#set(CMAKE_WARN_DEPRECATED YES) +set(CMAKE_WARN_UNUSED_CLI YES) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +if (NOT XCODE AND NOT MSVC AND NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") +endif() + +# Add path to modules +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/") + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + +if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + set(WHISPER_STANDALONE ON) + + include(git-vars) + + # configure project version + configure_file(${CMAKE_SOURCE_DIR}/bindings/javascript/package-tmpl.json ${CMAKE_SOURCE_DIR}/bindings/javascript/package.json @ONLY) +else() + set(WHISPER_STANDALONE OFF) +endif() + +if (EMSCRIPTEN) + set(BUILD_SHARED_LIBS_DEFAULT OFF) + + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + + option(WHISPER_WASM_SINGLE_FILE "whisper: embed WASM inside the generated whisper.js" ON) + + # TODO: without these, we get the following error: + # wasm-ld: error: --shared-memory is disallowed by whisper.cpp.o because it was not compiled with 'atomics' or 'bulk-memory' features. + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") + + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_STACK=5242880") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -s TOTAL_STACK=5242880") + + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated") +else() + if (MINGW) + set(BUILD_SHARED_LIBS_DEFAULT OFF) + else() + set(BUILD_SHARED_LIBS_DEFAULT ON) + endif() +endif() + +option(BUILD_SHARED_LIBS "build shared libraries" ${BUILD_SHARED_LIBS_DEFAULT}) + +# +# option list +# + +# debug +option(WHISPER_ALL_WARNINGS "whisper: enable all compiler warnings" ON) +option(WHISPER_ALL_WARNINGS_3RD_PARTY "whisper: enable all compiler warnings in 3rd party libs" OFF) + +# build +option(WHISPER_FATAL_WARNINGS "whisper: enable -Werror flag" OFF) +option(WHISPER_USE_SYSTEM_GGML "whisper: use system-installed GGML library" OFF) +option(WHISPER_USE_SYSTEM_LLAMA "whisper: use system-installed llama.cpp library" OFF) + +if (WHISPER_USE_SYSTEM_LLAMA AND NOT WHISPER_USE_SYSTEM_GGML) + message(STATUS "WHISPER_USE_SYSTEM_LLAMA=ON: setting WHISPER_USE_SYSTEM_GGML=ON") + set(WHISPER_USE_SYSTEM_GGML ON CACHE BOOL "" FORCE) +endif() + +# sanitizers +option(WHISPER_SANITIZE_THREAD "whisper: enable thread sanitizer" OFF) +option(WHISPER_SANITIZE_ADDRESS "whisper: enable address sanitizer" OFF) +option(WHISPER_SANITIZE_UNDEFINED "whisper: enable undefined sanitizer" OFF) + +# extra artifacts +option(WHISPER_BUILD_TESTS "whisper: build tests" ${WHISPER_STANDALONE}) +option(WHISPER_BUILD_EXAMPLES "whisper: build examples" ${WHISPER_STANDALONE}) +option(WHISPER_BUILD_SERVER "whisper: build server example" ${WHISPER_STANDALONE}) + +# 3rd party libs +option(WHISPER_CURL "whisper: use libcurl to download model from an URL" OFF) +option(WHISPER_SDL2 "whisper: support for libSDL2" OFF) + +if (CMAKE_SYSTEM_NAME MATCHES "Linux") + option(WHISPER_COMMON_FFMPEG "whisper: examples link with ffmpeg libs in order to decode more audio formats" OFF) +endif() + +option(WHISPER_COREML "whisper: enable Core ML framework" OFF) +option(WHISPER_COREML_ALLOW_FALLBACK "whisper: allow non-CoreML fallback" OFF) +option(WHISPER_OPENVINO "whisper: support for OpenVINO" OFF) +option(WHISPER_VITISAI "whisper: support for AMD Vitis AI" OFF) + +# Required for relocatable CMake package +include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/build-info.cmake) + +# override ggml options +set(GGML_SANITIZE_THREAD ${WHISPER_SANITIZE_THREAD}) +set(GGML_SANITIZE_ADDRESS ${WHISPER_SANITIZE_ADDRESS}) +set(GGML_SANITIZE_UNDEFINED ${WHISPER_SANITIZE_UNDEFINED}) +set(GGML_ALL_WARNINGS ${WHISPER_ALL_WARNINGS}) +set(GGML_FATAL_WARNINGS ${WHISPER_FATAL_WARNINGS}) + +# transition helpers +function (whisper_option_depr TYPE OLD NEW) + if (${OLD}) + message(${TYPE} "${OLD} is deprecated and will be removed in the future.\nUse ${NEW} instead\n") + set(${NEW} ON) + endif() +endfunction() + +whisper_option_depr(FATAL_ERROR WHISPER_CUBLAS GGML_CUDA) +whisper_option_depr(WARNING WHISPER_CUDA GGML_CUDA) +whisper_option_depr(WARNING WHISPER_KOMPUTE GGML_KOMPUTE) +whisper_option_depr(WARNING WHISPER_METAL GGML_METAL) +whisper_option_depr(WARNING WHISPER_METAL_EMBED_LIBRARY GGML_METAL_EMBED_LIBRARY) +whisper_option_depr(WARNING WHISPER_NATIVE GGML_NATIVE) +whisper_option_depr(WARNING WHISPER_OPENMP GGML_OPENMP) +whisper_option_depr(WARNING WHISPER_RPC GGML_RPC) +whisper_option_depr(WARNING WHISPER_SYCL GGML_SYCL) +whisper_option_depr(WARNING WHISPER_SYCL_F16 GGML_SYCL_F16) +whisper_option_depr(WARNING WHISPER_CCACHE GGML_CCACHE) +whisper_option_depr(WARNING WHISPER_FFMPEG WHISPER_COMMON_FFMPEG) + +if (GGML_CUDA AND NOT MSVC) + #GGML_CUDA enabled, add the necessary compile options -Wno-deprecated-gpu-targets + add_compile_options(-Wno-deprecated-gpu-targets) +endif() + +# +# build the library +# + +if (NOT TARGET ggml) + if (WHISPER_USE_SYSTEM_GGML) + find_package(ggml REQUIRED) + if (NOT ggml_FOUND) + message(FATAL_ERROR "System-installed GGML library not found.") + endif() + add_library(ggml ALIAS ggml::ggml) + else() + add_subdirectory(ggml) + if(WIN32) + # The following adds a _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR macro and is a workaround for + # the Windows C++ standard library which does not support constexpr mutexes. + # From the release notes://github.com/microsoft/STL/wiki/Changelog + # Disable constexpr mutex constructor on Windows + # Fixed mutex's constructor to be constexpr. #3824 #4000 #4339 + # Note: Programs that aren't following the documented restrictions on binary compatibility may encounter + # null dereferences in mutex machinery. You must follow this rule: + # When you mix binaries built by different supported versions of the toolset, the Redistributable version + # must be at least as new as the latest toolset used by any app component. + # You can define _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR as an escape hatch. + # + # Specifically to whisper.cpp this would cause a crash when using the Java bindings. + # resulting in a Invalid memory access error. + target_compile_definitions(ggml-base PRIVATE _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR) + endif() + endif() + # ... otherwise assume ggml is added by a parent CMakeLists.txt +endif() +add_subdirectory(src) + +# +# install +# + +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) + +if (NOT DEFINED WHISPER_BUILD_NUMBER) + set(WHISPER_BUILD_NUMBER ${BUILD_NUMBER}) +endif() +if (NOT DEFINED WHISPER_BUILD_COMMIT) + set(WHISPER_BUILD_COMMIT ${BUILD_COMMIT}) +endif() + +set(WHISPER_INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR} CACHE PATH "Location of header files") +set(WHISPER_LIB_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR} CACHE PATH "Location of library files") +set(WHISPER_BIN_INSTALL_DIR ${CMAKE_INSTALL_BINDIR} CACHE PATH "Location of binary files") + +get_directory_property(WHISPER_TRANSIENT_DEFINES COMPILE_DEFINITIONS) + +set_target_properties(whisper PROPERTIES PUBLIC_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/include/whisper.h) + +install(TARGETS whisper LIBRARY PUBLIC_HEADER) + +target_compile_definitions(whisper PRIVATE + WHISPER_VERSION="${WHISPER_VERSION}" +) + +set_target_properties(parakeet PROPERTIES PUBLIC_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/include/parakeet.h) +install(TARGETS parakeet LIBRARY PUBLIC_HEADER) + +target_compile_definitions(parakeet PRIVATE + PARAKEET_VERSION="${WHISPER_VERSION}" +) + +configure_package_config_file( + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/whisper-config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/whisper-config.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/whisper + PATH_VARS + WHISPER_INCLUDE_INSTALL_DIR + WHISPER_LIB_INSTALL_DIR + WHISPER_BIN_INSTALL_DIR ) + +write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/whisper-config-version.cmake + VERSION ${WHISPER_VERSION} + COMPATIBILITY SameMajorVersion) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/whisper-config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/whisper-config-version.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/whisper) + +configure_file(cmake/whisper.pc.in + "${CMAKE_CURRENT_BINARY_DIR}/whisper.pc" + @ONLY) + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/whisper.pc" + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) + +set(PARAKEET_INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR} CACHE PATH "Location of header files") +set(PARAKEET_LIB_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR} CACHE PATH "Location of library files") +set(PARAKEET_BIN_INSTALL_DIR ${CMAKE_INSTALL_BINDIR} CACHE PATH "Location of binary files") + +configure_package_config_file( + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/parakeet-config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/parakeet-config.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/parakeet + PATH_VARS + PARAKEET_INCLUDE_INSTALL_DIR + PARAKEET_LIB_INSTALL_DIR + PARAKEET_BIN_INSTALL_DIR) + +write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/parakeet-config-version.cmake + VERSION ${WHISPER_VERSION} + COMPATIBILITY SameMajorVersion) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/parakeet-config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/parakeet-config-version.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/parakeet) + +configure_file(cmake/parakeet.pc.in + "${CMAKE_CURRENT_BINARY_DIR}/parakeet.pc" + @ONLY) + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/parakeet.pc" + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) + +# +# programs, examples and tests +# + +if (WHISPER_BUILD_TESTS AND NOT CMAKE_JS_VERSION) + include(CTest) + add_subdirectory(tests) +endif () + +if (WHISPER_BUILD_EXAMPLES) + add_subdirectory(examples) +endif() + +if (MSVC) + set(MSVC_WARNING_FLAGS + /wd4101 # Unreferenced local variable + /wd4005 # Macro redefinition + /wd4065 # switch statement contains 'default' but no 'case' labels + /wd4267 # Conversion from 'size_t' to a smaller type, possible loss of data + /wd4244 # Conversion from one type to another type, possible loss of ata + /wd4805 # Unsafe mix of type + /wd4305 # Truncation from 'type1' to 'type2' (often double to float) + /wd4996 # Function or variable may be unsafe/deprecated + ) + function(disable_msvc_warnings target_name) + if(TARGET ${target_name}) + target_compile_options(${target_name} PRIVATE ${MSVC_WARNING_FLAGS}) + endif() + endfunction() + + if (WHISPER_BUILD_EXAMPLES) + disable_msvc_warnings(whisper) + disable_msvc_warnings(common) + disable_msvc_warnings(common-sdl) + disable_msvc_warnings(lsp) + disable_msvc_warnings(wchess-core) + disable_msvc_warnings(whisper-command) + disable_msvc_warnings(whisper-cli) + disable_msvc_warnings(whisper-server) + disable_msvc_warnings(whisper-stream) + disable_msvc_warnings(whisper-talk-llama) + disable_msvc_warnings(whisper-bench) + disable_msvc_warnings(quantize) + disable_msvc_warnings(vad-speech-segments) + endif() +endif() diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..b5afeb3 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,95 @@ +{ + "version": 4, + "configurePresets": [ + { + "name": "base", + "hidden": true, + "generator": "Ninja", + "binaryDir": "${sourceDir}/build-${presetName}", + "cacheVariables": { + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", + "CMAKE_INSTALL_RPATH": "$ORIGIN;$ORIGIN/.." + } + }, + { + "name": "sycl-base", + "hidden": true, + "generator": "Ninja", + "binaryDir": "${sourceDir}/build-${presetName}", + "cacheVariables": { + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", + "CMAKE_CXX_COMPILER": "icx", + "CMAKE_C_COMPILER": "cl", + "GGML_SYCL": "ON", + "CMAKE_INSTALL_RPATH": "$ORIGIN;$ORIGIN/.." + } + }, + { "name": "debug", "hidden": true, "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" } }, + { "name": "release", "hidden": true, "cacheVariables": { "CMAKE_BUILD_TYPE": "Release" } }, + { "name": "reldbg", "hidden": true, "cacheVariables": { "CMAKE_BUILD_TYPE": "RelWithDebInfo" } }, + { "name": "static", "hidden": true, "cacheVariables": { "GGML_STATIC": "ON" } }, + { "name": "sycl_f16", "hidden": true, "cacheVariables": { "GGML_SYCL_F16": "ON" } }, + { "name": "vulkan", "hidden": true, "cacheVariables": { "GGML_VULKAN": "ON" } }, + + { + "name": "x64-windows-llvm", "hidden": true, + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/cmake/x64-windows-llvm.cmake" + } + }, + + { + "name": "arm64-windows-llvm", "hidden": true, + "architecture": { "value": "arm64", "strategy": "external" }, + "toolset": { "value": "host=x64", "strategy": "external" }, + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/cmake/arm64-windows-llvm.cmake" + } + }, + + { + "name": "arm64-apple-clang", "hidden": true, + "architecture": { "value": "arm64", "strategy": "external" }, + "toolset": { "value": "host=x64", "strategy": "external" }, + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/cmake/arm64-apple-clang.cmake" + } + }, + { + "name": "x64-linux-gcc", "hidden": true, + "cacheVariables": { + "CMAKE_C_COMPILER": "gcc", + "CMAKE_CXX_COMPILER": "g++" + } + }, + { "name": "x64-linux-gcc-debug", "inherits": [ "base", "x64-linux-gcc", "debug" ] }, + { "name": "x64-linux-gcc-release", "inherits": [ "base", "x64-linux-gcc", "release" ] }, + { "name": "x64-linux-gcc-reldbg", "inherits": [ "base", "x64-linux-gcc", "reldbg" ] }, + { "name": "x64-linux-gcc+static-release", "inherits": [ "base", "x64-linux-gcc", "release", "static" ] }, + + { "name": "arm64-windows-llvm-debug", "inherits": [ "base", "arm64-windows-llvm", "debug" ] }, + { "name": "arm64-windows-llvm-release", "inherits": [ "base", "arm64-windows-llvm", "reldbg" ] }, + { "name": "arm64-windows-llvm+static-release", "inherits": [ "base", "arm64-windows-llvm", "reldbg", "static" ] }, + + { "name": "arm64-apple-clang-debug", "inherits": [ "base", "arm64-apple-clang", "debug" ] }, + { "name": "arm64-apple-clang-release", "inherits": [ "base", "arm64-apple-clang", "reldbg" ] }, + { "name": "arm64-apple-clang+static-release", "inherits": [ "base", "arm64-apple-clang", "reldbg", "static" ] }, + + { "name": "x64-windows-llvm-debug", "inherits": [ "base", "x64-windows-llvm", "debug" ] }, + { "name": "x64-windows-llvm-release", "inherits": [ "base", "x64-windows-llvm", "release" ] }, + { "name": "x64-windows-llvm-reldbg", "inherits": [ "base", "x64-windows-llvm", "reldbg" ] }, + { "name": "x64-windows-llvm+static-release", "inherits": [ "base", "x64-windows-llvm", "reldbg", "static" ] }, + + { "name": "x64-windows-msvc-debug", "inherits": [ "base", "debug" ] }, + { "name": "x64-windows-msvc-release", "inherits": [ "base", "reldbg" ] }, + { "name": "x64-windows-msvc+static-release", "inherits": [ "base", "reldbg", "static" ] }, + + { "name": "x64-windows-sycl-debug", "inherits": [ "sycl-base", "debug" ] }, + { "name": "x64-windows-sycl-debug-f16", "inherits": [ "sycl-base", "debug", "sycl_f16" ] }, + { "name": "x64-windows-sycl-release", "inherits": [ "sycl-base", "release" ] }, + { "name": "x64-windows-sycl-release-f16", "inherits": [ "sycl-base", "release", "sycl_f16" ] }, + + { "name": "x64-windows-vulkan-debug", "inherits": [ "base", "vulkan", "debug" ] }, + { "name": "x64-windows-vulkan-release", "inherits": [ "base", "vulkan", "release" ] } + ] +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..c301604 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,176 @@ +# Contributors + +The project differentiates between 3 levels of contributors: + +- Contributors: people who have contributed before (no special privileges) +- Collaborators (Triage): people with significant contributions, who may be responsible for some parts of the code, and are expected to maintain and review contributions for the code they own +- Maintainers: responsible for reviewing and merging PRs, after approval from the code owners + +# AI Usage Policy + +> [!IMPORTANT] +> This project does **not** accept pull requests that are fully or predominantly AI-generated. AI tools may be utilized solely in an assistive capacity. +> +> Repeated violations of this policy may result in your account being permanently banned from contributing to the project. +> +> Detailed information regarding permissible and restricted uses of AI can be found in the [AGENTS.md](AGENTS.md) file. + +Code that is initially generated by AI and subsequently edited will still be considered AI-generated. AI assistance is permissible only when the majority of the code is authored by a human contributor, with AI employed exclusively for corrections or to expand on verbose modifications that the contributor has already conceptualized (e.g., generating repeated lines with minor variations). + +If AI is used to generate any portion of the code, contributors must adhere to the following requirements: + +1. Explicitly disclose the manner in which AI was employed. +2. Perform a comprehensive manual review prior to submitting the pull request. +3. Be prepared to explain every line of code they submitted when asked about it by a maintainer. +4. It is strictly prohibited to use AI to write your posts for you (bug reports, feature requests, pull request descriptions, Github discussions, responding to humans, ...). + +For more info, please refer to the [AGENTS.md](AGENTS.md) file. + +# Pull requests (for contributors & collaborators) + +Before submitting your PR: +- Search for existing PRs to prevent duplicating efforts +- whisper.cpp uses the ggml tensor library for model evaluation. If you are unfamiliar with ggml, consider taking a look at the [examples in the ggml repository](https://github.com/ggml-org/ggml/tree/master/examples/). [simple](https://github.com/ggml-org/ggml/tree/master/examples/simple) shows the bare minimum for using ggml. [gpt-2](https://github.com/ggml-org/ggml/tree/master/examples/gpt-2) has minimal implementations for language model inference using GPT-2. [mnist](https://github.com/ggml-org/ggml/tree/master/examples/mnist) demonstrates how to train and evaluate a simple image classifier +- Test your changes: + - Execute [the full CI locally on your machine](ci/README.md) before publishing +- Create separate PRs for each feature or fix: + - Avoid combining unrelated changes in a single PR + - For intricate features, consider opening a feature request first to discuss and align expectations +- If you are a new contributor + - Limit your open PRs to 1 + - Do not submit trivial fixes (e.g. typos, formatting changes) + +After submitting your PR: +- Expect requests for modifications to ensure the code meets whisper.cpp's standards for quality and long-term maintainability +- Maintainers will rely on your insights and approval when making a final decision to approve and merge a PR +- If your PR becomes stale, rebase it on top of latest `master` to get maintainers attention + +# Pull requests (for maintainers) + +- Squash-merge PRs +- Use the following format for the squashed commit title: `<module> : <commit title> (#<issue_number>)`. For example: `utils : fix typo in utils.py (#1234)` +- Optionally pick a `<module>` from here: https://github.com/ggml-org/llama.cpp/wiki/Modules +- Let other maintainers merge their own PRs +- When merging a PR, make sure you have a good understanding of the changes +- Be mindful of maintenance: most of the work going into a feature happens after the PR is merged. If the PR author is not committed to contribute long-term, someone else needs to take responsibility (you) + +Maintainers reserve the right to decline review or close pull requests for any reason, without any questions, particularly under any of the following conditions: +- The proposed change is already mentioned in the roadmap or an existing issue, and it has been assigned to someone. +- The pull request duplicates an existing one. +- The contributor fails to adhere to this contributing guide or the AI policy. + +# Coding guidelines + +- Avoid adding third-party dependencies, extra files, extra headers, etc. +- Always consider cross-compatibility with other operating systems and architectures +- Avoid fancy-looking modern STL constructs, use basic `for` loops, avoid templates, keep it simple +- Vertical alignment makes things more readable and easier to batch edit +- Clean-up any trailing whitespaces, use 4 spaces for indentation, brackets on the same line, `void * ptr`, `int & a` +- Use sized integer types such as `int32_t` in the public API, e.g. `size_t` may also be appropriate for allocation sizes or byte offsets +- Declare structs with `struct foo {}` instead of `typedef struct foo {} foo` + - In C++ code omit optional `struct` and `enum` keyword whenever they are not necessary + ```cpp + // OK + llama_context * ctx; + const llama_rope_type rope_type; + + // not OK + struct llama_context * ctx; + const enum llama_rope_type rope_type; + ``` + + _(NOTE: this guideline is yet to be applied to the `whisper.cpp` codebase. New code should follow this guideline.)_ + +- Try to follow the existing patterns in the code (indentation, spaces, etc.). In case of doubt use `clang-format` (from clang-tools v15+) to format the added code +- For anything not covered in the current guidelines, refer to the [C++ Core Guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines) +- Tensors store data in row-major order. We refer to dimension 0 as columns, 1 as rows, 2 as matrices +- Matrix multiplication is unconventional: [`C = ggml_mul_mat(ctx, A, B)`](https://github.com/ggml-org/llama.cpp/blob/880e352277fc017df4d5794f0c21c44e1eae2b84/ggml.h#L1058-L1064) means $C^T = A B^T \Leftrightarrow C = B A^T.$ + +![matmul](media/matmul.png) + +# Naming guidelines + +- Use `snake_case` for function, variable and type names +- Naming usually optimizes for longest common prefix (see https://github.com/ggml-org/ggml/pull/302#discussion_r1243240963) + + ```cpp + // not OK + int small_number; + int big_number; + + // OK + int number_small; + int number_big; + ``` + +- Enum values are always in upper case and prefixed with the enum name + + ```cpp + enum llama_vocab_type { + LLAMA_VOCAB_TYPE_NONE = 0, + LLAMA_VOCAB_TYPE_SPM = 1, + LLAMA_VOCAB_TYPE_BPE = 2, + LLAMA_VOCAB_TYPE_WPM = 3, + LLAMA_VOCAB_TYPE_UGM = 4, + LLAMA_VOCAB_TYPE_RWKV = 5, + }; + ``` + +- The general naming pattern is `<class>_<method>`, with `<method>` being `<action>_<noun>` + + ```cpp + llama_model_init(); // class: "llama_model", method: "init" + llama_sampler_chain_remove(); // class: "llama_sampler_chain", method: "remove" + llama_sampler_get_seed(); // class: "llama_sampler", method: "get_seed" + llama_set_embeddings(); // class: "llama_context", method: "set_embeddings" + llama_n_threads(); // class: "llama_context", method: "n_threads" + llama_adapter_lora_free(); // class: "llama_adapter_lora", method: "free" + ``` + + - The `get` `<action>` can be omitted + - The `<noun>` can be omitted if not necessary + - The `_context` suffix of the `<class>` is optional. Use it to disambiguate symbols when needed + - Use `init`/`free` for constructor/destructor `<action>` + +- Use the `_t` suffix when a type is supposed to be opaque to the user - it's not relevant to them if it is a struct or anything else + + ```cpp + typedef struct llama_context * llama_context_t; + + enum llama_pooling_type llama_pooling_type(const llama_context_t ctx); + ``` + + _(NOTE: this guideline is yet to be applied to the `whisper.cpp` codebase. New code should follow this guideline)_ + +- C/C++ filenames are all lowercase with dashes. Headers use the `.h` extension. Source files use the `.c` or `.cpp` extension +- Python filenames are all lowercase with underscores + +- _(TODO: abbreviations usage)_ + +# Preprocessor directives + +- _(TODO: add guidelines with examples and apply them to the codebase)_ + + ```cpp + #ifdef FOO + #endif // FOO + ``` + +# Code maintenance + +- New code should follow the guidelines (coding, naming, etc.) outlined in this document. Exceptions are allowed in isolated, backend-specific parts of the code that do not interface directly with the `ggml` interfaces. + _(NOTE: for legacy reasons, existing code is not required to follow this guideline)_ + +- For changes in server, please make sure to refer to the [server development documentation](./tools/server/README-dev.md) + +# Documentation + +- Documentation is a community effort +- When you need to look into the source code to figure out how to use an API consider adding a short summary to the header file for future reference +- When you notice incorrect or outdated documentation, please update it + +# Resources + +The Github issues, PRs and discussions contain a lot of information that can be useful to get familiar with the codebase. For convenience, some of the more important information is referenced from Github projects: + +https://github.com/ggml-org/whisper.cpp/projects diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e7dca55 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023-2026 The ggml authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7c4fbe3 --- /dev/null +++ b/Makefile @@ -0,0 +1,58 @@ +# +# Audio samples +# + +.PHONY: build +build: + cmake -B build $(CMAKE_ARGS) + cmake --build build --config Release --parallel $(shell nproc) + +# download a few audio samples into folder "./samples": +.PHONY: samples +samples: + @echo "Downloading samples..." + @mkdir -p samples + @wget --quiet --show-progress -O samples/gb0.ogg https://upload.wikimedia.org/wikipedia/commons/2/22/George_W._Bush%27s_weekly_radio_address_%28November_1%2C_2008%29.oga + @wget --quiet --show-progress -O samples/gb1.ogg https://upload.wikimedia.org/wikipedia/commons/1/1f/George_W_Bush_Columbia_FINAL.ogg + @wget --quiet --show-progress -O samples/hp0.ogg https://upload.wikimedia.org/wikipedia/en/d/d4/En.henryfphillips.ogg + @wget --quiet --show-progress -O samples/mm1.wav https://cdn.openai.com/whisper/draft-20220913a/micro-machines.wav + @wget --quiet --show-progress -O samples/a13.mp3 https://upload.wikimedia.org/wikipedia/commons/transcoded/6/6f/Apollo13-wehaveaproblem.ogg/Apollo13-wehaveaproblem.ogg.mp3 + @wget --quiet --show-progress -O samples/diffusion2023-07-03.flac https://archive.org/download/diffusion2023-07-03/diffusion2023-07-03.flac + +# +# Models +# + +# if not already downloaded, the following targets download the specified model and +# runs it on all samples in the folder "./samples": + +.PHONY: tiny.en +.PHONY: tiny +.PHONY: base.en +.PHONY: base +.PHONY: small.en +.PHONY: small +.PHONY: medium.en +.PHONY: medium +.PHONY: large-v1 +.PHONY: large-v2 +.PHONY: large-v3 +.PHONY: large-v3-turbo + +tiny.en tiny base.en base small.en small medium.en medium large-v1 large-v2 large-v3 large-v3-turbo: + bash ./models/download-ggml-model.sh $@ + cmake -B build $(CMAKE_ARGS) + cmake --build build --config Release --parallel $(shell nproc) + @echo "" + @echo "===============================================" + @echo "Running $@ on all samples in ./samples ..." + @echo "===============================================" + @echo "" + @for f in samples/*.{flac,mp3,ogg,wav}; do \ + echo "----------------------------------------------" ; \ + echo "[+] Running $@ on $$f ... (run 'ffplay $$f' to listen)" ; \ + echo "----------------------------------------------" ; \ + echo "" ; \ + ./build/bin/whisper-cli -m models/ggml-$@.bin -f $$f ; \ + echo "" ; \ + done diff --git a/README.md b/README.md new file mode 100644 index 0000000..6bf407d --- /dev/null +++ b/README.md @@ -0,0 +1,970 @@ +# whisper.cpp + +<div align="center"> + +![whisper.cpp](https://user-images.githubusercontent.com/1991296/235238348-05d0f6a4-da44-4900-a1de-d0707e75b763.jpeg) + +[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT) +[![Release](https://img.shields.io/github/v/release/ggml-org/whisper.cpp?filter=v*)](https://github.com/ggml-org/whisper.cpp/releases) +[![Actions Status](https://github.com/ggml-org/whisper.cpp/workflows/CI/badge.svg)](https://github.com/ggml-org/whisper.cpp/actions) +[![Conan Center](https://shields.io/conan/v/whisper-cpp)](https://conan.io/center/whisper-cpp) +[![npm](https://img.shields.io/npm/v/whisper.cpp.svg)](https://www.npmjs.com/package/whisper.cpp/) + +</div> + +High-performance inference of [OpenAI's Whisper](https://github.com/openai/whisper) automatic speech recognition (ASR) model: + +- Plain C/C++ implementation without dependencies +- Apple Silicon first-class citizen - optimized via ARM NEON, Accelerate framework, Metal and [Core ML](#core-ml-support) +- AVX intrinsics support for x86 architectures +- [VSX intrinsics support for POWER architectures](#power-vsx-intrinsics) +- Mixed F16 / F32 precision +- [Integer quantization support](#quantization) +- Zero memory allocations at runtime +- [Vulkan support](#vulkan-gpu-support) +- Support for CPU-only inference +- [Efficient GPU support for NVIDIA](#nvidia-gpu-support) +- [AMD ROCm GPU support](#amd-rocm-gpu-support) +- [AMD Ryzen AI NPU Support](#amd-ryzen-ai-npu-support) +- [OpenVINO Support](#openvino-support) +- [Ascend NPU Support](#ascend-npu-support) +- [Moore Threads GPU Support](#moore-threads-gpu-support) +- [C-style API](https://github.com/ggml-org/whisper.cpp/blob/master/include/whisper.h) +- [Voice Activity Detection (VAD)](#voice-activity-detection-vad) + +Supported platforms: + +- [x] Mac OS (Intel and Arm) +- [x] [iOS](examples/whisper.objc) +- [x] [Android](examples/whisper.android) +- [x] [Java](bindings/java/README.md) +- [x] Linux / [FreeBSD](https://github.com/ggml-org/whisper.cpp/issues/56#issuecomment-1350920264) +- [x] [WebAssembly](examples/whisper.wasm) +- [x] Windows ([MSVC](https://github.com/ggml-org/whisper.cpp/blob/master/.github/workflows/build.yml#L117-L144) and [MinGW](https://github.com/ggml-org/whisper.cpp/issues/168)) +- [x] [Raspberry Pi](https://github.com/ggml-org/whisper.cpp/discussions/166) +- [x] [Docker](https://github.com/ggml-org/whisper.cpp/pkgs/container/whisper.cpp) + +The entire high-level implementation of the model is contained in [whisper.h](include/whisper.h) and [whisper.cpp](src/whisper.cpp). +The rest of the code is part of the [`ggml`](https://github.com/ggml-org/ggml) machine learning library. + +Having such a lightweight implementation of the model allows to easily integrate it in different platforms and applications. +As an example, here is a video of running the model on an iPhone 13 device - fully offline, on-device: [whisper.objc](examples/whisper.objc) + +https://user-images.githubusercontent.com/1991296/197385372-962a6dea-bca1-4d50-bf96-1d8c27b98c81.mp4 + +You can also easily make your own offline voice assistant application: [command](examples/command) + +https://user-images.githubusercontent.com/1991296/204038393-2f846eae-c255-4099-a76d-5735c25c49da.mp4 + +On Apple Silicon, the inference runs fully on the GPU via Metal: + +https://github.com/ggml-org/whisper.cpp/assets/1991296/c82e8f86-60dc-49f2-b048-d2fdbd6b5225 + +## Quick start + +First clone the repository: + +```bash +git clone https://github.com/ggml-org/whisper.cpp.git +``` + +Navigate into the directory: + +``` +cd whisper.cpp +``` + +Then, download one of the Whisper [models](models/README.md) converted in [`ggml` format](#ggml-format). For example: + +```bash +sh ./models/download-ggml-model.sh base.en +``` + +Now build the [whisper-cli](examples/cli) example and transcribe an audio file like this: + +```bash +# build the project +cmake -B build +cmake --build build -j --config Release + +# transcribe an audio file +./build/bin/whisper-cli -f samples/jfk.wav +``` + +--- + +For a quick demo, simply run `make base.en`. + +The command downloads the `base.en` model converted to custom `ggml` format and runs the inference on all `.wav` samples in the folder `samples`. + +For detailed usage instructions, run: `./build/bin/whisper-cli -h` + +Note that the [whisper-cli](examples/cli) example currently runs only with 16-bit WAV files, so make sure to convert your input before running the tool. +For example, you can use `ffmpeg` like this: + +```bash +ffmpeg -i input.mp3 -ar 16000 -ac 1 -c:a pcm_s16le output.wav +``` + +## More audio samples + +If you want some extra audio samples to play with, simply run: + +``` +make -j samples +``` + +This will download a few more audio files from Wikipedia and convert them to 16-bit WAV format via `ffmpeg`. + +You can download and run the other models as follows: + +``` +make -j tiny.en +make -j tiny +make -j base.en +make -j base +make -j small.en +make -j small +make -j medium.en +make -j medium +make -j large-v1 +make -j large-v2 +make -j large-v3 +make -j large-v3-turbo +``` + +## Memory usage + +| Model | Disk | Mem | +| ------ | ------- | ------- | +| tiny | 75 MiB | ~273 MB | +| base | 142 MiB | ~388 MB | +| small | 466 MiB | ~852 MB | +| medium | 1.5 GiB | ~2.1 GB | +| large | 2.9 GiB | ~3.9 GB | + +## POWER VSX Intrinsics + +`whisper.cpp` supports POWER architectures and includes code which +significantly speeds operation on Linux running on POWER9/10, making it +capable of faster-than-realtime transcription on underclocked Raptor +Talos II. Ensure you have a BLAS package installed, and replace the +standard cmake setup with: + +```bash +# build with GGML_BLAS defined +cmake -B build -DGGML_BLAS=1 +cmake --build build -j --config Release +./build/bin/whisper-cli [ .. etc .. ] +``` + +## Quantization + +`whisper.cpp` supports integer quantization of the Whisper `ggml` models. +Quantized models require less memory and disk space and depending on the hardware can be processed more efficiently. + +Here are the steps for creating and using a quantized model: + +```bash +# quantize a model with Q5_0 method +cmake -B build +cmake --build build -j --config Release +./build/bin/quantize models/ggml-base.en.bin models/ggml-base.en-q5_0.bin q5_0 + +# run the examples as usual, specifying the quantized model file +./build/bin/whisper-cli -m models/ggml-base.en-q5_0.bin ./samples/gb0.wav +``` + +## Core ML support + +On Apple Silicon devices, the Encoder inference can be executed on the Apple Neural Engine (ANE) via Core ML. This can result in significant +speed-up - more than x3 faster compared with CPU-only execution. Here are the instructions for generating a Core ML model and using it with `whisper.cpp`: + +- Install Python dependencies needed for the creation of the Core ML model: + + ```bash + pip install ane_transformers + pip install openai-whisper + pip install coremltools + ``` + + - To ensure `coremltools` operates correctly, please confirm that [Xcode](https://developer.apple.com/xcode/) is installed and execute `xcode-select --install` to install the command-line tools. + - Python 3.11 is recommended. + - MacOS Sonoma (version 14) or newer is recommended, as older versions of MacOS might experience issues with transcription hallucination. + - [OPTIONAL] It is recommended to utilize a Python version management system, such as [Miniconda](https://docs.conda.io/en/latest/miniconda.html) for this step: + - To create an environment, use: `conda create -n py311-whisper python=3.11 -y` + - To activate the environment, use: `conda activate py311-whisper` + +- Generate a Core ML model. For example, to generate a `base.en` model, use: + + ```bash + ./models/generate-coreml-model.sh base.en + ``` + + This will generate the folder `models/ggml-base.en-encoder.mlmodelc` + +- Build `whisper.cpp` with Core ML support: + + ```bash + # using CMake + cmake -B build -DWHISPER_COREML=1 + cmake --build build -j --config Release + ``` + +- Run the examples as usual. For example: + + ```text + $ ./build/bin/whisper-cli -m models/ggml-base.en.bin -f samples/jfk.wav + + ... + + whisper_init_state: loading Core ML model from 'models/ggml-base.en-encoder.mlmodelc' + whisper_init_state: first run on a device may take a while ... + whisper_init_state: Core ML model loaded + + system_info: n_threads = 4 / 10 | AVX = 0 | AVX2 = 0 | AVX512 = 0 | FMA = 0 | NEON = 1 | ARM_FMA = 1 | F16C = 0 | FP16_VA = 1 | WASM_SIMD = 0 | BLAS = 1 | SSE3 = 0 | VSX = 0 | COREML = 1 | + + ... + ``` + + The first run on a device is slow, since the ANE service compiles the Core ML model to some device-specific format. + Next runs are faster. + +For more information about the Core ML implementation please refer to PR [#566](https://github.com/ggml-org/whisper.cpp/pull/566). + +## OpenVINO support + +On platforms that support [OpenVINO](https://github.com/openvinotoolkit/openvino), the Encoder inference can be executed +on OpenVINO-supported devices including x86 CPUs and Intel GPUs (integrated & discrete). + +This can result in significant speedup in encoder performance. Here are the instructions for generating the OpenVINO model and using it with `whisper.cpp`: + +- First, setup python virtual env. and install python dependencies. Python 3.10 is recommended. + + Windows: + + ```powershell + cd models + python -m venv openvino_conv_env + openvino_conv_env\Scripts\activate + python -m pip install --upgrade pip + pip install -r requirements-openvino.txt + ``` + + Linux and macOS: + + ```bash + cd models + python3 -m venv openvino_conv_env + source openvino_conv_env/bin/activate + python -m pip install --upgrade pip + pip install -r requirements-openvino.txt + ``` + +- Generate an OpenVINO encoder model. For example, to generate a `base.en` model, use: + + ``` + python convert-whisper-to-openvino.py --model base.en + ``` + + This will produce ggml-base.en-encoder-openvino.xml/.bin IR model files. It's recommended to relocate these to the same folder as `ggml` models, as that + is the default location that the OpenVINO extension will search at runtime. + +- Build `whisper.cpp` with OpenVINO support: + + Download OpenVINO package from [release page](https://github.com/openvinotoolkit/openvino/releases). The recommended version to use is [2026.3.0](https://github.com/openvinotoolkit/openvino/releases/tag/2026.3.0). Ready to use Binaries of the required libraries can be found in the [OpenVino Archives](https://storage.openvinotoolkit.org/repositories/openvino/packages/2026.3/) + + After downloading & extracting package onto your development system, set up required environment by sourcing setupvars script. For example: + + Linux: + + ```bash + source /path/to/openvino_toolkit_ubuntu/setupvars.sh + ``` + + Windows (cmd): + + ```powershell + C:\Path\To\openvino_toolkit_windows\setupvars.bat + ``` + + And then build the project using cmake: + + ```bash + cmake -B build -DWHISPER_OPENVINO=1 + cmake --build build -j --config Release + ``` + +- Run the examples as usual. For example: + + ```text + $ ./build/bin/whisper-cli -m models/ggml-base.en.bin -f samples/jfk.wav + + ... + + whisper_ctx_init_openvino_encoder: loading OpenVINO model from 'models/ggml-base.en-encoder-openvino.xml' + whisper_ctx_init_openvino_encoder: first run on a device may take a while ... + whisper_openvino_init: path_model = models/ggml-base.en-encoder-openvino.xml, device = GPU, cache_dir = models/ggml-base.en-encoder-openvino-cache + whisper_ctx_init_openvino_encoder: OpenVINO model loaded + + system_info: n_threads = 4 / 8 | AVX = 1 | AVX2 = 1 | AVX512 = 0 | FMA = 1 | NEON = 0 | ARM_FMA = 0 | F16C = 1 | FP16_VA = 0 | WASM_SIMD = 0 | BLAS = 0 | SSE3 = 1 | VSX = 0 | COREML = 0 | OPENVINO = 1 | + + ... + ``` + + The first time run on an OpenVINO device is slow, since the OpenVINO framework will compile the IR (Intermediate Representation) model to a device-specific 'blob'. This device-specific blob will get + cached for the next run. + +For more information about the OpenVINO implementation please refer to PR [#1037](https://github.com/ggml-org/whisper.cpp/pull/1037). + +## AMD Ryzen™ AI NPU support + +On AMD Ryzen™ AI 300 and 400 Series processors with a dedicated NPU, whisper.cpp can fully offload the Whisper encoder to the NPU via VitisAI, delivering significant speedup over CPU-only inference. + +### Prerequisites + +Supported Platforms + +- **Windows 11** +- **Linux** (Ubuntu 24.04 LTS, Python 3.12) + +Install the XRT runtime and FlexML runtime for your platform: + +- **XRT**: provides the NPU kernel driver and `xrt-smi` diagnostic tool — on Windows this is bundled with the NPU driver; on Linux install it separately following the [NPU driver installation guide](https://ryzenai.docs.amd.com/en/latest/linux.html#install-npu-drivers) +- **FlexML runtime** (`flexmlrt`): VitisAI inference engine used by whisper.cpp — download from the [FlexML runtime releases](https://github.com/lemonade-sdk/whisper.cpp-rocm/releases/tag/deps) + +After installing, source the setup scripts in every shell you use to build or run whisper.cpp: + +```bash +# Linux +source /opt/xilinx/xrt/setup.sh +source /path/to/flexmlrt/setup.sh +``` + +```cmd +:: Windows +cd /path/to/flexmlrt && call setup.bat +``` + +You can verify the NPU is visible with: + +```bash +xrt-smi examine +``` + +### Download models + +Download the ggml model and the matching prebuilt VitisAI encoder cache: + +```bash +# Linux / macOS +sh ./models/download-ggml-model.sh base +sh ./models/download-vitisai-model.sh base +``` + +```cmd +:: Windows +.\models\download-ggml-model.cmd base +.\models\download-vitisai-model.cmd base +``` + +Use the same model name with both scripts. To see all available VitisAI encoder caches: + +```bash +sh ./models/download-vitisai-model.sh --list +``` + +```cmd +.\models\download-vitisai-model.cmd --list +``` + +The VitisAI script queries the [AMD Ryzen AI Whisper NPU collection on Hugging Face](https://huggingface.co/collections/amd/ryzen-ai-whisper-npu-optimized-onnx-models) and downloads the `.rai` encoder cache as `models/ggml-<model>-encoder-vitisai.rai`. + +> Depending on the `.rai` cache, VitisAI may offload the encoder only, or the encoder plus cross-projection layers. whisper.cpp detects this at runtime and logs the selected offload mode during model initialization. + +### Build + +```bash +cmake -B build -DWHISPER_VITISAI=1 +cmake --build build -j --config Release +``` + +### Run + +```bash +./build/bin/whisper-cli -m models/ggml-base.bin -f samples/jfk.wav +``` + +For more information see the [Ryzen AI documentation](https://ryzenai.docs.amd.com/en/latest/). + + +## NVIDIA GPU support + +With NVIDIA cards the processing of the models is done efficiently on the GPU via cuBLAS and custom CUDA kernels. +First, make sure you have installed `cuda`: https://developer.nvidia.com/cuda-downloads + +Now build `whisper.cpp` with CUDA support: + +``` +cmake -B build -DGGML_CUDA=1 +cmake --build build -j --config Release +``` + +or for newer NVIDIA GPU's (RTX 5000 series): +``` +cmake -B build -DGGML_CUDA=1 -DCMAKE_CUDA_ARCHITECTURES="86" +cmake --build build -j --config Release +``` + +## Vulkan GPU support +Cross-vendor solution which allows you to accelerate workload on your GPU. +First, make sure your graphics card driver provides support for Vulkan API. + +Now build `whisper.cpp` with Vulkan support: +``` +cmake -B build -DGGML_VULKAN=1 +cmake --build build -j --config Release +``` + +## AMD ROCm GPU support + +With AMD GPUs the processing can be accelerated via HIP/ROCm. +First, make sure you have installed [ROCm](https://rocm.docs.amd.com/en/latest/). + +Now build `whisper.cpp` with HIP support: + +``` +cmake -B build -DGGML_HIP=1 -DAMDGPU_TARGETS="gfx1201" +cmake --build build -j --config Release +``` + +Replace `gfx1201` with your GPU architecture. You can find it with: + +``` +rocminfo | grep "gfx" +``` + +Common architectures: `gfx1100` (RX 7900 XTX), `gfx1101` (RX 7800 XT), `gfx1201` (RX 9070 XT). +For multiple GPUs with different architectures: `-DAMDGPU_TARGETS="gfx1100;gfx1201"`. + +## BLAS CPU support via OpenBLAS + +Encoder processing can be accelerated on the CPU via OpenBLAS. +First, make sure you have installed `openblas`: https://www.openblas.net/ + +Now build `whisper.cpp` with OpenBLAS support: + +``` +cmake -B build -DGGML_BLAS=1 +cmake --build build -j --config Release +``` + +## Ascend NPU support + +Ascend NPU provides inference acceleration via [`CANN`](https://www.hiascend.com/en/software/cann) and AI cores. + +First, check if your Ascend NPU device is supported: + +**Verified devices** +| Ascend NPU | Status | +|:-----------------------------:|:-------:| +| Atlas 300T A2 | Support | +| Atlas 300I Duo | Support | + +Then, make sure you have installed [`CANN toolkit`](https://www.hiascend.com/en/software/cann/community) . The latest version of CANN is recommended. + +Now build `whisper.cpp` with CANN support: + +``` +cmake -B build -DGGML_CANN=1 +cmake --build build -j --config Release +``` + +Run the inference examples as usual, for example: + +``` +./build/bin/whisper-cli -f samples/jfk.wav -m models/ggml-base.en.bin -t 8 +``` + +*Notes:* + +- If you have trouble with Ascend NPU device, please create a issue with **[CANN]** prefix/tag. +- If you run successfully with your Ascend NPU device, please help update the table `Verified devices`. + +## Moore Threads GPU support + +With Moore Threads cards the processing of the models is done efficiently on the GPU via muBLAS and custom MUSA kernels. +First, make sure you have installed `MUSA SDK rc4.2.0`: https://developer.mthreads.com/sdk/download/musa?equipment=&os=&driverVersion=&version=4.2.0 + +Now build `whisper.cpp` with MUSA support: + +``` +cmake -B build -DGGML_MUSA=1 +cmake --build build -j --config Release +``` + +or specify the architecture for your Moore Threads GPU. For example, if you have a MTT S80 GPU, you can specify the architecture as follows: + +``` +cmake -B build -DGGML_MUSA=1 -DMUSA_ARCHITECTURES="21" +cmake --build build -j --config Release +``` + +## FFmpeg support (examples only) + +By default, the examples in this repo use the [miniaudio](https://github.com/mackron/miniaudio) library to decode audio files. +Some of the examples also can use FFmpeg for decoding and broader format support. To enable that, build with `WHISPER_COMMON_FFMPEG`. + +First, you need to install required libraries: + +```bash +# Debian/Ubuntu +sudo apt install libavcodec-dev libavformat-dev libavutil-dev + +# RHEL/Fedora +sudo dnf install libavcodec-free-devel libavformat-free-devel libavutil-free-devel +``` + +Then you can build the project as follows: + +```bash +cmake -B build -D WHISPER_COMMON_FFMPEG=yes +cmake --build build +``` + +Run the following example to confirm it's working: + +```bash +# Convert an audio file to Opus format +ffmpeg -i samples/jfk.wav jfk.opus + +# Transcribe the audio file +./build/bin/whisper-cli --model models/ggml-base.en.bin --file jfk.opus +``` + +## Docker + +### Prerequisites + +- Docker must be installed and running on your system. +- Create a folder to store big models & intermediate files (ex. /whisper/models) + +### Images + +We have multiple Docker images available for this project: + +1. `ghcr.io/ggml-org/whisper.cpp:main`: This image includes the main executable file as well as `curl` and `ffmpeg`. (platforms: `linux/amd64`, `linux/arm64`) +2. `ghcr.io/ggml-org/whisper.cpp:main-cuda`: Same as `main` but compiled with CUDA support. (platforms: `linux/amd64`) +3. `ghcr.io/ggml-org/whisper.cpp:main-musa`: Same as `main` but compiled with MUSA support. (platforms: `linux/amd64`) +4. `ghcr.io/ggml-org/whisper.cpp:main-vulkan`: Same as `main` but compiled with Vulkan support. (platforms: `linux/amd64`) + +### Usage + +```shell +# download model and persist it in a local folder +docker run -it --rm \ + -v path/to/models:/models \ + whisper.cpp:main "./models/download-ggml-model.sh base /models" + +# transcribe an audio file +docker run -it --rm \ + -v path/to/models:/models \ + -v path/to/audios:/audios \ + whisper.cpp:main "whisper-cli -m /models/ggml-base.bin -f /audios/jfk.wav" + +# transcribe an audio file in samples folder +docker run -it --rm \ + -v path/to/models:/models \ + whisper.cpp:main "whisper-cli -m /models/ggml-base.bin -f ./samples/jfk.wav" + +# run the web server +docker run -it --rm -p "8080:8080" \ + -v path/to/models:/models \ + whisper.cpp:main "whisper-server --host 127.0.0.1 -m /models/ggml-base.bin" + +# run the bench too on the small.en model using 4 threads +docker run -it --rm \ + -v path/to/models:/models \ + whisper.cpp:main "whisper-bench -m /models/ggml-small.en.bin -t 4" +``` + +## Installing with Conan + +You can install pre-built binaries for whisper.cpp or build it from source using [Conan](https://conan.io/). Use the following command: + +``` +conan install --requires="whisper-cpp/[*]" --build=missing +``` + +For detailed instructions on how to use Conan, please refer to the [Conan documentation](https://docs.conan.io/2/). + +## Limitations + +- Inference only + +## Real-time audio input example + +This is a naive example of performing real-time inference on audio from your microphone. +The [stream](examples/stream) tool samples the audio every half a second and runs the transcription continuously. +More info is available in [issue #10](https://github.com/ggml-org/whisper.cpp/issues/10). +You will need to have [sdl2](https://wiki.libsdl.org/SDL2/Installation) installed for it to work properly. + +```bash +cmake -B build -DWHISPER_SDL2=ON +cmake --build build -j --config Release +./build/bin/whisper-stream -m ./models/ggml-base.en.bin -t 8 --step 500 --length 5000 +``` + +https://user-images.githubusercontent.com/1991296/194935793-76afede7-cfa8-48d8-a80f-28ba83be7d09.mp4 + +## Confidence color-coding + +Adding the `--print-colors` argument will print the transcribed text using an experimental color coding strategy +to highlight words with high or low confidence: + +```bash +./build/bin/whisper-cli -m models/ggml-base.en.bin -f samples/gb0.wav --print-colors +``` + +<img width="965" alt="image" src="https://user-images.githubusercontent.com/1991296/197356445-311c8643-9397-4e5e-b46e-0b4b4daa2530.png"> + +## Controlling the length of the generated text segments (experimental) + +For example, to limit the line length to a maximum of 16 characters, simply add `-ml 16`: + +```text +$ ./build/bin/whisper-cli -m ./models/ggml-base.en.bin -f ./samples/jfk.wav -ml 16 + +whisper_model_load: loading model from './models/ggml-base.en.bin' +... +system_info: n_threads = 4 / 10 | AVX2 = 0 | AVX512 = 0 | NEON = 1 | FP16_VA = 1 | WASM_SIMD = 0 | BLAS = 1 | + +main: processing './samples/jfk.wav' (176000 samples, 11.0 sec), 4 threads, 1 processors, lang = en, task = transcribe, timestamps = 1 ... + +[00:00:00.000 --> 00:00:00.850] And so my +[00:00:00.850 --> 00:00:01.590] fellow +[00:00:01.590 --> 00:00:04.140] Americans, ask +[00:00:04.140 --> 00:00:05.660] not what your +[00:00:05.660 --> 00:00:06.840] country can do +[00:00:06.840 --> 00:00:08.430] for you, ask +[00:00:08.430 --> 00:00:09.440] what you can do +[00:00:09.440 --> 00:00:10.020] for your +[00:00:10.020 --> 00:00:11.000] country. +``` + +## Word-level timestamp (experimental) + +The `--max-len` argument can be used to obtain word-level timestamps. Simply use `-ml 1`: + +```text +$ ./build/bin/whisper-cli -m ./models/ggml-base.en.bin -f ./samples/jfk.wav -ml 1 + +whisper_model_load: loading model from './models/ggml-base.en.bin' +... +system_info: n_threads = 4 / 10 | AVX2 = 0 | AVX512 = 0 | NEON = 1 | FP16_VA = 1 | WASM_SIMD = 0 | BLAS = 1 | + +main: processing './samples/jfk.wav' (176000 samples, 11.0 sec), 4 threads, 1 processors, lang = en, task = transcribe, timestamps = 1 ... + +[00:00:00.000 --> 00:00:00.320] +[00:00:00.320 --> 00:00:00.370] And +[00:00:00.370 --> 00:00:00.690] so +[00:00:00.690 --> 00:00:00.850] my +[00:00:00.850 --> 00:00:01.590] fellow +[00:00:01.590 --> 00:00:02.850] Americans +[00:00:02.850 --> 00:00:03.300] , +[00:00:03.300 --> 00:00:04.140] ask +[00:00:04.140 --> 00:00:04.990] not +[00:00:04.990 --> 00:00:05.410] what +[00:00:05.410 --> 00:00:05.660] your +[00:00:05.660 --> 00:00:06.260] country +[00:00:06.260 --> 00:00:06.600] can +[00:00:06.600 --> 00:00:06.840] do +[00:00:06.840 --> 00:00:07.010] for +[00:00:07.010 --> 00:00:08.170] you +[00:00:08.170 --> 00:00:08.190] , +[00:00:08.190 --> 00:00:08.430] ask +[00:00:08.430 --> 00:00:08.910] what +[00:00:08.910 --> 00:00:09.040] you +[00:00:09.040 --> 00:00:09.320] can +[00:00:09.320 --> 00:00:09.440] do +[00:00:09.440 --> 00:00:09.760] for +[00:00:09.760 --> 00:00:10.020] your +[00:00:10.020 --> 00:00:10.510] country +[00:00:10.510 --> 00:00:11.000] . +``` + +## Speaker segmentation via tinydiarize (experimental) + +More information about this approach is available here: https://github.com/ggml-org/whisper.cpp/pull/1058 + +Sample usage: + +```py +# download a tinydiarize compatible model +./models/download-ggml-model.sh small.en-tdrz + +# run as usual, adding the "-tdrz" command-line argument +./build/bin/whisper-cli -f ./samples/a13.wav -m ./models/ggml-small.en-tdrz.bin -tdrz +... +main: processing './samples/a13.wav' (480000 samples, 30.0 sec), 4 threads, 1 processors, lang = en, task = transcribe, tdrz = 1, timestamps = 1 ... +... +[00:00:00.000 --> 00:00:03.800] Okay Houston, we've had a problem here. [SPEAKER_TURN] +[00:00:03.800 --> 00:00:06.200] This is Houston. Say again please. [SPEAKER_TURN] +[00:00:06.200 --> 00:00:08.260] Uh Houston we've had a problem. +[00:00:08.260 --> 00:00:11.320] We've had a main beam up on a volt. [SPEAKER_TURN] +[00:00:11.320 --> 00:00:13.820] Roger main beam interval. [SPEAKER_TURN] +[00:00:13.820 --> 00:00:15.100] Uh uh [SPEAKER_TURN] +[00:00:15.100 --> 00:00:18.020] So okay stand, by thirteen we're looking at it. [SPEAKER_TURN] +[00:00:18.020 --> 00:00:25.740] Okay uh right now uh Houston the uh voltage is uh is looking good um. +[00:00:27.620 --> 00:00:29.940] And we had a a pretty large bank or so. +``` + +## Karaoke-style movie generation (experimental) + +The [whisper-cli](examples/cli) example provides support for output of karaoke-style movies, where the +currently pronounced word is highlighted. Use the `-owts` argument and run the generated bash script. +This requires to have `ffmpeg` installed. + +Here are a few _"typical"_ examples: + +```bash +./build/bin/whisper-cli -m ./models/ggml-base.en.bin -f ./samples/jfk.wav -owts +source ./samples/jfk.wav.wts +ffplay ./samples/jfk.wav.mp4 +``` + +https://user-images.githubusercontent.com/1991296/199337465-dbee4b5e-9aeb-48a3-b1c6-323ac4db5b2c.mp4 + +--- + +```bash +./build/bin/whisper-cli -m ./models/ggml-base.en.bin -f ./samples/mm0.wav -owts +source ./samples/mm0.wav.wts +ffplay ./samples/mm0.wav.mp4 +``` + +https://user-images.githubusercontent.com/1991296/199337504-cc8fd233-0cb7-4920-95f9-4227de3570aa.mp4 + +--- + +```bash +./build/bin/whisper-cli -m ./models/ggml-base.en.bin -f ./samples/gb0.wav -owts +source ./samples/gb0.wav.wts +ffplay ./samples/gb0.wav.mp4 +``` + +https://user-images.githubusercontent.com/1991296/199337538-b7b0c7a3-2753-4a88-a0cd-f28a317987ba.mp4 + +--- + +## Video comparison of different models + +Use the [scripts/bench-wts.sh](https://github.com/ggml-org/whisper.cpp/blob/master/scripts/bench-wts.sh) script to generate a video in the following format: + +```bash +./scripts/bench-wts.sh samples/jfk.wav +ffplay ./samples/jfk.wav.all.mp4 +``` + +https://user-images.githubusercontent.com/1991296/223206245-2d36d903-cf8e-4f09-8c3b-eb9f9c39d6fc.mp4 + +--- + +## Benchmarks + +In order to have an objective comparison of the performance of the inference across different system configurations, +use the [whisper-bench](examples/bench) tool. The tool simply runs the Encoder part of the model and prints how much time it +took to execute it. The results are summarized in the following Github issue: + +[Benchmark results](https://github.com/ggml-org/whisper.cpp/issues/89) + +Additionally a script to run whisper.cpp with different models and audio files is provided [bench.py](scripts/bench.py). + +You can run it with the following command, by default it will run against any standard model in the models folder. + +```bash +python3 scripts/bench.py -f samples/jfk.wav -t 2,4,8 -p 1,2 +``` + +It is written in python with the intention of being easy to modify and extend for your benchmarking use case. + +It outputs a csv file with the results of the benchmarking. + +## `ggml` format + +The original models are converted to a custom binary format. This allows to pack everything needed into a single file: + +- model parameters +- mel filters +- vocabulary +- weights + +You can download the converted models using the [models/download-ggml-model.sh](models/download-ggml-model.sh) script +or manually from here: + +- https://huggingface.co/ggerganov/whisper.cpp + +For more details, see the conversion script [models/convert-pt-to-ggml.py](models/convert-pt-to-ggml.py) or [models/README.md](models/README.md). + +## [Bindings](https://github.com/ggml-org/whisper.cpp/discussions/categories/bindings) + +- [x] Rust: [tazz4843/whisper-rs](https://github.com/tazz4843/whisper-rs) | [#310](https://github.com/ggml-org/whisper.cpp/discussions/310) +- [x] JavaScript: [bindings/javascript](bindings/javascript) | [#309](https://github.com/ggml-org/whisper.cpp/discussions/309) + - React Native (iOS / Android): [whisper.rn](https://github.com/mybigday/whisper.rn) +- [x] Go: [bindings/go](bindings/go) | [#312](https://github.com/ggml-org/whisper.cpp/discussions/312) +- [x] Java: + - [GiviMAD/whisper-jni](https://github.com/GiviMAD/whisper-jni) +- [x] Ruby: [bindings/ruby](bindings/ruby) | [#507](https://github.com/ggml-org/whisper.cpp/discussions/507) +- [x] Objective-C / Swift: [ggml-org/whisper.spm](https://github.com/ggml-org/whisper.spm) | [#313](https://github.com/ggml-org/whisper.cpp/discussions/313) + - [exPHAT/SwiftWhisper](https://github.com/exPHAT/SwiftWhisper) +- [x] .NET: | [#422](https://github.com/ggml-org/whisper.cpp/discussions/422) + - [sandrohanea/whisper.net](https://github.com/sandrohanea/whisper.net) + - [NickDarvey/whisper](https://github.com/NickDarvey/whisper) +- [x] Python: | [#9](https://github.com/ggml-org/whisper.cpp/issues/9) + - [stlukey/whispercpp.py](https://github.com/stlukey/whispercpp.py) (Cython) + - [AIWintermuteAI/whispercpp](https://github.com/AIWintermuteAI/whispercpp) (Updated fork of aarnphm/whispercpp) + - [aarnphm/whispercpp](https://github.com/aarnphm/whispercpp) (Pybind11) + - [abdeladim-s/pywhispercpp](https://github.com/abdeladim-s/pywhispercpp) (Pybind11) +- [x] R: [bnosac/audio.whisper](https://github.com/bnosac/audio.whisper) +- [x] Unity: [macoron/whisper.unity](https://github.com/Macoron/whisper.unity) + +## XCFramework +The XCFramework is a precompiled version of the library for iOS, visionOS, tvOS, +and macOS. It can be used in Swift projects without the need to compile the +library from source. For example, the v1.7.5 version of the XCFramework can be +used as follows: + +```swift +// swift-tools-version: 5.10 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "Whisper", + targets: [ + .executableTarget( + name: "Whisper", + dependencies: [ + "WhisperFramework" + ]), + .binaryTarget( + name: "WhisperFramework", + url: "https://github.com/ggml-org/whisper.cpp/releases/download/v1.7.5/whisper-v1.7.5-xcframework.zip", + checksum: "c7faeb328620d6012e130f3d705c51a6ea6c995605f2df50f6e1ad68c59c6c4a" + ) + ] +) +``` + +## Voice Activity Detection (VAD) +Support for Voice Activity Detection (VAD) can be enabled using the `--vad` +argument to `whisper-cli`. In addition to this option a VAD model is also +required. + +The way this works is that first the audio samples are passed through +the VAD model which will detect speech segments. Using this information, +only the speech segments that are detected are extracted from the original audio +input and passed to whisper for processing. This reduces the amount of audio +data that needs to be processed by whisper and can significantly speed up the +transcription process. + +The following VAD models are currently supported: + +### Silero-VAD +[Silero-vad](https://github.com/snakers4/silero-vad) is a lightweight VAD model +written in Python that is fast and accurate. + +Models can be downloaded by running the following command on Linux or MacOS: +```console +$ ./models/download-vad-model.sh silero-v6.2.0 +Downloading ggml model silero-v6.2.0 from 'https://huggingface.co/ggml-org/whisper-vad' ... +ggml-silero-v6.2.0.bin 100%[==============================================>] 864.35K --.-KB/s in 0.04s +Done! Model 'silero-v6.2.0' saved in '/path/models/ggml-silero-v6.2.0.bin' +You can now use it like this: + + $ ./build/bin/whisper-cli -vm /path/models/ggml-silero-v6.2.0.bin --vad -f samples/jfk.wav -m models/ggml-base.en.bin + +``` +And the following command on Windows: +```console +> .\models\download-vad-model.cmd silero-v6.2.0 +Downloading vad model silero-v6.2.0... +Done! Model silero-v6.2.0 saved in C:\Users\danie\work\ai\whisper.cpp\ggml-silero-v6.2.0.bin +You can now use it like this: + +C:\path\build\bin\Release\whisper-cli.exe -vm C:\path\ggml-silero-v6.2.0.bin --vad -m models/ggml-base.en.bin -f samples\jfk.wav + +``` + +To see a list of all available models, run the above commands without any +arguments. + +This model can be also be converted manually to ggml using the following command: +```console +$ python3 -m venv venv && source venv/bin/activate +$ (venv) pip install silero-vad +$ (venv) $ python models/convert-silero-vad-to-ggml.py --output models/silero.bin +Saving GGML Silero-VAD model to models/silero-v6.2.0-ggml.bin +``` +And it can then be used with whisper as follows: +```console +$ ./build/bin/whisper-cli \ + --file ./samples/jfk.wav \ + --model ./models/ggml-base.en.bin \ + --vad \ + --vad-model ./models/silero-v6.2.0-ggml.bin +``` + +### VAD Options + +* --vad-threshold: Threshold probability for speech detection. A probability +for a speech segment/frame above this threshold will be considered as speech. + +* --vad-min-speech-duration-ms: Minimum speech duration in milliseconds. Speech +segments shorter than this value will be discarded to filter out brief noise or +false positives. + +* --vad-min-silence-duration-ms: Minimum silence duration in milliseconds. Silence +periods must be at least this long to end a speech segment. Shorter silence +periods will be ignored and included as part of the speech. + +* --vad-max-speech-duration-s: Maximum speech duration in seconds. Speech segments +longer than this will be automatically split into multiple segments at silence +points exceeding 98ms to prevent excessively long segments. + +* --vad-speech-pad-ms: Speech padding in milliseconds. Adds this amount of padding +before and after each detected speech segment to avoid cutting off speech edges. + +* --vad-samples-overlap: Amount of audio to extend from each speech segment into +the next one, in seconds (e.g., 0.10 = 100ms overlap). This ensures speech isn't +cut off abruptly between segments when they're concatenated together. + +## Examples + +There are various examples of using the library for different projects in the [examples](examples) folder. +Some of the examples are even ported to run in the browser using WebAssembly. Check them out! + +| Example | Web | Description | +| --------------------------------------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | +| [whisper-cli](examples/cli) | [whisper.wasm](examples/whisper.wasm) | Tool for translating and transcribing audio using Whisper | +| [whisper-bench](examples/bench) | [bench.wasm](examples/bench.wasm) | Benchmark the performance of Whisper on your machine | +| [whisper-stream](examples/stream) | [stream.wasm](examples/stream.wasm) | Real-time transcription of raw microphone capture | +| [whisper-command](examples/command) | [command.wasm](examples/command.wasm) | Basic voice assistant example for receiving voice commands from the mic | +| [whisper-server](examples/server) | | HTTP transcription server with OAI-like API | +| [whisper-talk-llama](examples/talk-llama) | | Talk with a LLaMA bot | +| [whisper.objc](examples/whisper.objc) | | iOS mobile application using whisper.cpp | +| [whisper.swiftui](examples/whisper.swiftui) | | SwiftUI iOS / macOS application using whisper.cpp | +| [whisper.android](examples/whisper.android) | | Android mobile application using whisper.cpp | +| [whisper.nvim](examples/whisper.nvim) | | Speech-to-text plugin for Neovim | +| [generate-karaoke.sh](examples/generate-karaoke.sh) | | Helper script to easily [generate a karaoke video](https://youtu.be/uj7hVta4blM) of raw audio capture | +| [livestream.sh](examples/livestream.sh) | | [Livestream audio transcription](https://github.com/ggml-org/whisper.cpp/issues/185) | +| [yt-wsp.sh](examples/yt-wsp.sh) | | Download + transcribe and/or translate any VOD [(original)](https://gist.github.com/DaniruKun/96f763ec1a037cc92fe1a059b643b818) | +| [wchess](examples/wchess) | [wchess.wasm](examples/wchess) | Voice-controlled chess | + +## [Discussions](https://github.com/ggml-org/whisper.cpp/discussions) + +If you have any kind of feedback about this project feel free to use the Discussions section and open a new topic. +You can use the [Show and tell](https://github.com/ggml-org/whisper.cpp/discussions/categories/show-and-tell) category +to share your own projects that use `whisper.cpp`. If you have a question, make sure to check the +[Frequently asked questions (#126)](https://github.com/ggml-org/whisper.cpp/discussions/126) discussion. diff --git a/README_sycl.md b/README_sycl.md new file mode 100644 index 0000000..2d31d28 --- /dev/null +++ b/README_sycl.md @@ -0,0 +1,249 @@ +# whisper.cpp for SYCL + +[Background](#background) + +[OS](#os) + +[Intel GPU](#intel-gpu) + +[Linux](#linux) + +[Environment Variable](#environment-variable) + +[Known Issue](#known-issue) + +[Todo](#todo) + +## Background + +SYCL is a higher-level programming model to improve programming productivity on various hardware accelerators—such as CPUs, GPUs, and FPGAs. It is a single-source embedded domain-specific language based on pure C++17. + +oneAPI is a specification that is open and standards-based, supporting multiple architecture types including but not limited to GPU, CPU, and FPGA. The spec has both direct programming and API-based programming paradigms. + +Intel uses the SYCL as direct programming language to support CPU, GPUs and FPGAs. + +To avoid re-inventing the wheel, this code refers other code paths in llama.cpp (like OpenBLAS, cuBLAS, CLBlast). We use a open-source tool [SYCLomatic](https://github.com/oneapi-src/SYCLomatic) (Commercial release [Intel® DPC++ Compatibility Tool](https://www.intel.com/content/www/us/en/developer/tools/oneapi/dpc-compatibility-tool.html)) migrate to SYCL. + +The whisper.cpp for SYCL is used to support Intel GPUs. + +For Intel CPU, recommend to use whisper.cpp for X86 (Intel MKL build). + +## OS + +|OS|Status|Verified| +|-|-|-| +|Linux|Support|Ubuntu 22.04| +|Windows|Ongoing| | + + +## Intel GPU + +|Intel GPU| Status | Verified Model| +|-|-|-| +|Intel Data Center Max Series| Support| Max 1550| +|Intel Data Center Flex Series| Support| Flex 170| +|Intel Arc Series| Support| Arc 770| +|Intel built-in Arc GPU| Support| built-in Arc GPU in Meteor Lake| +|Intel iGPU| Support| iGPU in i5-1250P, i7-1165G7| + + +## Linux + +### Setup Environment + +1. Install Intel GPU driver. + +a. Please install Intel GPU driver by official guide: [Install GPU Drivers](https://dgpu-docs.intel.com/driver/installation.html). + +Note: for iGPU, please install the client GPU driver. + +b. Add user to group: video, render. + +``` +sudo usermod -aG render username +sudo usermod -aG video username +``` + +Note: re-login to enable it. + +c. Check + +``` +sudo apt install clinfo +sudo clinfo -l +``` + +Output (example): + +``` +Platform #0: Intel(R) OpenCL Graphics + `-- Device #0: Intel(R) Arc(TM) A770 Graphics + + +Platform #0: Intel(R) OpenCL HD Graphics + `-- Device #0: Intel(R) Iris(R) Xe Graphics [0x9a49] +``` + +2. Install Intel® oneAPI Base toolkit. + + +a. Please follow the procedure in [Get the Intel® oneAPI Base Toolkit ](https://www.intel.com/content/www/us/en/developer/tools/oneapi/base-toolkit.html). + +Recommend to install to default folder: **/opt/intel/oneapi**. + +Following guide use the default folder as example. If you use other folder, please modify the following guide info with your folder. + +b. Check + +``` +source /opt/intel/oneapi/setvars.sh + +sycl-ls +``` + +There should be one or more level-zero devices. Like **[ext_oneapi_level_zero:gpu:0]**. + +Output (example): +``` +[opencl:acc:0] Intel(R) FPGA Emulation Platform for OpenCL(TM), Intel(R) FPGA Emulation Device OpenCL 1.2 [2023.16.10.0.17_160000] +[opencl:cpu:1] Intel(R) OpenCL, 13th Gen Intel(R) Core(TM) i7-13700K OpenCL 3.0 (Build 0) [2023.16.10.0.17_160000] +[opencl:gpu:2] Intel(R) OpenCL Graphics, Intel(R) Arc(TM) A770 Graphics OpenCL 3.0 NEO [23.30.26918.50] +[ext_oneapi_level_zero:gpu:0] Intel(R) Level-Zero, Intel(R) Arc(TM) A770 Graphics 1.3 [1.3.26918] + +``` + +2. Build locally: + +``` +mkdir -p build +cd build +source /opt/intel/oneapi/setvars.sh + +#for FP16 +#cmake .. -DWHISPER_SYCL=ON -DCMAKE_C_COMPILER=icx -DCMAKE_CXX_COMPILER=icpx -DWHISPER_SYCL_F16=ON + +#for FP32 +cmake .. -DWHISPER_SYCL=ON -DCMAKE_C_COMPILER=icx -DCMAKE_CXX_COMPILER=icpx + +#build example/main only +#cmake --build . --config Release --target main + +#build all binary +cmake --build . --config Release -v + +``` + +or + +``` +./examples/sycl/build.sh +``` + +Note: + +- By default, it will build for all binary files. It will take more time. To reduce the time, we recommend to build for **example/main** only. + +### Run + +1. Put model file to folder **models** + +2. Enable oneAPI running environment + +``` +source /opt/intel/oneapi/setvars.sh +``` + +3. List device ID + +Run without parameter: + +``` +./build/bin/ls-sycl-device + +or + +./build/bin/main +``` + +Check the ID in startup log, like: + +``` +found 4 SYCL devices: + Device 0: Intel(R) Arc(TM) A770 Graphics, compute capability 1.3, + max compute_units 512, max work group size 1024, max sub group size 32, global mem size 16225243136 + Device 1: Intel(R) FPGA Emulation Device, compute capability 1.2, + max compute_units 24, max work group size 67108864, max sub group size 64, global mem size 67065057280 + Device 2: 13th Gen Intel(R) Core(TM) i7-13700K, compute capability 3.0, + max compute_units 24, max work group size 8192, max sub group size 64, global mem size 67065057280 + Device 3: Intel(R) Arc(TM) A770 Graphics, compute capability 3.0, + max compute_units 512, max work group size 1024, max sub group size 32, global mem size 16225243136 + +``` + +|Attribute|Note| +|-|-| +|compute capability 1.3|Level-zero running time, recommended | +|compute capability 3.0|OpenCL running time, slower than level-zero in most cases| + +4. Set device ID and execute whisper.cpp + +Set device ID = 0 by **GGML_SYCL_DEVICE=0** + +``` +GGML_SYCL_DEVICE=0 ./build/bin/main -m models/ggml-base.en.bin -f samples/jfk.wav +``` +or run by script: + +``` +./examples/sycl/run_whisper.sh +``` + + + +5. Check the device ID in output + +Like: +``` +Using device **0** (Intel(R) Arc(TM) A770 Graphics) as main device +``` + + +## Environment Variable + +#### Build + +|Name|Value|Function| +|-|-|-| +|WHISPER_SYCL|ON (mandatory)|Enable build with SYCL code path. <br>For FP32/FP16, WHISPER_SYCL=ON is mandatory.| +|WHISPER_SYCL_F16|ON (optional)|Enable FP16 build with SYCL code path.For FP32, do not set it.| +|CMAKE_C_COMPILER|icx|Use icx compiler for SYCL code path| +|CMAKE_CXX_COMPILER|icpx|use icpx for SYCL code path| + +#### Running + + +|Name|Value|Function| +|-|-|-| +|GGML_SYCL_DEVICE|0 (default) or 1|Set the device id used. Check the device ids by default running output| +|GGML_SYCL_DEBUG|0 (default) or 1|Enable log function by macro: GGML_SYCL_DEBUG| + +## Known Issue + +- Error: `error while loading shared libraries: libsycl.so.7: cannot open shared object file: No such file or directory`. + + Miss to enable oneAPI running environment. + + Install oneAPI base toolkit and enable it by: `source /opt/intel/oneapi/setvars.sh`. + + +- Hang during startup + + llama.cpp use mmap as default way to read model file and copy to GPU. In some system, memcpy will be abnormal and block. + + Solution: add **--no-mmap**. + +## Todo + +- Support to build in Windows. + +- Support multiple cards. diff --git a/bindings/CMakeLists.txt b/bindings/CMakeLists.txt new file mode 100644 index 0000000..af79c51 --- /dev/null +++ b/bindings/CMakeLists.txt @@ -0,0 +1,19 @@ +if (EMSCRIPTEN) + add_subdirectory(javascript) + + add_custom_command( + OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/javascript/publish.log + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/javascript/whisper.js + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/javascript/libwhisper.worker.js + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/javascript/package.json + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/javascript + COMMAND npm publish + COMMAND touch publish.log + COMMENT "Publishing npm module v${PROJECT_VERSION}" + VERBATIM + ) + + add_custom_target(publish-npm + DEPENDS javascript/publish.log + ) +endif() diff --git a/bindings/go/.gitignore b/bindings/go/.gitignore new file mode 100644 index 0000000..036df1d --- /dev/null +++ b/bindings/go/.gitignore @@ -0,0 +1,2 @@ +build +models diff --git a/bindings/go/LICENSE b/bindings/go/LICENSE new file mode 100644 index 0000000..a8f0d7b --- /dev/null +++ b/bindings/go/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 David Thorpe + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/bindings/go/Makefile b/bindings/go/Makefile new file mode 100644 index 0000000..e4436a6 --- /dev/null +++ b/bindings/go/Makefile @@ -0,0 +1,72 @@ +ifndef UNAME_S +UNAME_S := $(shell uname -s) +endif + +ifndef UNAME_P +UNAME_P := $(shell uname -p) +endif + +ifndef UNAME_M +UNAME_M := $(shell uname -m) +endif + +GGML_METAL_PATH_RESOURCES := $(abspath ../..) +BUILD_DIR := build_go +MODELS_DIR := models +EXAMPLES_DIR := $(wildcard examples/*) +INCLUDE_PATH := $(abspath ../../include):$(abspath ../../ggml/include) +LIBRARY_PATH := $(abspath ../../${BUILD_DIR}/src):$(abspath ../../${BUILD_DIR}/ggml/src) + +ifeq ($(GGML_CUDA),1) + LIBRARY_PATH := $(LIBRARY_PATH):$(CUDA_PATH)/targets/$(UNAME_M)-linux/lib/ + BUILD_FLAGS := -ldflags "-extldflags '-lcudart -lcuda -lcublas'" +endif + +ifeq ($(UNAME_S),Darwin) + LIBRARY_PATH := $(LIBRARY_PATH):$(abspath ../../${BUILD_DIR}/ggml/src/ggml-blas):$(abspath ../../${BUILD_DIR}/ggml/src/ggml-metal) + EXT_LDFLAGS := -framework Foundation -framework Metal -framework MetalKit -lggml-metal -lggml-blas +endif + +all: clean whisper examples + +whisper: mkdir + cmake -S ../.. -B ../../${BUILD_DIR} \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_SHARED_LIBS=OFF + cmake --build ../../${BUILD_DIR} --target whisper + +test: model-small whisper modtidy +ifeq ($(UNAME_S),Darwin) + @C_INCLUDE_PATH=${INCLUDE_PATH} LIBRARY_PATH=${LIBRARY_PATH} GGML_METAL_PATH_RESOURCES=${GGML_METAL_PATH_RESOURCES} go test -ldflags "-extldflags '$(EXT_LDFLAGS)'" -v . + @C_INCLUDE_PATH=${INCLUDE_PATH} LIBRARY_PATH=${LIBRARY_PATH} GGML_METAL_PATH_RESOURCES=${GGML_METAL_PATH_RESOURCES} go test -ldflags "-extldflags '$(EXT_LDFLAGS)'" -v ./pkg/whisper/... +else + @C_INCLUDE_PATH=${INCLUDE_PATH} LIBRARY_PATH=${LIBRARY_PATH} go test -v . + @C_INCLUDE_PATH=${INCLUDE_PATH} LIBRARY_PATH=${LIBRARY_PATH} go test -v ./pkg/whisper/... +endif + +examples: $(EXAMPLES_DIR) + +model-small: mkdir examples/go-model-download + @${BUILD_DIR}/go-model-download -out models ggml-small.en.bin + +$(EXAMPLES_DIR): mkdir whisper modtidy + @echo Build example $(notdir $@) +ifeq ($(UNAME_S),Darwin) + @C_INCLUDE_PATH=${INCLUDE_PATH} LIBRARY_PATH=${LIBRARY_PATH} GGML_METAL_PATH_RESOURCES=${GGML_METAL_PATH_RESOURCES} go build ${BUILD_FLAGS} -ldflags "-extldflags '$(EXT_LDFLAGS)'" -o ${BUILD_DIR}/$(notdir $@) ./$@ +else + @C_INCLUDE_PATH=${INCLUDE_PATH} LIBRARY_PATH=${LIBRARY_PATH} go build ${BUILD_FLAGS} -o ${BUILD_DIR}/$(notdir $@) ./$@ +endif + +mkdir: + @echo Mkdir ${BUILD_DIR} + @install -d ${BUILD_DIR} + @echo Mkdir ${MODELS_DIR} + @install -d ${MODELS_DIR} + +modtidy: + @go mod tidy + +clean: + @echo Clean + @rm -fr $(BUILD_DIR) + @go clean diff --git a/bindings/go/README.md b/bindings/go/README.md new file mode 100644 index 0000000..9d83209 --- /dev/null +++ b/bindings/go/README.md @@ -0,0 +1,106 @@ +# Go bindings for Whisper + +This package provides Go bindings for whisper.cpp. They have been tested on: + + * Darwin (OS X) 12.6 on x64_64 + * Debian Linux on arm64 + * Fedora Linux on x86_64 + +The "low level" bindings are in the `bindings/go` directory and there is a more +Go-style package in the `bindings/go/pkg/whisper` directory. The most simple usage +is as follows: + +```go +import ( + "github.com/ggerganov/whisper.cpp/bindings/go/pkg/whisper" +) + +func main() { + var modelpath string // Path to the model + var samples []float32 // Samples to process + + // Load the model + model, err := whisper.New(modelpath) + if err != nil { + panic(err) + } + defer model.Close() + + // Process samples + context, err := model.NewContext() + if err != nil { + panic(err) + } + if err := context.Process(samples, nil, nil, nil); err != nil { + return err + } + + // Print out the results + for { + segment, err := context.NextSegment() + if err != nil { + break + } + fmt.Printf("[%6s->%6s] %s\n", segment.Start, segment.End, segment.Text) + } +} +``` + +## Building & Testing + +In order to build, you need to have the Go compiler installed. You can get it from [here](https://golang.org/dl/). Run the tests with: + +```bash +git clone https://github.com/ggml-org/whisper.cpp.git +cd whisper.cpp/bindings/go +make test +``` + +This will compile a static `libwhisper.a` in a `build` folder, download a model file, then run the tests. To build the examples: + +```bash +make examples +``` + +To build using cuda support add `GGML_CUDA=1`: + +```bash +GGML_CUDA=1 make examples +``` + +The examples are placed in the `build` directory. Once built, you can download all the models with the following command: + +```bash +./build/go-model-download -out models +``` + +And you can then test a model against samples with the following command: + +```bash +./build/go-whisper -model models/ggml-tiny.en.bin samples/jfk.wav +``` + +## Using the bindings + +To use the bindings in your own software, + + 1. Import `github.com/ggerganov/whisper.cpp/bindings/go/pkg/whisper` (or `github.com/ggerganov/whisper.cpp/bindings/go` into your package; + 2. Compile `libwhisper.a` (you can use `make whisper` in the `bindings/go` directory); + 3. Link your go binary against whisper by setting the environment variables `C_INCLUDE_PATH` and `LIBRARY_PATH` + to point to the `whisper.h` file directory and `libwhisper.a` file directory respectively. + +Look at the `Makefile` in the `bindings/go` directory for an example. + +The API Documentation: + + * https://pkg.go.dev/github.com/ggerganov/whisper.cpp/bindings/go + * https://pkg.go.dev/github.com/ggerganov/whisper.cpp/bindings/go/pkg/whisper + +Getting help: + + * Follow the discussion for the go bindings [here](https://github.com/ggml-org/whisper.cpp/discussions/312) + +## License + +The license for the Go bindings is the same as the license for the rest of the whisper.cpp project, which is the MIT License. See the `LICENSE` file for more details. + diff --git a/bindings/go/doc.go b/bindings/go/doc.go new file mode 100644 index 0000000..a5dae93 --- /dev/null +++ b/bindings/go/doc.go @@ -0,0 +1,5 @@ +/* +github.com/ggml-org/whisper.cpp/bindings/go +provides a speech-to-text service bindings for the Go programming language. +*/ +package whisper diff --git a/bindings/go/examples/go-model-download/context.go b/bindings/go/examples/go-model-download/context.go new file mode 100644 index 0000000..7d5f0dd --- /dev/null +++ b/bindings/go/examples/go-model-download/context.go @@ -0,0 +1,31 @@ +package main + +import ( + "context" + "os" + "os/signal" +) + +// ContextForSignal returns a context object which is cancelled when a signal +// is received. It returns nil if no signal parameter is provided +func ContextForSignal(signals ...os.Signal) context.Context { + if len(signals) == 0 { + return nil + } + + ch := make(chan os.Signal, 1) // Buffered channel with space for 1 signal + ctx, cancel := context.WithCancel(context.Background()) + + // Send message on channel when signal received + signal.Notify(ch, signals...) + + // When any signal is received, call cancel + go func() { + <-ch + cancel() + }() + + // Return success + return ctx +} + diff --git a/bindings/go/examples/go-model-download/main.go b/bindings/go/examples/go-model-download/main.go new file mode 100644 index 0000000..e72262e --- /dev/null +++ b/bindings/go/examples/go-model-download/main.go @@ -0,0 +1,311 @@ +package main + +import ( + "context" + "flag" + "fmt" + "io" + "net/http" + "net/url" + "os" + "path/filepath" + "strings" + "syscall" + "time" +) + +/////////////////////////////////////////////////////////////////////////////// +// CONSTANTS + +const ( + srcUrl = "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/" // The location of the models + srcExt = ".bin" // Filename extension + bufSize = 1024 * 64 // Size of the buffer used for downloading the model +) + +var ( + // The models which will be downloaded, if no model is specified as an argument + modelNames = []string{ + "tiny", "tiny-q5_1", "tiny-q8_0", + "tiny.en", "tiny.en-q5_1", "tiny.en-q8_0", + "base", "base-q5_1", "base-q8_0", + "base.en", "base.en-q5_1", "base.en-q8_0", + "small", "small-q5_1", "small-q8_0", + "small.en", "small.en-q5_1", "small.en-q8_0", + "medium", "medium-q5_0", "medium-q8_0", + "medium.en", "medium.en-q5_0", "medium.en-q8_0", + "large-v1", + "large-v2", "large-v2-q5_0", "large-v2-q8_0", + "large-v3", "large-v3-q5_0", + "large-v3-turbo", "large-v3-turbo-q5_0", "large-v3-turbo-q8_0", + } +) + +var ( + // The output folder. When not set, use current working directory. + flagOut = flag.String("out", "", "Output folder") + + // HTTP timeout parameter - will timeout if takes longer than this to download a model + flagTimeout = flag.Duration("timeout", 30*time.Minute, "HTTP timeout") + + // Quiet parameter - will not print progress if set + flagQuiet = flag.Bool("quiet", false, "Quiet mode") +) + +/////////////////////////////////////////////////////////////////////////////// +// MAIN + +func main() { + flag.Usage = func() { + name := filepath.Base(flag.CommandLine.Name()) + fmt.Fprintf(flag.CommandLine.Output(), ` + Usage: %s [options] [<model>...] + + Options: + -out string Specify the output folder where models will be saved. + Default: Current working directory. + -timeout duration Set the maximum duration for downloading a model. + Example: 10m, 1h (default: 30m0s). + -quiet Suppress all output except errors. + + Examples: + 1. Download a specific model: + %s -out ./models tiny-q8_0 + + 2. Download all models: + %s -out ./models + + `, name, name, name) + + flag.PrintDefaults() + } + flag.Parse() + + // Get output path + out, err := GetOut() + if err != nil { + fmt.Fprintln(os.Stderr, "Error:", err) + os.Exit(-1) + } + + // Create context which quits on SIGINT or SIGQUIT + ctx := ContextForSignal(os.Interrupt, syscall.SIGQUIT) + + // Progress filehandle + progress := os.Stdout + if *flagQuiet { + progress, err = os.Open(os.DevNull) + if err != nil { + fmt.Fprintln(os.Stderr, "Error:", err) + os.Exit(-1) + } + defer progress.Close() + } + + // Download models - exit on error or interrupt + for _, model := range GetModels() { + url, err := URLForModel(model) + if err != nil { + fmt.Fprintln(os.Stderr, "Error:", err) + continue + } else if path, err := Download(ctx, progress, url, out); err == nil || err == io.EOF { + continue + } else if err == context.Canceled { + os.Remove(path) + fmt.Fprintln(progress, "\nInterrupted") + break + } else if err == context.DeadlineExceeded { + os.Remove(path) + fmt.Fprintln(progress, "Timeout downloading model") + continue + } else { + os.Remove(path) + fmt.Fprintln(os.Stderr, "Error:", err) + break + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// PUBLIC METHODS + +// GetOut returns the path to the output directory +func GetOut() (string, error) { + if *flagOut == "" { + return os.Getwd() + } + if info, err := os.Stat(*flagOut); err != nil { + return "", err + } else if !info.IsDir() { + return "", fmt.Errorf("not a directory: %s", info.Name()) + } else { + return *flagOut, nil + } +} + +// GetModels returns the list of models to download +func GetModels() []string { + if flag.NArg() == 0 { + fmt.Println("No model specified.") + fmt.Println("Preparing to download all models...") + + // Calculate total download size + fmt.Println("Calculating total download size...") + totalSize, err := CalculateTotalDownloadSize(modelNames) + if err != nil { + fmt.Println("Error calculating download sizes:", err) + os.Exit(1) + } + + fmt.Println("View available models: https://huggingface.co/ggerganov/whisper.cpp/tree/main") + fmt.Printf("Total download size: %.2f GB\n", float64(totalSize)/(1024*1024*1024)) + fmt.Println("Would you like to download all models? (y/N)") + + // Prompt for user input + var response string + fmt.Scanln(&response) + if response != "y" && response != "Y" { + fmt.Println("Aborting. Specify a model to download.") + os.Exit(0) + } + + return modelNames // Return all models if confirmed + } + return flag.Args() // Return specific models if arguments are provided +} + +func CalculateTotalDownloadSize(models []string) (int64, error) { + var totalSize int64 + client := http.Client{} + + for _, model := range models { + modelURL, err := URLForModel(model) + if err != nil { + return 0, err + } + + // Issue a HEAD request to get the file size + req, err := http.NewRequest("HEAD", modelURL, nil) + if err != nil { + return 0, err + } + + resp, err := client.Do(req) + if err != nil { + return 0, err + } + resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + fmt.Printf("Warning: Unable to fetch size for %s (HTTP %d)\n", model, resp.StatusCode) + continue + } + + size := resp.ContentLength + totalSize += size + } + return totalSize, nil +} + +// URLForModel returns the URL for the given model on huggingface.co +func URLForModel(model string) (string, error) { + // Ensure "ggml-" prefix is added only once + if !strings.HasPrefix(model, "ggml-") { + model = "ggml-" + model + } + + // Ensure ".bin" extension is added only once + if filepath.Ext(model) != srcExt { + model += srcExt + } + + // Parse the base URL + url, err := url.Parse(srcUrl) + if err != nil { + return "", err + } + + // Ensure no trailing slash in the base URL + url.Path = fmt.Sprintf("%s/%s", strings.TrimSuffix(url.Path, "/"), model) + return url.String(), nil +} + +// Download downloads the model from the given URL to the given output directory +func Download(ctx context.Context, p io.Writer, model, out string) (string, error) { + // Create HTTP client + client := http.Client{ + Timeout: *flagTimeout, + } + + // Initiate the download + req, err := http.NewRequest("GET", model, nil) + if err != nil { + return "", err + } + resp, err := client.Do(req) + if err != nil { + return "", err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return "", fmt.Errorf("%s: %s", model, resp.Status) + } + + // If output file exists and is the same size as the model, skip + path := filepath.Join(out, filepath.Base(model)) + if info, err := os.Stat(path); err == nil && info.Size() == resp.ContentLength { + fmt.Fprintln(p, "Skipping", model, "as it already exists") + return "", nil + } + + // Create file + w, err := os.Create(path) + if err != nil { + return "", err + } + defer w.Close() + + // Report + fmt.Fprintln(p, "Downloading", model, "to", out) + + // Progressively download the model + data := make([]byte, bufSize) + count, pct := int64(0), int64(0) + ticker := time.NewTicker(5 * time.Second) + for { + select { + case <-ctx.Done(): + // Cancelled, return error + return path, ctx.Err() + case <-ticker.C: + pct = DownloadReport(p, pct, count, resp.ContentLength) + default: + // Read body + n, err := resp.Body.Read(data) + if n > 0 { + if m, err := w.Write(data[:n]); err != nil { + return path, err + } else { + count += int64(m) + } + } + + if err != nil { + if err == io.EOF { + DownloadReport(p, pct, count, resp.ContentLength) + return path, nil + } + return path, err + } + } + } +} + +// Report periodically reports the download progress when percentage changes +func DownloadReport(w io.Writer, pct, count, total int64) int64 { + pct_ := count * 100 / total + if pct_ > pct { + fmt.Fprintf(w, " ...%d MB written (%d%%)\n", count/1e6, pct_) + } + return pct_ +} diff --git a/bindings/go/examples/go-whisper/color.go b/bindings/go/examples/go-whisper/color.go new file mode 100644 index 0000000..fa5ac2f --- /dev/null +++ b/bindings/go/examples/go-whisper/color.go @@ -0,0 +1,22 @@ +package main + +import "fmt" + +/////////////////////////////////////////////////////////////////////////////// +// CONSTANTS + +const ( + Reset = "\033[0m" + RGBPrefix = "\033[38;5;" // followed by RGB values in decimal format separated by colons + RGBSuffix = "m" +) + +/////////////////////////////////////////////////////////////////////////////// +// PUBLIC METHODS + +// Colorize text with RGB values, from 0 to 23 +func Colorize(text string, v int) string { + // https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit + // Grayscale colors are in the range 232-255 + return RGBPrefix + fmt.Sprint(v%24+232) + RGBSuffix + text + Reset +} diff --git a/bindings/go/examples/go-whisper/flags.go b/bindings/go/examples/go-whisper/flags.go new file mode 100644 index 0000000..766c92f --- /dev/null +++ b/bindings/go/examples/go-whisper/flags.go @@ -0,0 +1,147 @@ +package main + +import ( + "flag" + "fmt" + "strings" + "time" + + // Packages + whisper "github.com/ggerganov/whisper.cpp/bindings/go/pkg/whisper" +) + +/////////////////////////////////////////////////////////////////////////////// +// TYPES + +type Flags struct { + *flag.FlagSet +} + +/////////////////////////////////////////////////////////////////////////////// +// LIFECYCLE + +func NewFlags(name string, args []string) (*Flags, error) { + flags := &Flags{ + FlagSet: flag.NewFlagSet(name, flag.ContinueOnError), + } + + // Register the command line arguments + registerFlags(flags) + + // Parse command line + if err := flags.Parse(args); err != nil { + return nil, err + } + + // Return success + return flags, nil +} + +/////////////////////////////////////////////////////////////////////////////// +// PUBLIC METHODS + +func (flags *Flags) GetModel() string { + return flags.Lookup("model").Value.String() +} + +func (flags *Flags) GetLanguage() string { + return flags.Lookup("language").Value.String() +} + +func (flags *Flags) IsTranslate() bool { + return flags.Lookup("translate").Value.(flag.Getter).Get().(bool) +} + +func (flags *Flags) GetOffset() time.Duration { + return flags.Lookup("offset").Value.(flag.Getter).Get().(time.Duration) +} + +func (flags *Flags) GetDuration() time.Duration { + return flags.Lookup("duration").Value.(flag.Getter).Get().(time.Duration) +} + +func (flags *Flags) GetThreads() uint { + return flags.Lookup("threads").Value.(flag.Getter).Get().(uint) +} + +func (flags *Flags) GetOut() string { + return strings.ToLower(flags.Lookup("out").Value.String()) +} + +func (flags *Flags) IsTokens() bool { + return flags.Lookup("tokens").Value.String() == "true" +} + +func (flags *Flags) IsColorize() bool { + return flags.Lookup("colorize").Value.String() == "true" +} + +func (flags *Flags) GetMaxLen() uint { + return flags.Lookup("max-len").Value.(flag.Getter).Get().(uint) +} + +func (flags *Flags) GetMaxTokens() uint { + return flags.Lookup("max-tokens").Value.(flag.Getter).Get().(uint) +} + +func (flags *Flags) GetWordThreshold() float32 { + return float32(flags.Lookup("word-thold").Value.(flag.Getter).Get().(float64)) +} + +func (flags *Flags) SetParams(context whisper.Context) error { + if lang := flags.GetLanguage(); lang != "" && lang != "auto" { + fmt.Fprintf(flags.Output(), "Setting language to %q\n", lang) + if err := context.SetLanguage(lang); err != nil { + return err + } + } + if flags.IsTranslate() && context.IsMultilingual() { + fmt.Fprintf(flags.Output(), "Setting translate to true\n") + context.SetTranslate(true) + } + if offset := flags.GetOffset(); offset != 0 { + fmt.Fprintf(flags.Output(), "Setting offset to %v\n", offset) + context.SetOffset(offset) + } + if duration := flags.GetDuration(); duration != 0 { + fmt.Fprintf(flags.Output(), "Setting duration to %v\n", duration) + context.SetDuration(duration) + } + if threads := flags.GetThreads(); threads != 0 { + fmt.Fprintf(flags.Output(), "Setting threads to %d\n", threads) + context.SetThreads(threads) + } + if max_len := flags.GetMaxLen(); max_len != 0 { + fmt.Fprintf(flags.Output(), "Setting max_segment_length to %d\n", max_len) + context.SetMaxSegmentLength(max_len) + } + if max_tokens := flags.GetMaxTokens(); max_tokens != 0 { + fmt.Fprintf(flags.Output(), "Setting max_tokens to %d\n", max_tokens) + context.SetMaxTokensPerSegment(max_tokens) + } + if word_threshold := flags.GetWordThreshold(); word_threshold != 0 { + fmt.Fprintf(flags.Output(), "Setting word_threshold to %f\n", word_threshold) + context.SetTokenThreshold(word_threshold) + } + + // Return success + return nil +} + +/////////////////////////////////////////////////////////////////////////////// +// PRIVATE METHODS + +func registerFlags(flag *Flags) { + flag.String("model", "", "Path to the model file") + flag.String("language", "", "Spoken language") + flag.Bool("translate", false, "Translate from source language to english") + flag.Duration("offset", 0, "Time offset") + flag.Duration("duration", 0, "Duration of audio to process") + flag.Uint("threads", 0, "Number of threads to use") + flag.Uint("max-len", 0, "Maximum segment length in characters") + flag.Uint("max-tokens", 0, "Maximum tokens per segment") + flag.Float64("word-thold", 0, "Maximum segment score") + flag.Bool("tokens", false, "Display tokens") + flag.Bool("colorize", false, "Colorize tokens") + flag.String("out", "", "Output format (srt, none or leave as empty string)") +} diff --git a/bindings/go/examples/go-whisper/main.go b/bindings/go/examples/go-whisper/main.go new file mode 100644 index 0000000..1bff7f5 --- /dev/null +++ b/bindings/go/examples/go-whisper/main.go @@ -0,0 +1,43 @@ +package main + +import ( + "flag" + "fmt" + "os" + "path/filepath" + + // Packages + whisper "github.com/ggerganov/whisper.cpp/bindings/go/pkg/whisper" +) + +func main() { + flags, err := NewFlags(filepath.Base(os.Args[0]), os.Args[1:]) + if err == flag.ErrHelp { + os.Exit(0) + } else if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } else if flags.GetModel() == "" { + fmt.Fprintln(os.Stderr, "Use -model flag to specify which model file to use") + os.Exit(1) + } else if flags.NArg() == 0 { + fmt.Fprintln(os.Stderr, "No input files specified") + os.Exit(1) + } + + // Load model + model, err := whisper.New(flags.GetModel()) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + defer model.Close() + + // Process files + for _, filename := range flags.Args() { + if err := Process(model, filename, flags); err != nil { + fmt.Fprintln(os.Stderr, err) + continue + } + } +} diff --git a/bindings/go/examples/go-whisper/process.go b/bindings/go/examples/go-whisper/process.go new file mode 100644 index 0000000..833947e --- /dev/null +++ b/bindings/go/examples/go-whisper/process.go @@ -0,0 +1,132 @@ +package main + +import ( + "fmt" + "io" + "os" + "time" + + // Package imports + whisper "github.com/ggerganov/whisper.cpp/bindings/go/pkg/whisper" + wav "github.com/go-audio/wav" +) + +func Process(model whisper.Model, path string, flags *Flags) error { + var data []float32 + + // Create processing context + context, err := model.NewContext() + if err != nil { + return err + } + + // Set the parameters + if err := flags.SetParams(context); err != nil { + return err + } + + fmt.Printf("\n%s\n", context.SystemInfo()) + + // Open the file + fmt.Fprintf(flags.Output(), "Loading %q\n", path) + fh, err := os.Open(path) + if err != nil { + return err + } + defer fh.Close() + + // Decode the WAV file - load the full buffer + dec := wav.NewDecoder(fh) + if buf, err := dec.FullPCMBuffer(); err != nil { + return err + } else if dec.SampleRate != whisper.SampleRate { + return fmt.Errorf("unsupported sample rate: %d", dec.SampleRate) + } else if dec.NumChans != 1 { + return fmt.Errorf("unsupported number of channels: %d", dec.NumChans) + } else { + data = buf.AsFloat32Buffer().Data + } + + // Segment callback when -tokens is specified + var cb whisper.SegmentCallback + if flags.IsTokens() { + cb = func(segment whisper.Segment) { + fmt.Fprintf(flags.Output(), "%02d [%6s->%6s] ", segment.Num, segment.Start.Truncate(time.Millisecond), segment.End.Truncate(time.Millisecond)) + for _, token := range segment.Tokens { + if flags.IsColorize() && context.IsText(token) { + fmt.Fprint(flags.Output(), Colorize(token.Text, int(token.P*24.0)), " ") + } else { + fmt.Fprint(flags.Output(), token.Text, " ") + } + } + fmt.Fprintln(flags.Output(), "") + fmt.Fprintln(flags.Output(), "") + } + } + + // Process the data + fmt.Fprintf(flags.Output(), " ...processing %q\n", path) + context.ResetTimings() + if err := context.Process(data, nil, cb, nil); err != nil { + return err + } + + context.PrintTimings() + + // Print out the results + switch { + case flags.GetOut() == "srt": + return OutputSRT(os.Stdout, context) + case flags.GetOut() == "none": + return nil + default: + return Output(os.Stdout, context, flags.IsColorize()) + } +} + +// Output text as SRT file +func OutputSRT(w io.Writer, context whisper.Context) error { + n := 1 + for { + segment, err := context.NextSegment() + if err == io.EOF { + return nil + } else if err != nil { + return err + } + fmt.Fprintln(w, n) + fmt.Fprintln(w, srtTimestamp(segment.Start), " --> ", srtTimestamp(segment.End)) + fmt.Fprintln(w, segment.Text) + fmt.Fprintln(w, "") + n++ + } +} + +// Output text to terminal +func Output(w io.Writer, context whisper.Context, colorize bool) error { + for { + segment, err := context.NextSegment() + if err == io.EOF { + return nil + } else if err != nil { + return err + } + fmt.Fprintf(w, "[%6s->%6s]", segment.Start.Truncate(time.Millisecond), segment.End.Truncate(time.Millisecond)) + if colorize { + for _, token := range segment.Tokens { + if !context.IsText(token) { + continue + } + fmt.Fprint(w, " ", Colorize(token.Text, int(token.P*24.0))) + } + fmt.Fprint(w, "\n") + } else { + fmt.Fprintln(w, " ", segment.Text) + } + } +} + +// Return srtTimestamp +func srtTimestamp(t time.Duration) string { + return fmt.Sprintf("%02d:%02d:%02d,%03d", t/time.Hour, (t%time.Hour)/time.Minute, (t%time.Minute)/time.Second, (t%time.Second)/time.Millisecond) +} diff --git a/bindings/go/go.mod b/bindings/go/go.mod new file mode 100644 index 0000000..7c92c7b --- /dev/null +++ b/bindings/go/go.mod @@ -0,0 +1,16 @@ +module github.com/ggerganov/whisper.cpp/bindings/go + +go 1.23 + +require ( + github.com/go-audio/wav v1.1.0 + github.com/stretchr/testify v1.9.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-audio/audio v1.0.0 // indirect + github.com/go-audio/riff v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/bindings/go/go.sum b/bindings/go/go.sum new file mode 100644 index 0000000..1f99aeb --- /dev/null +++ b/bindings/go/go.sum @@ -0,0 +1,16 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-audio/audio v1.0.0 h1:zS9vebldgbQqktK4H0lUqWrG8P0NxCJVqcj7ZpNnwd4= +github.com/go-audio/audio v1.0.0/go.mod h1:6uAu0+H2lHkwdGsAY+j2wHPNPpPoeg5AaEFh9FlA+Zs= +github.com/go-audio/riff v1.0.0 h1:d8iCGbDvox9BfLagY94fBynxSPHO80LmZCaOsmKxokA= +github.com/go-audio/riff v1.0.0/go.mod h1:l3cQwc85y79NQFCRB7TiPoNiaijp6q8Z0Uv38rVG498= +github.com/go-audio/wav v1.1.0 h1:jQgLtbqBzY7G+BM8fXF7AHUk1uHUviWS4X39d5rsL2g= +github.com/go-audio/wav v1.1.0/go.mod h1:mpe9qfwbScEbkd8uybLuIpTgHyrISw/OTuvjUW2iGtE= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/bindings/go/params.go b/bindings/go/params.go new file mode 100644 index 0000000..7d6a30d --- /dev/null +++ b/bindings/go/params.go @@ -0,0 +1,244 @@ +package whisper + +import ( + "fmt" +) + +/////////////////////////////////////////////////////////////////////////////// +// CGO + +/* +#include <whisper.h> +*/ +import "C" + +/////////////////////////////////////////////////////////////////////////////// +// PUBLIC METHODS + +func (p *Params) SetTranslate(v bool) { + p.translate = toBool(v) +} + +func (p *Params) SetSplitOnWord(v bool) { + p.split_on_word = toBool(v) +} + +func (p *Params) SetNoContext(v bool) { + p.no_context = toBool(v) +} + +func (p *Params) SetSingleSegment(v bool) { + p.single_segment = toBool(v) +} + +func (p *Params) SetPrintSpecial(v bool) { + p.print_special = toBool(v) +} + +func (p *Params) SetPrintProgress(v bool) { + p.print_progress = toBool(v) +} + +func (p *Params) SetPrintRealtime(v bool) { + p.print_realtime = toBool(v) +} + +func (p *Params) SetPrintTimestamps(v bool) { + p.print_timestamps = toBool(v) +} + +// Voice Activity Detection (VAD) +func (p *Params) SetVAD(v bool) { + p.vad = toBool(v) +} + +func (p *Params) SetVADModelPath(path string) { + p.vad_model_path = C.CString(path) +} + +func (p *Params) SetVADThreshold(t float32) { + p.vad_params.threshold = C.float(t) +} + +func (p *Params) SetVADMinSpeechMs(ms int) { + p.vad_params.min_speech_duration_ms = C.int(ms) +} + +func (p *Params) SetVADMinSilenceMs(ms int) { + p.vad_params.min_silence_duration_ms = C.int(ms) +} + +func (p *Params) SetVADMaxSpeechSec(s float32) { + p.vad_params.max_speech_duration_s = C.float(s) +} + +func (p *Params) SetVADSpeechPadMs(ms int) { + p.vad_params.speech_pad_ms = C.int(ms) +} + +func (p *Params) SetVADSamplesOverlap(sec float32) { + p.vad_params.samples_overlap = C.float(sec) +} + +// Set language id +func (p *Params) SetLanguage(lang int) error { + if lang == -1 { + p.language = nil + return nil + } + str := C.whisper_lang_str(C.int(lang)) + if str == nil { + return ErrInvalidLanguage + } else { + p.language = str + } + return nil +} + +// Get language id +func (p *Params) Language() int { + if p.language == nil { + return -1 + } + return int(C.whisper_lang_id(p.language)) +} + +// Threads available +func (p *Params) Threads() int { + return int(p.n_threads) +} + +// Set number of threads to use +func (p *Params) SetThreads(threads int) { + p.n_threads = C.int(threads) +} + +// Set start offset in ms +func (p *Params) SetOffset(offset_ms int) { + p.offset_ms = C.int(offset_ms) +} + +// Set audio duration to process in ms +func (p *Params) SetDuration(duration_ms int) { + p.duration_ms = C.int(duration_ms) +} + +// Set timestamp token probability threshold (~0.01) +func (p *Params) SetTokenThreshold(t float32) { + p.thold_pt = C.float(t) +} + +// Set timestamp token sum probability threshold (~0.01) +func (p *Params) SetTokenSumThreshold(t float32) { + p.thold_ptsum = C.float(t) +} + +// Set max segment length in characters +func (p *Params) SetMaxSegmentLength(n int) { + p.max_len = C.int(n) +} + +func (p *Params) SetTokenTimestamps(b bool) { + p.token_timestamps = toBool(b) +} + +// Set max tokens per segment (0 = no limit) +func (p *Params) SetMaxTokensPerSegment(n int) { + p.max_tokens = C.int(n) +} + +// Set audio encoder context +func (p *Params) SetAudioCtx(n int) { + p.audio_ctx = C.int(n) +} + +func (p *Params) SetMaxContext(n int) { + p.n_max_text_ctx = C.int(n) +} + +func (p *Params) SetBeamSize(n int) { + p.beam_search.beam_size = C.int(n) +} + +func (p *Params) SetEntropyThold(t float32) { + p.entropy_thold = C.float(t) +} + +func (p *Params) SetTemperature(t float32) { + p.temperature = C.float(t) +} + +// Sets the fallback temperature incrementation +// Pass -1.0 to disable this feature +func (p *Params) SetTemperatureFallback(t float32) { + p.temperature_inc = C.float(t) +} + +// Set initial prompt +func (p *Params) SetInitialPrompt(prompt string) { + p.initial_prompt = C.CString(prompt) +} + +func (p *Params) SetCarryInitialPrompt(v bool) { + p.carry_initial_prompt = toBool(v) +} + +/////////////////////////////////////////////////////////////////////////////// +// PRIVATE METHODS + +func toBool(v bool) C.bool { + if v { + return C.bool(true) + } + return C.bool(false) +} + +/////////////////////////////////////////////////////////////////////////////// +// STRINGIFY + +func (p *Params) String() string { + str := "<whisper.params" + str += fmt.Sprintf(" strategy=%v", p.strategy) + str += fmt.Sprintf(" n_threads=%d", p.n_threads) + if p.language != nil { + str += fmt.Sprintf(" language=%s", C.GoString(p.language)) + } + str += fmt.Sprintf(" n_max_text_ctx=%d", p.n_max_text_ctx) + str += fmt.Sprintf(" offset_ms=%d", p.offset_ms) + str += fmt.Sprintf(" duration_ms=%d", p.duration_ms) + str += fmt.Sprintf(" audio_ctx=%d", p.audio_ctx) + str += fmt.Sprintf(" initial_prompt=%s", C.GoString(p.initial_prompt)) + str += fmt.Sprintf(" entropy_thold=%f", p.entropy_thold) + str += fmt.Sprintf(" temperature=%f", p.temperature) + str += fmt.Sprintf(" temperature_inc=%f", p.temperature_inc) + str += fmt.Sprintf(" beam_size=%d", p.beam_search.beam_size) + if p.translate { + str += " translate" + } + if p.no_context { + str += " no_context" + } + if p.single_segment { + str += " single_segment" + } + if p.print_special { + str += " print_special" + } + if p.print_progress { + str += " print_progress" + } + if p.print_realtime { + str += " print_realtime" + } + if p.print_timestamps { + str += " print_timestamps" + } + if p.token_timestamps { + str += " token_timestamps" + } + if p.carry_initial_prompt { + str += " carry_initial_prompt" + } + + return str + ">" +} diff --git a/bindings/go/pkg/whisper/consts.go b/bindings/go/pkg/whisper/consts.go new file mode 100644 index 0000000..5c22dc1 --- /dev/null +++ b/bindings/go/pkg/whisper/consts.go @@ -0,0 +1,28 @@ +package whisper + +import ( + "errors" + + // Bindings + whisper "github.com/ggerganov/whisper.cpp/bindings/go" +) + +/////////////////////////////////////////////////////////////////////////////// +// ERRORS + +var ( + ErrUnableToLoadModel = errors.New("unable to load model") + ErrInternalAppError = errors.New("internal application error") + ErrProcessingFailed = errors.New("processing failed") + ErrUnsupportedLanguage = errors.New("unsupported language") + ErrModelNotMultilingual = errors.New("model is not multilingual") +) + +/////////////////////////////////////////////////////////////////////////////// +// CONSTANTS + +// SampleRate is the sample rate of the audio data. +const SampleRate = whisper.SampleRate + +// SampleBits is the number of bytes per sample. +const SampleBits = whisper.SampleBits diff --git a/bindings/go/pkg/whisper/context.go b/bindings/go/pkg/whisper/context.go new file mode 100644 index 0000000..d356d72 --- /dev/null +++ b/bindings/go/pkg/whisper/context.go @@ -0,0 +1,385 @@ +package whisper + +import ( + "fmt" + "io" + "runtime" + "strings" + "time" + + // Bindings + whisper "github.com/ggerganov/whisper.cpp/bindings/go" +) + +/////////////////////////////////////////////////////////////////////////////// +// TYPES + +type context struct { + n int + model *model + params whisper.Params +} + +// Make sure context adheres to the interface +var _ Context = (*context)(nil) + +/////////////////////////////////////////////////////////////////////////////// +// LIFECYCLE + +func newContext(model *model, params whisper.Params) (Context, error) { + context := new(context) + context.model = model + context.params = params + + // Return success + return context, nil +} + +/////////////////////////////////////////////////////////////////////////////// +// PUBLIC METHODS + +// Set the language to use for speech recognition. +func (context *context) SetLanguage(lang string) error { + if context.model.ctx == nil { + return ErrInternalAppError + } + if !context.model.IsMultilingual() { + return ErrModelNotMultilingual + } + + if lang == "auto" { + context.params.SetLanguage(-1) + } else if id := context.model.ctx.Whisper_lang_id(lang); id < 0 { + return ErrUnsupportedLanguage + } else if err := context.params.SetLanguage(id); err != nil { + return err + } + // Return success + return nil +} + +func (context *context) IsMultilingual() bool { + return context.model.IsMultilingual() +} + +// Get language +func (context *context) Language() string { + id := context.params.Language() + if id == -1 { + return "auto" + } + return whisper.Whisper_lang_str(context.params.Language()) +} + +func (context *context) DetectedLanguage() string { + return whisper.Whisper_lang_str(context.model.ctx.Whisper_full_lang_id()) +} + +// Set translate flag +func (context *context) SetTranslate(v bool) { + context.params.SetTranslate(v) +} + +// Voice Activity Detection (VAD) +func (context *context) SetVAD(v bool) { + context.params.SetVAD(v) +} + +func (context *context) SetVADModelPath(path string) { + context.params.SetVADModelPath(path) +} + +func (context *context) SetVADThreshold(t float32) { + context.params.SetVADThreshold(t) +} + +func (context *context) SetVADMinSpeechMs(ms int) { + context.params.SetVADMinSpeechMs(ms) +} + +func (context *context) SetVADMinSilenceMs(ms int) { + context.params.SetVADMinSilenceMs(ms) +} + +func (context *context) SetVADMaxSpeechSec(s float32) { + context.params.SetVADMaxSpeechSec(s) +} + +func (context *context) SetVADSpeechPadMs(ms int) { + context.params.SetVADSpeechPadMs(ms) +} + +func (context *context) SetVADSamplesOverlap(sec float32) { + context.params.SetVADSamplesOverlap(sec) +} + +func (context *context) SetSplitOnWord(v bool) { + context.params.SetSplitOnWord(v) +} + +// Set number of threads to use +func (context *context) SetThreads(v uint) { + context.params.SetThreads(int(v)) +} + +// Set time offset +func (context *context) SetOffset(v time.Duration) { + context.params.SetOffset(int(v.Milliseconds())) +} + +// Set duration of audio to process +func (context *context) SetDuration(v time.Duration) { + context.params.SetDuration(int(v.Milliseconds())) +} + +// Set timestamp token probability threshold (~0.01) +func (context *context) SetTokenThreshold(t float32) { + context.params.SetTokenThreshold(t) +} + +// Set timestamp token sum probability threshold (~0.01) +func (context *context) SetTokenSumThreshold(t float32) { + context.params.SetTokenSumThreshold(t) +} + +// Set max segment length in characters +func (context *context) SetMaxSegmentLength(n uint) { + context.params.SetMaxSegmentLength(int(n)) +} + +// Set token timestamps flag +func (context *context) SetTokenTimestamps(b bool) { + context.params.SetTokenTimestamps(b) +} + +// Set max tokens per segment (0 = no limit) +func (context *context) SetMaxTokensPerSegment(n uint) { + context.params.SetMaxTokensPerSegment(int(n)) +} + +// Set audio encoder context +func (context *context) SetAudioCtx(n uint) { + context.params.SetAudioCtx(int(n)) +} + +// Set maximum number of text context tokens to store +func (context *context) SetMaxContext(n int) { + context.params.SetMaxContext(n) +} + +// Set Beam Size +func (context *context) SetBeamSize(n int) { + context.params.SetBeamSize(n) +} + +// Set Entropy threshold +func (context *context) SetEntropyThold(t float32) { + context.params.SetEntropyThold(t) +} + +// Set Temperature +func (context *context) SetTemperature(t float32) { + context.params.SetTemperature(t) +} + +// Set the fallback temperature incrementation +// Pass -1.0 to disable this feature +func (context *context) SetTemperatureFallback(t float32) { + context.params.SetTemperatureFallback(t) +} + +// Set initial prompt +func (context *context) SetInitialPrompt(prompt string) { + context.params.SetInitialPrompt(prompt) +} + +// ResetTimings resets the mode timings. Should be called before processing +func (context *context) ResetTimings() { + context.model.ctx.Whisper_reset_timings() +} + +// PrintTimings prints the model timings to stdout. +func (context *context) PrintTimings() { + context.model.ctx.Whisper_print_timings() +} + +// SystemInfo returns the system information +func (context *context) SystemInfo() string { + return fmt.Sprintf("system_info: n_threads = %d / %d | %s\n", + context.params.Threads(), + runtime.NumCPU(), + whisper.Whisper_print_system_info(), + ) +} + +// Use mel data at offset_ms to try and auto-detect the spoken language +// Make sure to call whisper_pcm_to_mel() or whisper_set_mel() first. +// Returns the probabilities of all languages. +func (context *context) WhisperLangAutoDetect(offset_ms int, n_threads int) ([]float32, error) { + langProbs, err := context.model.ctx.Whisper_lang_auto_detect(offset_ms, n_threads) + if err != nil { + return nil, err + } + return langProbs, nil +} + +// Process new sample data and return any errors +func (context *context) Process( + data []float32, + callEncoderBegin EncoderBeginCallback, + callNewSegment SegmentCallback, + callProgress ProgressCallback, +) error { + if context.model.ctx == nil { + return ErrInternalAppError + } + // If the callback is defined then we force on single_segment mode + if callNewSegment != nil { + context.params.SetSingleSegment(true) + } + + // We don't do parallel processing at the moment + processors := 0 + if processors > 1 { + if err := context.model.ctx.Whisper_full_parallel(context.params, data, processors, callEncoderBegin, + func(new int) { + if callNewSegment != nil { + num_segments := context.model.ctx.Whisper_full_n_segments() + s0 := num_segments - new + for i := s0; i < num_segments; i++ { + callNewSegment(toSegment(context.model.ctx, i)) + } + } + }); err != nil { + return err + } + } else if err := context.model.ctx.Whisper_full(context.params, data, callEncoderBegin, + func(new int) { + if callNewSegment != nil { + num_segments := context.model.ctx.Whisper_full_n_segments() + s0 := num_segments - new + for i := s0; i < num_segments; i++ { + callNewSegment(toSegment(context.model.ctx, i)) + } + } + }, func(progress int) { + if callProgress != nil { + callProgress(progress) + } + }); err != nil { + return err + } + + // Reset n so that more Segments can be available within NextSegment call + context.n = 0 + + // Return success + return nil +} + +// Return the next segment of tokens +func (context *context) NextSegment() (Segment, error) { + if context.model.ctx == nil { + return Segment{}, ErrInternalAppError + } + if context.n >= context.model.ctx.Whisper_full_n_segments() { + return Segment{}, io.EOF + } + + // Populate result + result := toSegment(context.model.ctx, context.n) + + // Increment the cursor + context.n++ + + // Return success + return result, nil +} + +// Test for text tokens +func (context *context) IsText(t Token) bool { + switch { + case context.IsBEG(t): + return false + case context.IsSOT(t): + return false + case whisper.Token(t.Id) >= context.model.ctx.Whisper_token_eot(): + return false + case context.IsPREV(t): + return false + case context.IsSOLM(t): + return false + case context.IsNOT(t): + return false + default: + return true + } +} + +// Test for "begin" token +func (context *context) IsBEG(t Token) bool { + return whisper.Token(t.Id) == context.model.ctx.Whisper_token_beg() +} + +// Test for "start of transcription" token +func (context *context) IsSOT(t Token) bool { + return whisper.Token(t.Id) == context.model.ctx.Whisper_token_sot() +} + +// Test for "end of transcription" token +func (context *context) IsEOT(t Token) bool { + return whisper.Token(t.Id) == context.model.ctx.Whisper_token_eot() +} + +// Test for "start of prev" token +func (context *context) IsPREV(t Token) bool { + return whisper.Token(t.Id) == context.model.ctx.Whisper_token_prev() +} + +// Test for "start of lm" token +func (context *context) IsSOLM(t Token) bool { + return whisper.Token(t.Id) == context.model.ctx.Whisper_token_solm() +} + +// Test for "No timestamps" token +func (context *context) IsNOT(t Token) bool { + return whisper.Token(t.Id) == context.model.ctx.Whisper_token_not() +} + +// Test for token associated with a specific language +func (context *context) IsLANG(t Token, lang string) bool { + if id := context.model.ctx.Whisper_lang_id(lang); id >= 0 { + return whisper.Token(t.Id) == context.model.ctx.Whisper_token_lang(id) + } else { + return false + } +} + +/////////////////////////////////////////////////////////////////////////////// +// PRIVATE METHODS + +func toSegment(ctx *whisper.Context, n int) Segment { + return Segment{ + Num: n, + Text: strings.TrimSpace(ctx.Whisper_full_get_segment_text(n)), + Start: time.Duration(ctx.Whisper_full_get_segment_t0(n)) * time.Millisecond * 10, + End: time.Duration(ctx.Whisper_full_get_segment_t1(n)) * time.Millisecond * 10, + Tokens: toTokens(ctx, n), + } +} + +func toTokens(ctx *whisper.Context, n int) []Token { + result := make([]Token, ctx.Whisper_full_n_tokens(n)) + for i := 0; i < len(result); i++ { + data := ctx.Whisper_full_get_token_data(n, i) + + result[i] = Token{ + Id: int(ctx.Whisper_full_get_token_id(n, i)), + Text: ctx.Whisper_full_get_token_text(n, i), + P: ctx.Whisper_full_get_token_p(n, i), + Start: time.Duration(data.T0()) * time.Millisecond * 10, + End: time.Duration(data.T1()) * time.Millisecond * 10, + } + } + return result +} diff --git a/bindings/go/pkg/whisper/context_test.go b/bindings/go/pkg/whisper/context_test.go new file mode 100644 index 0000000..79f6a59 --- /dev/null +++ b/bindings/go/pkg/whisper/context_test.go @@ -0,0 +1,172 @@ +package whisper_test + +import ( + "os" + "strings" + "testing" + + "github.com/ggerganov/whisper.cpp/bindings/go/pkg/whisper" + "github.com/go-audio/wav" + assert "github.com/stretchr/testify/assert" +) + +func TestSetLanguage(t *testing.T) { + assert := assert.New(t) + + model, err := whisper.New(ModelPath) + assert.NoError(err) + assert.NotNil(model) + defer model.Close() + + context, err := model.NewContext() + assert.NoError(err) + + // This returns an error since + // the model 'models/ggml-small.en.bin' + // that is loaded is not multilingual + err = context.SetLanguage("en") + assert.Error(err) +} + +func TestContextModelIsMultilingual(t *testing.T) { + assert := assert.New(t) + + model, err := whisper.New(ModelPath) + assert.NoError(err) + assert.NotNil(model) + defer model.Close() + + context, err := model.NewContext() + assert.NoError(err) + + isMultilingual := context.IsMultilingual() + + // This returns false since + // the model 'models/ggml-small.en.bin' + // that is loaded is not multilingual + assert.False(isMultilingual) +} + +func TestLanguage(t *testing.T) { + assert := assert.New(t) + + model, err := whisper.New(ModelPath) + assert.NoError(err) + assert.NotNil(model) + defer model.Close() + + context, err := model.NewContext() + assert.NoError(err) + + // This always returns en since + // the model 'models/ggml-small.en.bin' + // that is loaded is not multilingual + expectedLanguage := "en" + actualLanguage := context.Language() + assert.Equal(expectedLanguage, actualLanguage) +} + +func TestProcess(t *testing.T) { + assert := assert.New(t) + + fh, err := os.Open(SamplePath) + assert.NoError(err) + defer fh.Close() + + // Decode the WAV file - load the full buffer + dec := wav.NewDecoder(fh) + buf, err := dec.FullPCMBuffer() + assert.NoError(err) + assert.Equal(uint16(1), dec.NumChans) + + data := buf.AsFloat32Buffer().Data + + model, err := whisper.New(ModelPath) + assert.NoError(err) + assert.NotNil(model) + defer model.Close() + + context, err := model.NewContext() + assert.NoError(err) + + err = context.Process(data, nil, nil, nil) + assert.NoError(err) +} + +func TestProcessMaxTokensPerSegment(t *testing.T) { + assert := assert.New(t) + + if _, err := os.Stat(ModelPath); os.IsNotExist(err) { + t.Skip("Skipping test, model not found:", ModelPath) + } + + fh, err := os.Open(SamplePath) + assert.NoError(err) + defer fh.Close() + + // Decode the WAV file - load the full buffer + dec := wav.NewDecoder(fh) + buf, err := dec.FullPCMBuffer() + assert.NoError(err) + assert.Equal(uint16(1), dec.NumChans) + + data := buf.AsFloat32Buffer().Data + + model, err := whisper.New(ModelPath) + assert.NoError(err) + assert.NotNil(model) + defer model.Close() + + context, err := model.NewContext() + assert.NoError(err) + + context.SetMaxTokensPerSegment(5) + + err = context.Process(data, nil, nil, nil) + assert.NoError(err) + + var text strings.Builder + nSegments := 0 + for { + segment, err := context.NextSegment() + if err != nil { + break + } + nSegments++ + text.WriteString(segment.Text) + } + + assert.Greater(nSegments, 1) + assert.Contains(text.String(), "country") +} + +func TestDetectedLanguage(t *testing.T) { + assert := assert.New(t) + + fh, err := os.Open(SamplePath) + assert.NoError(err) + defer fh.Close() + + // Decode the WAV file - load the full buffer + dec := wav.NewDecoder(fh) + buf, err := dec.FullPCMBuffer() + assert.NoError(err) + assert.Equal(uint16(1), dec.NumChans) + + data := buf.AsFloat32Buffer().Data + + model, err := whisper.New(ModelPath) + assert.NoError(err) + assert.NotNil(model) + defer model.Close() + + context, err := model.NewContext() + assert.NoError(err) + + err = context.Process(data, nil, nil, nil) + assert.NoError(err) + + expectedLanguage := "en" + actualLanguage := context.DetectedLanguage() + assert.Equal(expectedLanguage, actualLanguage) +} diff --git a/bindings/go/pkg/whisper/doc.go b/bindings/go/pkg/whisper/doc.go new file mode 100644 index 0000000..fd4f1b9 --- /dev/null +++ b/bindings/go/pkg/whisper/doc.go @@ -0,0 +1,4 @@ +/* +This is the higher-level speech-to-text whisper.cpp API for go +*/ +package whisper diff --git a/bindings/go/pkg/whisper/interface.go b/bindings/go/pkg/whisper/interface.go new file mode 100644 index 0000000..2b275dd --- /dev/null +++ b/bindings/go/pkg/whisper/interface.go @@ -0,0 +1,118 @@ +package whisper + +import ( + "io" + "time" +) + +/////////////////////////////////////////////////////////////////////////////// +// TYPES + +// SegmentCallback is the callback function for processing segments in real +// time. It is called during the Process function +type SegmentCallback func(Segment) + +// ProgressCallback is the callback function for reporting progress during +// processing. It is called during the Process function +type ProgressCallback func(int) + +// EncoderBeginCallback is the callback function for checking if we want to +// continue processing. It is called during the Process function +type EncoderBeginCallback func() bool + +// Model is the interface to a whisper model. Create a new model with the +// function whisper.New(string) +type Model interface { + io.Closer + + // Return a new speech-to-text context. + NewContext() (Context, error) + + // Return true if the model is multilingual. + IsMultilingual() bool + + // Return all languages supported. + Languages() []string +} + +// Context is the speech recognition context. +type Context interface { + SetLanguage(string) error // Set the language to use for speech recognition, use "auto" for auto detect language. + SetTranslate(bool) // Set translate flag + IsMultilingual() bool // Return true if the model is multilingual. + Language() string // Get language + DetectedLanguage() string // Get detected language + + SetOffset(time.Duration) // Set offset + SetDuration(time.Duration) // Set duration + SetThreads(uint) // Set number of threads to use + SetSplitOnWord(bool) // Set split on word flag + SetTokenThreshold(float32) // Set timestamp token probability threshold + SetTokenSumThreshold(float32) // Set timestamp token sum probability threshold + SetMaxSegmentLength(uint) // Set max segment length in characters + SetTokenTimestamps(bool) // Set token timestamps flag + SetMaxTokensPerSegment(uint) // Set max tokens per segment (0 = no limit) + SetAudioCtx(uint) // Set audio encoder context + SetMaxContext(n int) // Set maximum number of text context tokens to store + SetBeamSize(n int) // Set Beam Size + SetEntropyThold(t float32) // Set Entropy threshold + SetInitialPrompt(prompt string) // Set initial prompt + SetTemperature(t float32) // Set temperature + SetTemperatureFallback(t float32) // Set temperature incrementation + + SetVAD(v bool) + SetVADModelPath(path string) + SetVADThreshold(t float32) + SetVADMinSpeechMs(ms int) + SetVADMinSilenceMs(ms int) + SetVADMaxSpeechSec(s float32) + SetVADSpeechPadMs(ms int) + SetVADSamplesOverlap(sec float32) + + // Process mono audio data and return any errors. + // If defined, newly generated segments are passed to the + // callback function during processing. + Process([]float32, EncoderBeginCallback, SegmentCallback, ProgressCallback) error + + // After process is called, return segments until the end of the stream + // is reached, when io.EOF is returned. + NextSegment() (Segment, error) + + IsBEG(Token) bool // Test for "begin" token + IsSOT(Token) bool // Test for "start of transcription" token + IsEOT(Token) bool // Test for "end of transcription" token + IsPREV(Token) bool // Test for "start of prev" token + IsSOLM(Token) bool // Test for "start of lm" token + IsNOT(Token) bool // Test for "No timestamps" token + IsLANG(Token, string) bool // Test for token associated with a specific language + IsText(Token) bool // Test for text token + + // Timings + PrintTimings() + ResetTimings() + + SystemInfo() string +} + +// Segment is the text result of a speech recognition. +type Segment struct { + // Segment Number + Num int + + // Time beginning and end timestamps for the segment. + Start, End time.Duration + + // The text of the segment. + Text string + + // The tokens of the segment. + Tokens []Token +} + +// Token is a text or special token +type Token struct { + Id int + Text string + P float32 + Start, End time.Duration +} diff --git a/bindings/go/pkg/whisper/model.go b/bindings/go/pkg/whisper/model.go new file mode 100644 index 0000000..68a1502 --- /dev/null +++ b/bindings/go/pkg/whisper/model.go @@ -0,0 +1,101 @@ +package whisper + +import ( + "fmt" + "os" + "runtime" + + // Bindings + whisper "github.com/ggerganov/whisper.cpp/bindings/go" +) + +/////////////////////////////////////////////////////////////////////////////// +// TYPES + +type model struct { + path string + ctx *whisper.Context +} + +// Make sure model adheres to the interface +var _ Model = (*model)(nil) + +/////////////////////////////////////////////////////////////////////////////// +// LIFECYCLE + +func New(path string) (Model, error) { + model := new(model) + if _, err := os.Stat(path); err != nil { + return nil, err + } else if ctx := whisper.Whisper_init(path); ctx == nil { + return nil, ErrUnableToLoadModel + } else { + model.ctx = ctx + model.path = path + } + + // Return success + return model, nil +} + +func (model *model) Close() error { + if model.ctx != nil { + model.ctx.Whisper_free() + } + + // Release resources + model.ctx = nil + + // Return success + return nil +} + +/////////////////////////////////////////////////////////////////////////////// +// STRINGIFY + +func (model *model) String() string { + str := "<whisper.model" + if model.ctx != nil { + str += fmt.Sprintf(" model=%q", model.path) + } + return str + ">" +} + +/////////////////////////////////////////////////////////////////////////////// +// PUBLIC METHODS + +// Return true if model is multilingual (language and translation options are supported) +func (model *model) IsMultilingual() bool { + return model.ctx.Whisper_is_multilingual() != 0 +} + +// Return all recognized languages. Initially it is set to auto-detect +func (model *model) Languages() []string { + result := make([]string, 0, whisper.Whisper_lang_max_id()) + for i := 0; i < whisper.Whisper_lang_max_id(); i++ { + str := whisper.Whisper_lang_str(i) + if model.ctx.Whisper_lang_id(str) >= 0 { + result = append(result, str) + } + } + return result +} + +func (model *model) NewContext() (Context, error) { + if model.ctx == nil { + return nil, ErrInternalAppError + } + + // Create new context + params := model.ctx.Whisper_full_default_params(whisper.SAMPLING_GREEDY) + params.SetTranslate(false) + params.SetPrintSpecial(false) + params.SetPrintProgress(false) + params.SetPrintRealtime(false) + params.SetPrintTimestamps(false) + params.SetThreads(runtime.NumCPU()) + params.SetNoContext(true) + + // Return new context + return newContext(model, params) +} diff --git a/bindings/go/pkg/whisper/model_test.go b/bindings/go/pkg/whisper/model_test.go new file mode 100644 index 0000000..8797f0d --- /dev/null +++ b/bindings/go/pkg/whisper/model_test.go @@ -0,0 +1,91 @@ +package whisper_test + +import ( + "testing" + + "github.com/ggerganov/whisper.cpp/bindings/go/pkg/whisper" + assert "github.com/stretchr/testify/assert" +) + +func TestNew(t *testing.T) { + assert := assert.New(t) + t.Run("valid model path", func(t *testing.T) { + model, err := whisper.New(ModelPath) + assert.NoError(err) + assert.NotNil(model) + defer model.Close() + + }) + + t.Run("invalid model path", func(t *testing.T) { + invalidModelPath := "invalid-model-path.bin" + model, err := whisper.New(invalidModelPath) + assert.Error(err) + assert.Nil(model) + }) +} + +func TestClose(t *testing.T) { + assert := assert.New(t) + + model, err := whisper.New(ModelPath) + assert.NoError(err) + assert.NotNil(model) + + err = model.Close() + assert.NoError(err) +} + +func TestNewContext(t *testing.T) { + assert := assert.New(t) + + model, err := whisper.New(ModelPath) + assert.NoError(err) + assert.NotNil(model) + defer model.Close() + + context, err := model.NewContext() + assert.NoError(err) + assert.NotNil(context) +} + +func TestIsMultilingual(t *testing.T) { + assert := assert.New(t) + + model, err := whisper.New(ModelPath) + assert.NoError(err) + assert.NotNil(model) + defer model.Close() + + isMultilingual := model.IsMultilingual() + + // This returns false since + // the model 'models/ggml-small.en.bin' + // that is loaded is not multilingual + assert.False(isMultilingual) +} + +func TestLanguages(t *testing.T) { + assert := assert.New(t) + + model, err := whisper.New(ModelPath) + assert.NoError(err) + assert.NotNil(model) + defer model.Close() + + expectedLanguages := []string{ + "en", "zh", "de", "es", "ru", "ko", "fr", "ja", "pt", "tr", "pl", + "ca", "nl", "ar", "sv", "it", "id", "hi", "fi", "vi", "he", "uk", + "el", "ms", "cs", "ro", "da", "hu", "ta", "no", "th", "ur", "hr", + "bg", "lt", "la", "mi", "ml", "cy", "sk", "te", "fa", "lv", "bn", + "sr", "az", "sl", "kn", "et", "mk", "br", "eu", "is", "hy", "ne", + "mn", "bs", "kk", "sq", "sw", "gl", "mr", "pa", "si", "km", "sn", + "yo", "so", "af", "oc", "ka", "be", "tg", "sd", "gu", "am", "yi", + "lo", "uz", "fo", "ht", "ps", "tk", "nn", "mt", "sa", "lb", "my", + "bo", "tl", "mg", "as", "tt", "haw", "ln", "ha", "ba", "jw", "su", + } + + actualLanguages := model.Languages() + + assert.Equal(expectedLanguages, actualLanguages) +} diff --git a/bindings/go/pkg/whisper/util_test.go b/bindings/go/pkg/whisper/util_test.go new file mode 100644 index 0000000..8ea2d5b --- /dev/null +++ b/bindings/go/pkg/whisper/util_test.go @@ -0,0 +1,6 @@ +package whisper_test + +const ( + ModelPath = "../../models/ggml-small.en.bin" + SamplePath = "../../samples/jfk.wav" +) diff --git a/bindings/go/samples/jfk.wav b/bindings/go/samples/jfk.wav new file mode 100644 index 0000000..3184d37 Binary files /dev/null and b/bindings/go/samples/jfk.wav differ diff --git a/bindings/go/whisper.go b/bindings/go/whisper.go new file mode 100644 index 0000000..3ef7341 --- /dev/null +++ b/bindings/go/whisper.go @@ -0,0 +1,470 @@ +package whisper + +import ( + "errors" + "unsafe" +) + +/////////////////////////////////////////////////////////////////////////////// +// CGO + +/* +#cgo LDFLAGS: -lwhisper -lggml -lggml-base -lggml-cpu -lm -lstdc++ +#cgo linux LDFLAGS: -fopenmp +#cgo darwin LDFLAGS: -lggml-metal -lggml-blas +#cgo darwin LDFLAGS: -framework Accelerate -framework Metal -framework Foundation -framework CoreGraphics +#include <whisper.h> +#include <stdlib.h> + +extern void callNewSegment(void* user_data, int new); +extern void callProgress(void* user_data, int progress); +extern bool callEncoderBegin(void* user_data); + +// Text segment callback +// Called on every newly generated text segment +// Use the whisper_full_...() functions to obtain the text segments +static void whisper_new_segment_cb(struct whisper_context* ctx, struct whisper_state* state, int n_new, void* user_data) { + if(user_data != NULL && ctx != NULL) { + callNewSegment(user_data, n_new); + } +} + +// Progress callback +// Called on every newly generated text segment +// Use the whisper_full_...() functions to obtain the text segments +static void whisper_progress_cb(struct whisper_context* ctx, struct whisper_state* state, int progress, void* user_data) { + if(user_data != NULL && ctx != NULL) { + callProgress(user_data, progress); + } +} + +// Encoder begin callback +// If not NULL, called before the encoder starts +// If it returns false, the computation is aborted +static bool whisper_encoder_begin_cb(struct whisper_context* ctx, struct whisper_state* state, void* user_data) { + if(user_data != NULL && ctx != NULL) { + return callEncoderBegin(user_data); + } + return false; +} + +// Get default parameters and set callbacks +static struct whisper_full_params whisper_full_default_params_cb(struct whisper_context* ctx, enum whisper_sampling_strategy strategy) { + struct whisper_full_params params = whisper_full_default_params(strategy); + params.new_segment_callback = whisper_new_segment_cb; + params.new_segment_callback_user_data = (void*)(ctx); + params.encoder_begin_callback = whisper_encoder_begin_cb; + params.encoder_begin_callback_user_data = (void*)(ctx); + params.progress_callback = whisper_progress_cb; + params.progress_callback_user_data = (void*)(ctx); + return params; +} +*/ +import "C" + +/////////////////////////////////////////////////////////////////////////////// +// TYPES + +type ( + Context C.struct_whisper_context + Token C.whisper_token + TokenData C.struct_whisper_token_data + SamplingStrategy C.enum_whisper_sampling_strategy + Params C.struct_whisper_full_params +) + +/////////////////////////////////////////////////////////////////////////////// +// GLOBALS + +const ( + SAMPLING_GREEDY SamplingStrategy = C.WHISPER_SAMPLING_GREEDY + SAMPLING_BEAM_SEARCH SamplingStrategy = C.WHISPER_SAMPLING_BEAM_SEARCH +) + +const ( + SampleRate = C.WHISPER_SAMPLE_RATE // Expected sample rate, samples per second + SampleBits = uint16(unsafe.Sizeof(C.float(0))) * 8 // Sample size in bits + NumFFT = C.WHISPER_N_FFT + HopLength = C.WHISPER_HOP_LENGTH + ChunkSize = C.WHISPER_CHUNK_SIZE +) + +var ( + ErrTokenizerFailed = errors.New("whisper_tokenize failed") + ErrAutoDetectFailed = errors.New("whisper_lang_auto_detect failed") + ErrConversionFailed = errors.New("whisper_convert failed") + ErrInvalidLanguage = errors.New("invalid language") +) + +/////////////////////////////////////////////////////////////////////////////// +// PUBLIC METHODS + +// Allocates all memory needed for the model and loads the model from the given file. +// Returns NULL on failure. +func Whisper_init(path string) *Context { + cPath := C.CString(path) + defer C.free(unsafe.Pointer(cPath)) + if ctx := C.whisper_init_from_file_with_params(cPath, C.whisper_context_default_params()); ctx != nil { + return (*Context)(ctx) + } else { + return nil + } +} + +// Frees all memory allocated by the model. +func (ctx *Context) Whisper_free() { + C.whisper_free((*C.struct_whisper_context)(ctx)) +} + +// Convert RAW PCM audio to log mel spectrogram. +// The resulting spectrogram is stored inside the provided whisper context. +func (ctx *Context) Whisper_pcm_to_mel(data []float32, threads int) error { + if C.whisper_pcm_to_mel((*C.struct_whisper_context)(ctx), (*C.float)(&data[0]), C.int(len(data)), C.int(threads)) == 0 { + return nil + } else { + return ErrConversionFailed + } +} + +// This can be used to set a custom log mel spectrogram inside the provided whisper context. +// Use this instead of whisper_pcm_to_mel() if you want to provide your own log mel spectrogram. +// n_mel must be 80 +func (ctx *Context) Whisper_set_mel(data []float32, n_mel int) error { + if C.whisper_set_mel((*C.struct_whisper_context)(ctx), (*C.float)(&data[0]), C.int(len(data)), C.int(n_mel)) == 0 { + return nil + } else { + return ErrConversionFailed + } +} + +// Run the Whisper encoder on the log mel spectrogram stored inside the provided whisper context. +// Make sure to call whisper_pcm_to_mel() or whisper_set_mel() first. +// offset can be used to specify the offset of the first frame in the spectrogram. +func (ctx *Context) Whisper_encode(offset, threads int) error { + if C.whisper_encode((*C.struct_whisper_context)(ctx), C.int(offset), C.int(threads)) == 0 { + return nil + } else { + return ErrConversionFailed + } +} + +// Run the Whisper decoder to obtain the logits and probabilities for the next token. +// Make sure to call whisper_encode() first. +// tokens + n_tokens is the provided context for the decoder. +// n_past is the number of tokens to use from previous decoder calls. +func (ctx *Context) Whisper_decode(tokens []Token, past, threads int) error { + if C.whisper_decode((*C.struct_whisper_context)(ctx), (*C.whisper_token)(&tokens[0]), C.int(len(tokens)), C.int(past), C.int(threads)) == 0 { + return nil + } else { + return ErrConversionFailed + } +} + +// Convert the provided text into tokens. The tokens pointer must be large enough to hold the resulting tokens. +// Returns the number of tokens on success +func (ctx *Context) Whisper_tokenize(text string, tokens []Token) (int, error) { + cText := C.CString(text) + defer C.free(unsafe.Pointer(cText)) + if n := C.whisper_tokenize((*C.struct_whisper_context)(ctx), cText, (*C.whisper_token)(&tokens[0]), C.int(len(tokens))); n >= 0 { + return int(n), nil + } else { + return 0, ErrTokenizerFailed + } +} + +// Return the id of the specified language, returns -1 if not found +// Examples: +// +// "de" -> 2 +// "german" -> 2 +func (ctx *Context) Whisper_lang_id(lang string) int { + return int(C.whisper_lang_id(C.CString(lang))) +} + +// Largest language id (i.e. number of available languages - 1) +func Whisper_lang_max_id() int { + return int(C.whisper_lang_max_id()) +} + +// Return the short string of the specified language id (e.g. 2 -> "de"), +// returns empty string if not found +func Whisper_lang_str(id int) string { + return C.GoString(C.whisper_lang_str(C.int(id))) +} + +// Use mel data at offset_ms to try and auto-detect the spoken language +// Make sure to call whisper_pcm_to_mel() or whisper_set_mel() first. +// Returns the probabilities of all languages. +// ref: https://github.com/openai/whisper/blob/main/whisper/decoding.py#L18-L69 +func (ctx *Context) Whisper_lang_auto_detect(offset_ms, n_threads int) ([]float32, error) { + probs := make([]float32, Whisper_lang_max_id()+1) + if n := int(C.whisper_lang_auto_detect((*C.struct_whisper_context)(ctx), C.int(offset_ms), C.int(n_threads), (*C.float)(&probs[0]))); n < 0 { + return nil, ErrAutoDetectFailed + } else { + return probs, nil + } +} + +func (ctx *Context) Whisper_n_len() int { + return int(C.whisper_n_len((*C.struct_whisper_context)(ctx))) +} + +func (ctx *Context) Whisper_n_vocab() int { + return int(C.whisper_n_vocab((*C.struct_whisper_context)(ctx))) +} + +func (ctx *Context) Whisper_n_text_ctx() int { + return int(C.whisper_n_text_ctx((*C.struct_whisper_context)(ctx))) +} + +func (ctx *Context) Whisper_n_audio_ctx() int { + return int(C.whisper_n_audio_ctx((*C.struct_whisper_context)(ctx))) +} + +func (ctx *Context) Whisper_is_multilingual() int { + return int(C.whisper_is_multilingual((*C.struct_whisper_context)(ctx))) +} + +// The probabilities for the next token +//func (ctx *Whisper_context) Whisper_get_probs() []float32 { +// return (*[1 << 30]float32)(unsafe.Pointer(C.whisper_get_probs((*C.struct_whisper_context)(ctx))))[:ctx.Whisper_n_vocab()] +//} + +// Token Id -> String. Uses the vocabulary in the provided context +func (ctx *Context) Whisper_token_to_str(token Token) string { + return C.GoString(C.whisper_token_to_str((*C.struct_whisper_context)(ctx), C.whisper_token(token))) +} + +// Special tokens +func (ctx *Context) Whisper_token_eot() Token { + return Token(C.whisper_token_eot((*C.struct_whisper_context)(ctx))) +} + +// Special tokens +func (ctx *Context) Whisper_token_sot() Token { + return Token(C.whisper_token_sot((*C.struct_whisper_context)(ctx))) +} + +// Special tokens +func (ctx *Context) Whisper_token_prev() Token { + return Token(C.whisper_token_prev((*C.struct_whisper_context)(ctx))) +} + +// Special tokens +func (ctx *Context) Whisper_token_solm() Token { + return Token(C.whisper_token_solm((*C.struct_whisper_context)(ctx))) +} + +// Special tokens +func (ctx *Context) Whisper_token_not() Token { + return Token(C.whisper_token_not((*C.struct_whisper_context)(ctx))) +} + +// Special tokens +func (ctx *Context) Whisper_token_beg() Token { + return Token(C.whisper_token_beg((*C.struct_whisper_context)(ctx))) +} + +// Special tokens +func (ctx *Context) Whisper_token_lang(lang_id int) Token { + return Token(C.whisper_token_lang((*C.struct_whisper_context)(ctx), C.int(lang_id))) +} + +// Task tokens +func (ctx *Context) Whisper_token_translate() Token { + return Token(C.whisper_token_translate((*C.struct_whisper_context)(ctx))) +} + +// Task tokens +func (ctx *Context) Whisper_token_transcribe() Token { + return Token(C.whisper_token_transcribe((*C.struct_whisper_context)(ctx))) +} + +// Performance information +func (ctx *Context) Whisper_print_timings() { + C.whisper_print_timings((*C.struct_whisper_context)(ctx)) +} + +// Performance information +func (ctx *Context) Whisper_reset_timings() { + C.whisper_reset_timings((*C.struct_whisper_context)(ctx)) +} + +// Print system information +func Whisper_print_system_info() string { + return C.GoString(C.whisper_print_system_info()) +} + +// Return default parameters for a strategy +func (ctx *Context) Whisper_full_default_params(strategy SamplingStrategy) Params { + // Get default parameters + return Params(C.whisper_full_default_params_cb((*C.struct_whisper_context)(ctx), C.enum_whisper_sampling_strategy(strategy))) +} + +// Run the entire model: PCM -> log mel spectrogram -> encoder -> decoder -> text +// Uses the specified decoding strategy to obtain the text. +func (ctx *Context) Whisper_full( + params Params, + samples []float32, + encoderBeginCallback func() bool, + newSegmentCallback func(int), + progressCallback func(int), +) error { + registerEncoderBeginCallback(ctx, encoderBeginCallback) + registerNewSegmentCallback(ctx, newSegmentCallback) + registerProgressCallback(ctx, progressCallback) + defer registerEncoderBeginCallback(ctx, nil) + defer registerNewSegmentCallback(ctx, nil) + defer registerProgressCallback(ctx, nil) + if C.whisper_full((*C.struct_whisper_context)(ctx), (C.struct_whisper_full_params)(params), (*C.float)(&samples[0]), C.int(len(samples))) == 0 { + return nil + } else { + return ErrConversionFailed + } +} + +// Split the input audio in chunks and process each chunk separately using whisper_full() +// It seems this approach can offer some speedup in some cases. +// However, the transcription accuracy can be worse at the beginning and end of each chunk. +func (ctx *Context) Whisper_full_parallel(params Params, samples []float32, processors int, encoderBeginCallback func() bool, newSegmentCallback func(int)) error { + registerEncoderBeginCallback(ctx, encoderBeginCallback) + registerNewSegmentCallback(ctx, newSegmentCallback) + defer registerEncoderBeginCallback(ctx, nil) + defer registerNewSegmentCallback(ctx, nil) + + if C.whisper_full_parallel((*C.struct_whisper_context)(ctx), (C.struct_whisper_full_params)(params), (*C.float)(&samples[0]), C.int(len(samples)), C.int(processors)) == 0 { + return nil + } else { + return ErrConversionFailed + } +} + +// Return the id of the autodetected language, returns -1 if not found +// Added to whisper.cpp in +// https://github.com/ggerganov/whisper.cpp/commit/a1c1583cc7cd8b75222857afc936f0638c5683d6 +// +// Examples: +// +// "de" -> 2 +// "german" -> 2 +func (ctx *Context) Whisper_full_lang_id() int { + return int(C.whisper_full_lang_id((*C.struct_whisper_context)(ctx))) +} + +// Number of generated text segments. +// A segment can be a few words, a sentence, or even a paragraph. +func (ctx *Context) Whisper_full_n_segments() int { + return int(C.whisper_full_n_segments((*C.struct_whisper_context)(ctx))) +} + +// Get the start and end time of the specified segment. +func (ctx *Context) Whisper_full_get_segment_t0(segment int) int64 { + return int64(C.whisper_full_get_segment_t0((*C.struct_whisper_context)(ctx), C.int(segment))) +} + +// Get the start and end time of the specified segment. +func (ctx *Context) Whisper_full_get_segment_t1(segment int) int64 { + return int64(C.whisper_full_get_segment_t1((*C.struct_whisper_context)(ctx), C.int(segment))) +} + +// Get the text of the specified segment. +func (ctx *Context) Whisper_full_get_segment_text(segment int) string { + return C.GoString(C.whisper_full_get_segment_text((*C.struct_whisper_context)(ctx), C.int(segment))) +} + +// Get number of tokens in the specified segment. +func (ctx *Context) Whisper_full_n_tokens(segment int) int { + return int(C.whisper_full_n_tokens((*C.struct_whisper_context)(ctx), C.int(segment))) +} + +// Get the token text of the specified token index in the specified segment. +func (ctx *Context) Whisper_full_get_token_text(segment int, token int) string { + return C.GoString(C.whisper_full_get_token_text((*C.struct_whisper_context)(ctx), C.int(segment), C.int(token))) +} + +// Get the token of the specified token index in the specified segment. +func (ctx *Context) Whisper_full_get_token_id(segment int, token int) Token { + return Token(C.whisper_full_get_token_id((*C.struct_whisper_context)(ctx), C.int(segment), C.int(token))) +} + +// Get token data for the specified token in the specified segment. +// This contains probabilities, timestamps, etc. +func (ctx *Context) Whisper_full_get_token_data(segment int, token int) TokenData { + return TokenData(C.whisper_full_get_token_data((*C.struct_whisper_context)(ctx), C.int(segment), C.int(token))) +} + +// Get the probability of the specified token in the specified segment. +func (ctx *Context) Whisper_full_get_token_p(segment int, token int) float32 { + return float32(C.whisper_full_get_token_p((*C.struct_whisper_context)(ctx), C.int(segment), C.int(token))) +} + +/////////////////////////////////////////////////////////////////////////////// +// CALLBACKS + +var ( + cbNewSegment = make(map[unsafe.Pointer]func(int)) + cbProgress = make(map[unsafe.Pointer]func(int)) + cbEncoderBegin = make(map[unsafe.Pointer]func() bool) +) + +func registerNewSegmentCallback(ctx *Context, fn func(int)) { + if fn == nil { + delete(cbNewSegment, unsafe.Pointer(ctx)) + } else { + cbNewSegment[unsafe.Pointer(ctx)] = fn + } +} + +func registerProgressCallback(ctx *Context, fn func(int)) { + if fn == nil { + delete(cbProgress, unsafe.Pointer(ctx)) + } else { + cbProgress[unsafe.Pointer(ctx)] = fn + } +} + +func registerEncoderBeginCallback(ctx *Context, fn func() bool) { + if fn == nil { + delete(cbEncoderBegin, unsafe.Pointer(ctx)) + } else { + cbEncoderBegin[unsafe.Pointer(ctx)] = fn + } +} + +//export callNewSegment +func callNewSegment(user_data unsafe.Pointer, new C.int) { + if fn, ok := cbNewSegment[user_data]; ok { + fn(int(new)) + } +} + +//export callProgress +func callProgress(user_data unsafe.Pointer, progress C.int) { + if fn, ok := cbProgress[user_data]; ok { + fn(int(progress)) + } +} + +//export callEncoderBegin +func callEncoderBegin(user_data unsafe.Pointer) C.bool { + if fn, ok := cbEncoderBegin[user_data]; ok { + if fn() { + return C.bool(true) + } else { + return C.bool(false) + } + } + return true +} + +func (t TokenData) T0() int64 { + return int64(t.t0) +} + +func (t TokenData) T1() int64 { + return int64(t.t1) +} + +func (t TokenData) Id() Token { + return Token(t.id) +} diff --git a/bindings/go/whisper_test.go b/bindings/go/whisper_test.go new file mode 100644 index 0000000..40648ff --- /dev/null +++ b/bindings/go/whisper_test.go @@ -0,0 +1,113 @@ +package whisper_test + +import ( + "os" + "runtime" + "testing" + "time" + + // Packages + whisper "github.com/ggerganov/whisper.cpp/bindings/go" + wav "github.com/go-audio/wav" + assert "github.com/stretchr/testify/assert" +) + +const ( + ModelPath = "models/ggml-small.en.bin" + SamplePath = "samples/jfk.wav" +) + +func Test_Whisper_000(t *testing.T) { + assert := assert.New(t) + if _, err := os.Stat(ModelPath); os.IsNotExist(err) { + t.Skip("Skipping test, model not found:", ModelPath) + } + ctx := whisper.Whisper_init(ModelPath) + assert.NotNil(ctx) + ctx.Whisper_free() +} + +func Test_Whisper_001(t *testing.T) { + assert := assert.New(t) + if _, err := os.Stat(ModelPath); os.IsNotExist(err) { + t.Skip("Skipping test, model not found:", ModelPath) + } + if _, err := os.Stat(SamplePath); os.IsNotExist(err) { + t.Skip("Skipping test, sample not found:", SamplePath) + } + + // Open samples + fh, err := os.Open(SamplePath) + assert.NoError(err) + defer fh.Close() + + // Read samples + d := wav.NewDecoder(fh) + buf, err := d.FullPCMBuffer() + assert.NoError(err) + + // Run whisper + ctx := whisper.Whisper_init(ModelPath) + assert.NotNil(ctx) + defer ctx.Whisper_free() + params := ctx.Whisper_full_default_params(whisper.SAMPLING_GREEDY) + data := buf.AsFloat32Buffer().Data + err = ctx.Whisper_full(params, data, nil, nil, nil) + assert.NoError(err) + + // Print out tokens + num_segments := ctx.Whisper_full_n_segments() + assert.GreaterOrEqual(num_segments, 1) + for i := 0; i < num_segments; i++ { + str := ctx.Whisper_full_get_segment_text(i) + assert.NotEmpty(str) + t0 := time.Duration(ctx.Whisper_full_get_segment_t0(i)) * time.Millisecond + t1 := time.Duration(ctx.Whisper_full_get_segment_t1(i)) * time.Millisecond + t.Logf("[%6s->%-6s] %q", t0, t1, str) + } +} + +func Test_Whisper_002(t *testing.T) { + assert := assert.New(t) + for i := 0; i < whisper.Whisper_lang_max_id(); i++ { + str := whisper.Whisper_lang_str(i) + assert.NotEmpty(str) + t.Log(str) + } +} + +func Test_Whisper_003(t *testing.T) { + threads := runtime.NumCPU() + assert := assert.New(t) + if _, err := os.Stat(ModelPath); os.IsNotExist(err) { + t.Skip("Skipping test, model not found:", ModelPath) + } + if _, err := os.Stat(SamplePath); os.IsNotExist(err) { + t.Skip("Skipping test, sample not found:", SamplePath) + } + + // Open samples + fh, err := os.Open(SamplePath) + assert.NoError(err) + defer fh.Close() + + // Read samples + d := wav.NewDecoder(fh) + buf, err := d.FullPCMBuffer() + assert.NoError(err) + + // Make the model + ctx := whisper.Whisper_init(ModelPath) + assert.NotNil(ctx) + defer ctx.Whisper_free() + + // Get MEL + assert.NoError(ctx.Whisper_pcm_to_mel(buf.AsFloat32Buffer().Data, threads)) + + // Get Languages + languages, err := ctx.Whisper_lang_auto_detect(0, threads) + assert.NoError(err) + for i, p := range languages { + t.Logf("%s: %f", whisper.Whisper_lang_str(i), p) + } +} diff --git a/bindings/java/README.md b/bindings/java/README.md new file mode 100644 index 0000000..9042699 --- /dev/null +++ b/bindings/java/README.md @@ -0,0 +1,87 @@ +# Java JNI bindings for Whisper + +This package provides Java JNI bindings for whisper.cpp. They have been tested on: + + * <strike>Darwin (OS X) 12.6 on x64_64</strike> + * Ubuntu on x86_64 + * Windows on x86_64 + +The "low level" bindings are in `WhisperCppJnaLibrary`. The most simple usage is as follows: + +JNA will attempt to load the `whispercpp` shared library from: + +- jna.library.path +- jna.platform.library +- ~/Library/Frameworks +- /Library/Frameworks +- /System/Library/Frameworks +- classpath + +```java +import io.github.ggerganov.whispercpp.WhisperCpp; + +public class Example { + + public static void main(String[] args) { + + WhisperCpp whisper = new WhisperCpp(); + try { + // By default, models are loaded from ~/.cache/whisper/ and are usually named "ggml-${name}.bin" + // or you can provide the absolute path to the model file. + whisper.initContext("../ggml-base.en.bin"); + WhisperFullParams.ByValue whisperParams = whisper.getFullDefaultParams(WhisperSamplingStrategy.WHISPER_SAMPLING_BEAM_SEARCH); + + // custom configuration if required + //whisperParams.n_threads = 8; + whisperParams.temperature = 0.0f; + whisperParams.temperature_inc = 0.2f; + //whisperParams.language = "en"; + + float[] samples = readAudio(); // divide each value by 32767.0f + List<WhisperSegment> whisperSegmentList = whisper.fullTranscribeWithTime(whisperParams, samples); + + for (WhisperSegment whisperSegment : whisperSegmentList) { + + long start = whisperSegment.getStart(); + long end = whisperSegment.getEnd(); + + String text = whisperSegment.getSentence(); + + System.out.println("start: "+start); + System.out.println("end: "+end); + System.out.println("text: "+text); + + } + + } catch (IOException e) { + e.printStackTrace(); + } finally { + whisper.close(); + } + + } +} +``` + +## Building & Testing + +In order to build, you need to have the JDK 8 or higher installed. Run the tests with: + +```bash +git clone https://github.com/ggml-org/whisper.cpp.git +cd whisper.cpp/bindings/java + +./gradlew build +``` + +You need to have the `whisper` library in your [JNA library path](https://java-native-access.github.io/jna/4.2.1/com/sun/jna/NativeLibrary.html). On Windows the dll is included in the jar and you can update it: + +```bash +copy /y ..\..\build\bin\Release\whisper.dll build\generated\resources\main\win32-x86-64\whisper.dll +``` + + +## License + +The license for the Java bindings is the same as the license for the rest of the whisper.cpp project, which is the MIT License. See the `LICENSE` file for more details. + diff --git a/bindings/java/build.gradle b/bindings/java/build.gradle new file mode 100644 index 0000000..30184ee --- /dev/null +++ b/bindings/java/build.gradle @@ -0,0 +1,159 @@ +plugins { + id 'java' + id 'java-library' + id 'maven-publish' + id 'signing' +} + +archivesBaseName = 'whispercpp' +group = 'io.github.ggerganov' +version = '1.4.0' + + +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + +sourceSets { + main { + resources { + srcDirs = ['src/main/resources', 'build/generated/resources/main'] + } + } + test { + runtimeClasspath += files('build/generated/resources/main') + } +} + +tasks.register('copyLibwhisperDynlib', Copy) { + from '../../build/src' + include 'libwhisper.dylib' + into 'build/generated/resources/main' +} + +tasks.register('copyLibwhisperSo', Copy) { + from '../../build/src' + include 'libwhisper.so' + into 'build/generated/resources/main' +} + +tasks.register('copyWhisperDLL', Copy) { + from '../../build/bin/Release' + include 'whisper.dll' + into 'build/generated/resources/main' +} + +tasks.register('copyGGML_BASE_DLL', Copy) { + from '../../build/bin/Release' + include 'ggml-base.dll' + into 'build/generated/resources/main' +} + +tasks.register('copyGGML_DLL', Copy) { + from '../../build/bin/Release' + include 'ggml.dll' + into 'build/generated/resources/main' +} + +tasks.register('copyGGML_CPU_DLL', Copy) { + from '../../build/bin/Release' + include 'ggml-cpu.dll' + into 'build/generated/resources/main' +} + +tasks.register('copyLibs') { + dependsOn copyLibwhisperDynlib, copyLibwhisperSo, copyWhisperDLL, copyGGML_BASE_DLL, copyGGML_DLL, copyGGML_CPU_DLL +} + +test { + systemProperty 'jna.library.path', project.file('build/generated/resources/main').absolutePath +} + +java { + withSourcesJar() + withJavadocJar() +} + +sourcesJar() { + dependsOn copyLibs +} + +jar { + dependsOn copyLibs + exclude '**/whisper_java.exp', '**/whisper_java.lib' +} + +javadoc { + options.addStringOption('Xdoclint:none', '-quiet') +} + +tasks.withType(Test) { + useJUnitPlatform() +} + +test.dependsOn copyLibs +processResources.dependsOn copyLibs + +dependencies { + implementation "net.java.dev.jna:jna:5.13.0" + testImplementation "org.junit.jupiter:junit-jupiter:5.9.2" + testImplementation "org.assertj:assertj-core:3.24.2" +} + +repositories { + mavenCentral() +} + +publishing { + publications { + mavenJava(MavenPublication) { + artifactId = 'whispercpp' + from components.java + pom { + name = 'whispercpp' + description = "Java JNA bindings for OpenAI's Whisper model, implemented in C/C++" + url = 'https://github.com/ggerganov/whisper.cpp' + licenses { + license { + name = 'MIT licence' + url = 'https://raw.githubusercontent.com/ggerganov/whisper.cpp/master/LICENSE' + } + } + developers { + developer { + id = 'ggerganov' + name = 'Georgi Gerganov' + email = 'ggerganov@gmail.com' + } + developer { + id = 'nalbion' + name = 'Nicholas Albion' + email = 'nalbion@yahoo.com' + } + } + scm { + connection = 'scm:git:git://github.com/ggerganov/whisper.cpp.git' + url = 'https://github.com/ggerganov/whisper.cpp' + } + } + } + } + + repositories { + maven { + def releasesRepoUrl = 'https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/' + def snapshotsRepoUrl = 'https://s01.oss.sonatype.org/content/repositories/snapshots/' + url = version.endsWith('-SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl + credentials { + username = System.getenv("MAVEN_USERNAME") + password = System.getenv("MAVEN_PASSWORD") + } + } + } +} + +signing { + def signingKey = System.getenv("PGP_SECRET") + def signingPassword = System.getenv("PGP_PASSPHRASE") + useInMemoryPgpKeys(signingKey, signingPassword) + sign publishing.publications.mavenJava +} diff --git a/bindings/java/gradle.properties b/bindings/java/gradle.properties new file mode 100644 index 0000000..3ea68c2 --- /dev/null +++ b/bindings/java/gradle.properties @@ -0,0 +1,6 @@ +org.gradle.jvmargs=-Xms256m -Xmx1024m +system.include.dir=/usr/include +#system.local.include.dir=../../include +system.local.include.dir=./build/generated/sources/headers/java/main +jni.include.dir=/usr/lib/jvm/java-8-openjdk-amd64/include/ +jni.lib.dir=/usr/lib/jvm/java-8-openjdk-amd64/lib/ diff --git a/bindings/java/gradle/wrapper/gradle-wrapper.jar b/bindings/java/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..ccebba7 Binary files /dev/null and b/bindings/java/gradle/wrapper/gradle-wrapper.jar differ diff --git a/bindings/java/gradle/wrapper/gradle-wrapper.properties b/bindings/java/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..0c85a1f --- /dev/null +++ b/bindings/java/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/bindings/java/gradlew b/bindings/java/gradlew new file mode 100755 index 0000000..79a61d4 --- /dev/null +++ b/bindings/java/gradlew @@ -0,0 +1,244 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# 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 +# +# https://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. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/bindings/java/gradlew.bat b/bindings/java/gradlew.bat new file mode 100644 index 0000000..6689b85 --- /dev/null +++ b/bindings/java/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/bindings/java/settings.gradle b/bindings/java/settings.gradle new file mode 100644 index 0000000..dbc6f38 --- /dev/null +++ b/bindings/java/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "whispercpp" diff --git a/bindings/java/src/main/java/io/github/ggerganov/whispercpp/WhisperConstants.java b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/WhisperConstants.java new file mode 100644 index 0000000..0c828f1 --- /dev/null +++ b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/WhisperConstants.java @@ -0,0 +1,24 @@ +package io.github.ggerganov.whispercpp; + +/** + * Presets for alignment heads in DTW token timestamps + */ +public class WhisperConstants { + // Alignment heads presets + public static final int WHISPER_AHEADS_NONE = 0; + public static final int WHISPER_AHEADS_TINY_EN = 1; + public static final int WHISPER_AHEADS_TINY = 2; + public static final int WHISPER_AHEADS_BASE_EN = 3; + public static final int WHISPER_AHEADS_BASE = 4; + public static final int WHISPER_AHEADS_SMALL_EN = 5; + public static final int WHISPER_AHEADS_SMALL = 6; + public static final int WHISPER_AHEADS_MEDIUM_EN = 7; + public static final int WHISPER_AHEADS_MEDIUM = 8; + public static final int WHISPER_AHEADS_LARGE_V1 = 9; + public static final int WHISPER_AHEADS_LARGE_V2 = 10; + public static final int WHISPER_AHEADS_LARGE_V3 = 11; + public static final int WHISPER_AHEADS_LARGE_V3_TURBO = 12; + public static final int WHISPER_AHEADS_CUSTOM = 13; + public static final int WHISPER_AHEADS_N_TOP_MOST = 14; + public static final int WHISPER_AHEADS_COUNT = 15; +} diff --git a/bindings/java/src/main/java/io/github/ggerganov/whispercpp/WhisperContext.java b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/WhisperContext.java new file mode 100644 index 0000000..7ac124e --- /dev/null +++ b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/WhisperContext.java @@ -0,0 +1,36 @@ +package io.github.ggerganov.whispercpp; + +import com.sun.jna.NativeLong; +import com.sun.jna.Structure; +import com.sun.jna.ptr.PointerByReference; +import com.sun.jna.Pointer; +import io.github.ggerganov.whispercpp.ggml.GgmlType; +import io.github.ggerganov.whispercpp.WhisperModel; +import io.github.ggerganov.whispercpp.params.WhisperContextParams; + +import java.util.List; + +public class WhisperContext extends Structure { + public NativeLong t_load_us; + public NativeLong t_start_us; + + /** weight type (FP32 / FP16 / QX) */ + public GgmlType wtype = GgmlType.GGML_TYPE_F16; + /** intermediate type (FP32 or FP16) */ + public GgmlType itype = GgmlType.GGML_TYPE_F16; + + public WhisperContextParams.ByValue params; + + public Pointer model; + public Pointer vocab; + public Pointer state; + + /** populated by whisper_init_from_file_with_params() */ + public Pointer path_model; + + @Override + protected List<String> getFieldOrder() { + return List.of("t_load_us", "t_start_us", "wtype", "itype", + "params", "model", "vocab", "state", "path_model"); + } +} diff --git a/bindings/java/src/main/java/io/github/ggerganov/whispercpp/WhisperCpp.java b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/WhisperCpp.java new file mode 100644 index 0000000..cc53148 --- /dev/null +++ b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/WhisperCpp.java @@ -0,0 +1,219 @@ +package io.github.ggerganov.whispercpp; + +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import io.github.ggerganov.whispercpp.bean.WhisperSegment; +import io.github.ggerganov.whispercpp.params.WhisperContextParams; +import io.github.ggerganov.whispercpp.params.WhisperFullParams; +import io.github.ggerganov.whispercpp.params.WhisperSamplingStrategy; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * Before calling most methods, you must call `initContext(modelPath)` to initialise the `ctx` Pointer. + */ +public class WhisperCpp implements AutoCloseable { + private WhisperCppJnaLibrary lib = WhisperCppJnaLibrary.instance; + private Pointer ctx = null; + private Pointer paramsPointer = null; + private Pointer greedyParamsPointer = null; + private Pointer beamParamsPointer = null; + + public File modelDir() { + String modelDirPath = System.getenv("XDG_CACHE_HOME"); + if (modelDirPath == null) { + modelDirPath = System.getProperty("user.home") + "/.cache"; + } + + return new File(modelDirPath, "whisper"); + } + + /** + * @param modelPath - absolute path, or just the name (eg: "base", "base-en" or "base.en") + */ + public void initContext(String modelPath) throws FileNotFoundException { + initContextImpl(modelPath, getContextDefaultParams()); + } + + /** + * @param modelPath - absolute path, or just the name (eg: "base", "base-en" or "base.en") + * @param params - params to use when initialising the context + */ + public void initContext(String modelPath, WhisperContextParams.ByValue params) throws FileNotFoundException { + initContextImpl(modelPath, params); + } + + private void initContextImpl(String modelPath, WhisperContextParams.ByValue params) throws FileNotFoundException { + if (ctx != null) { + lib.whisper_free(ctx); + } + + if (!modelPath.contains("/") && !modelPath.contains("\\")) { + if (!modelPath.endsWith(".bin")) { + modelPath = "ggml-" + modelPath.replace("-", ".") + ".bin"; + } + + modelPath = new File(modelDir(), modelPath).getAbsolutePath(); + } + + ctx = lib.whisper_init_from_file_with_params(modelPath, params); + + if (ctx == null) { + throw new FileNotFoundException(modelPath); + } + } + + /** + * Provides default params which can be used with `whisper_init_from_file_with_params()` etc. + * Returns a ByValue instance to ensure proper parameter passing to native code. + */ + public WhisperContextParams.ByValue getContextDefaultParams() { + WhisperContextParams.ByValue valueParams = new WhisperContextParams.ByValue( + lib.whisper_context_default_params_by_ref()); + valueParams.read(); + return valueParams; + } + + /** + * Provides default params which can be used with `whisper_full()` etc. + * Because this function allocates memory for the params, the caller must call either: + * - call `whisper_free_params()` + * - `Native.free(Pointer.nativeValue(pointer));` + * + * @param strategy - GREEDY + */ + public WhisperFullParams.ByValue getFullDefaultParams(WhisperSamplingStrategy strategy) { + Pointer pointer; + + // whisper_full_default_params_by_ref allocates memory which we need to delete, so only create max 1 pointer for each strategy. + if (strategy == WhisperSamplingStrategy.WHISPER_SAMPLING_GREEDY) { + if (greedyParamsPointer == null) { + greedyParamsPointer = lib.whisper_full_default_params_by_ref(strategy.ordinal()); + } + pointer = greedyParamsPointer; + } else { + if (beamParamsPointer == null) { + beamParamsPointer = lib.whisper_full_default_params_by_ref(strategy.ordinal()); + } + pointer = beamParamsPointer; + } + + WhisperFullParams.ByValue params = new WhisperFullParams.ByValue(pointer); + params.read(); + return params; + } + + @Override + public void close() { + freeContext(); + freeParams(); + System.out.println("Whisper closed"); + } + + private void freeContext() { + if (ctx != null) { + lib.whisper_free(ctx); + } + } + + private void freeParams() { + if (paramsPointer != null) { + Native.free(Pointer.nativeValue(paramsPointer)); + paramsPointer = null; + } + if (greedyParamsPointer != null) { + Native.free(Pointer.nativeValue(greedyParamsPointer)); + greedyParamsPointer = null; + } + if (beamParamsPointer != null) { + Native.free(Pointer.nativeValue(beamParamsPointer)); + beamParamsPointer = null; + } + } + + /** + * Run the entire model: PCM -> log mel spectrogram -> encoder -> decoder -> text. + * Not thread safe for same context + * Uses the specified decoding strategy to obtain the text. + */ + public String fullTranscribe(WhisperFullParams.ByValue whisperParams, float[] audioData) throws IOException { + if (ctx == null) { + throw new IllegalStateException("Model not initialised"); + } + + /* + WhisperFullParams.ByValue valueParams = new WhisperFullParams.ByValue( + lib.whisper_full_default_params_by_ref(WhisperSamplingStrategy.WHISPER_SAMPLING_BEAM_SEARCH.ordinal())); + valueParams.read(); + */ + + if (lib.whisper_full(ctx, whisperParams, audioData, audioData.length) != 0) { + throw new IOException("Failed to process audio"); + } + + int nSegments = lib.whisper_full_n_segments(ctx); + + StringBuilder str = new StringBuilder(); + + for (int i = 0; i < nSegments; i++) { + String text = lib.whisper_full_get_segment_text(ctx, i); + System.out.println("Segment:" + text); + str.append(text); + } + + return str.toString().trim(); + } + + /** + * Full transcribe with time list. + * + * @param whisperParams the whisper params + * @param audioData the audio data + * @return the list + * @throws IOException the io exception + */ + public List<WhisperSegment> fullTranscribeWithTime(WhisperFullParams.ByValue whisperParams, float[] audioData) throws IOException { + if (ctx == null) { + throw new IllegalStateException("Model not initialised"); + } + + if (lib.whisper_full(ctx, whisperParams, audioData, audioData.length) != 0) { + throw new IOException("Failed to process audio"); + } + + int nSegments = lib.whisper_full_n_segments(ctx); + List<WhisperSegment> segments= new ArrayList<>(nSegments); + + for (int i = 0; i < nSegments; i++) { + long t0 = lib.whisper_full_get_segment_t0(ctx, i); + String text = lib.whisper_full_get_segment_text(ctx, i); + long t1 = lib.whisper_full_get_segment_t1(ctx, i); + segments.add(new WhisperSegment(t0,t1,text)); + } + + return segments; + } + +// public int getTextSegmentCount(Pointer ctx) { +// return lib.whisper_full_n_segments(ctx); +// } +// public String getTextSegment(Pointer ctx, int index) { +// return lib.whisper_full_get_segment_text(ctx, index); +// } + + public String getSystemInfo() { + return lib.whisper_print_system_info(); + } + + public int benchMemcpy(int nthread) { + return lib.whisper_bench_memcpy(nthread); + } + + public int benchGgmlMulMat(int nthread) { + return lib.whisper_bench_ggml_mul_mat(nthread); + } +} diff --git a/bindings/java/src/main/java/io/github/ggerganov/whispercpp/WhisperCppJnaLibrary.java b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/WhisperCppJnaLibrary.java new file mode 100644 index 0000000..690f1bd --- /dev/null +++ b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/WhisperCppJnaLibrary.java @@ -0,0 +1,390 @@ +package io.github.ggerganov.whispercpp; + +import com.sun.jna.Library; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import io.github.ggerganov.whispercpp.model.WhisperModelLoader; +import io.github.ggerganov.whispercpp.model.WhisperTokenData; +import io.github.ggerganov.whispercpp.params.WhisperContextParams; +import io.github.ggerganov.whispercpp.params.WhisperFullParams; + +public interface WhisperCppJnaLibrary extends Library { + + WhisperCppJnaLibrary instance = Native.load("whisper", WhisperCppJnaLibrary.class); + + String whisper_print_system_info(); + + /** + * DEPRECATED. Allocate (almost) all memory needed for the model by loading from a file. + * + * @param path_model Path to the model file + * @return Whisper context on success, null on failure + */ + Pointer whisper_init_from_file(String path_model); + + /** + * Provides default params which can be used with `whisper_init_from_file_with_params()` etc. + * Because this function allocates memory for the params, the caller must call either: + * - call `whisper_free_context_params()` + * - `Native.free(Pointer.nativeValue(pointer));` + */ + Pointer whisper_context_default_params_by_ref(); + + void whisper_free_context_params(Pointer params); + + /** + * Allocate (almost) all memory needed for the model by loading from a file. + * + * @param path_model Path to the model file + * @param params Pointer to whisper_context_params + * @return Whisper context on success, null on failure + */ + Pointer whisper_init_from_file_with_params(String path_model, WhisperContextParams.ByValue params); + + /** + * Allocate (almost) all memory needed for the model by loading from a buffer. + * + * @param buffer Model buffer + * @param buffer_size Size of the model buffer + * @return Whisper context on success, null on failure + */ + Pointer whisper_init_from_buffer(Pointer buffer, int buffer_size); + + /** + * Allocate (almost) all memory needed for the model using a model loader. + * + * @param loader Model loader + * @return Whisper context on success, null on failure + */ + Pointer whisper_init(WhisperModelLoader loader); + + /** + * Allocate (almost) all memory needed for the model by loading from a file without allocating the state. + * + * @param path_model Path to the model file + * @return Whisper context on success, null on failure + */ + Pointer whisper_init_from_file_no_state(String path_model); + + /** + * Allocate (almost) all memory needed for the model by loading from a buffer without allocating the state. + * + * @param buffer Model buffer + * @param buffer_size Size of the model buffer + * @return Whisper context on success, null on failure + */ + Pointer whisper_init_from_buffer_no_state(Pointer buffer, int buffer_size); + +// Pointer whisper_init_from_buffer_no_state(Pointer buffer, long buffer_size); + + /** + * Allocate (almost) all memory needed for the model using a model loader without allocating the state. + * + * @param loader Model loader + * @return Whisper context on success, null on failure + */ + Pointer whisper_init_no_state(WhisperModelLoader loader); + + /** + * Allocate memory for the Whisper state. + * + * @param ctx Whisper context + * @return Whisper state on success, null on failure + */ + Pointer whisper_init_state(Pointer ctx); + + /** + * Free all allocated memory associated with the Whisper context. + * + * @param ctx Whisper context + */ + void whisper_free(Pointer ctx); + + /** + * Free all allocated memory associated with the Whisper state. + * + * @param state Whisper state + */ + void whisper_free_state(Pointer state); + + + /** + * Convert RAW PCM audio to log mel spectrogram. + * The resulting spectrogram is stored inside the default state of the provided whisper context. + * + * @param ctx - Pointer to a WhisperContext + * @return 0 on success + */ + int whisper_pcm_to_mel(Pointer ctx, final float[] samples, int n_samples, int n_threads); + + /** + * @param ctx Pointer to a WhisperContext + * @param state Pointer to WhisperState + * @param n_samples + * @param n_threads + * @return 0 on success + */ + int whisper_pcm_to_mel_with_state(Pointer ctx, Pointer state, final float[] samples, int n_samples, int n_threads); + + /** + * This can be used to set a custom log mel spectrogram inside the default state of the provided whisper context. + * Use this instead of whisper_pcm_to_mel() if you want to provide your own log mel spectrogram. + * n_mel must be 80 + * @return 0 on success + */ + int whisper_set_mel(Pointer ctx, final float[] data, int n_len, int n_mel); + int whisper_set_mel_with_state(Pointer ctx, Pointer state, final float[] data, int n_len, int n_mel); + + /** + * Run the Whisper encoder on the log mel spectrogram stored inside the default state in the provided whisper context. + * Make sure to call whisper_pcm_to_mel() or whisper_set_mel() first. + * Offset can be used to specify the offset of the first frame in the spectrogram. + * @return 0 on success + */ + int whisper_encode(Pointer ctx, int offset, int n_threads); + + int whisper_encode_with_state(Pointer ctx, Pointer state, int offset, int n_threads); + + /** + * Run the Whisper decoder to obtain the logits and probabilities for the next token. + * Make sure to call whisper_encode() first. + * tokens + n_tokens is the provided context for the decoder. + * n_past is the number of tokens to use from previous decoder calls. + * Returns 0 on success + * TODO: add support for multiple decoders + */ + int whisper_decode(Pointer ctx, Pointer tokens, int n_tokens, int n_past, int n_threads); + + /** + * @param ctx + * @param state + * @param tokens Pointer to int tokens + * @param n_tokens + * @param n_past + * @param n_threads + * @return + */ + int whisper_decode_with_state(Pointer ctx, Pointer state, Pointer tokens, int n_tokens, int n_past, int n_threads); + + /** + * Convert the provided text into tokens. + * The tokens pointer must be large enough to hold the resulting tokens. + * Returns the number of tokens on success, no more than n_max_tokens + * Returns -1 on failure + * TODO: not sure if correct + */ + int whisper_tokenize(Pointer ctx, String text, Pointer tokens, int n_max_tokens); + + /** Largest language id (i.e. number of available languages - 1) */ + int whisper_lang_max_id(); + + /** + * @return the id of the specified language, returns -1 if not found. + * Examples: + * "de" -> 2 + * "german" -> 2 + */ + int whisper_lang_id(String lang); + + /** @return the short string of the specified language id (e.g. 2 -> "de"), returns nullptr if not found */ + String whisper_lang_str(int id); + + /** + * Use mel data at offset_ms to try and auto-detect the spoken language. + * Make sure to call whisper_pcm_to_mel() or whisper_set_mel() first + * Returns the top language id or negative on failure + * If not null, fills the lang_probs array with the probabilities of all languages + * The array must be whisper_lang_max_id() + 1 in size + * + * ref: https://github.com/openai/whisper/blob/main/whisper/decoding.py#L18-L69 + */ + int whisper_lang_auto_detect(Pointer ctx, int offset_ms, int n_threads, float[] lang_probs); + + int whisper_lang_auto_detect_with_state(Pointer ctx, Pointer state, int offset_ms, int n_threads, float[] lang_probs); + + int whisper_n_len (Pointer ctx); // mel length + int whisper_n_len_from_state(Pointer state); // mel length + int whisper_n_vocab (Pointer ctx); + int whisper_n_text_ctx (Pointer ctx); + int whisper_n_audio_ctx (Pointer ctx); + int whisper_is_multilingual (Pointer ctx); + + int whisper_model_n_vocab (Pointer ctx); + int whisper_model_n_audio_ctx (Pointer ctx); + int whisper_model_n_audio_state(Pointer ctx); + int whisper_model_n_audio_head (Pointer ctx); + int whisper_model_n_audio_layer(Pointer ctx); + int whisper_model_n_text_ctx (Pointer ctx); + int whisper_model_n_text_state (Pointer ctx); + int whisper_model_n_text_head (Pointer ctx); + int whisper_model_n_text_layer (Pointer ctx); + int whisper_model_n_mels (Pointer ctx); + int whisper_model_ftype (Pointer ctx); + int whisper_model_type (Pointer ctx); + + /** + * Token logits obtained from the last call to whisper_decode(). + * The logits for the last token are stored in the last row + * Rows: n_tokens + * Cols: n_vocab + */ + float[] whisper_get_logits (Pointer ctx); + float[] whisper_get_logits_from_state(Pointer state); + + // Token Id -> String. Uses the vocabulary in the provided context + String whisper_token_to_str(Pointer ctx, int token); + String whisper_model_type_readable(Pointer ctx); + + // Special tokens + int whisper_token_eot (Pointer ctx); + int whisper_token_sot (Pointer ctx); + int whisper_token_prev(Pointer ctx); + int whisper_token_solm(Pointer ctx); + int whisper_token_not (Pointer ctx); + int whisper_token_beg (Pointer ctx); + int whisper_token_lang(Pointer ctx, int lang_id); + + // Task tokens + int whisper_token_translate (Pointer ctx); + int whisper_token_transcribe(Pointer ctx); + + // Performance information from the default state. + void whisper_print_timings(Pointer ctx); + void whisper_reset_timings(Pointer ctx); + + // Note: Even if `whisper_full_params is stripped back to just 4 ints, JNA throws "Invalid memory access" + // when `whisper_full_default_params()` tries to return a struct. + // WhisperFullParams whisper_full_default_params(int strategy); + + /** + * Provides default params which can be used with `whisper_full()` etc. + * Because this function allocates memory for the params, the caller must call either: + * - call `whisper_free_params()` + * - `Native.free(Pointer.nativeValue(pointer));` + * + * @param strategy - WhisperSamplingStrategy.value + */ + Pointer whisper_full_default_params_by_ref(int strategy); + + void whisper_free_params(Pointer params); + + /** + * Run the entire model: PCM -> log mel spectrogram -> encoder -> decoder -> text + * Not thread safe for same context + * Uses the specified decoding strategy to obtain the text. + */ + int whisper_full(Pointer ctx, WhisperFullParams.ByValue params, final float[] samples, int n_samples); + + public int whisper_full_with_state(Pointer ctx, Pointer state, WhisperFullParams.ByValue params, float[] samples, int n_samples); + //int whisper_full_with_state(Pointer ctx, Pointer state, WhisperFullParams params, final float[] samples, int n_samples); + + // Split the input audio in chunks and process each chunk separately using whisper_full_with_state() + // Result is stored in the default state of the context + // Not thread safe if executed in parallel on the same context. + // It seems this approach can offer some speedup in some cases. + // However, the transcription accuracy can be worse at the beginning and end of each chunk. + int whisper_full_parallel(Pointer ctx, WhisperFullParams.ByValue params, final float[] samples, int n_samples, int n_processors); + + /** + * Number of generated text segments. + * A segment can be a few words, a sentence, or even a paragraph. + * @param ctx Pointer to WhisperContext + */ + int whisper_full_n_segments (Pointer ctx); + + /** + * @param state Pointer to WhisperState + */ + int whisper_full_n_segments_from_state(Pointer state); + + /** + * Language id associated with the context's default state. + * @param ctx Pointer to WhisperContext + */ + int whisper_full_lang_id(Pointer ctx); + + /** Language id associated with the provided state */ + int whisper_full_lang_id_from_state(Pointer state); + + + /** Get the start time of the specified segment. */ + long whisper_full_get_segment_t0(Pointer ctx, int i_segment); + + /** Get the start time of the specified segment from the state. */ + long whisper_full_get_segment_t0_from_state(Pointer state, int i_segment); + + /** Get the end time of the specified segment. */ + long whisper_full_get_segment_t1(Pointer ctx, int i_segment); + + /** Get the end time of the specified segment from the state. */ + long whisper_full_get_segment_t1_from_state(Pointer state, int i_segment); + + /** Get the text of the specified segment. */ + String whisper_full_get_segment_text(Pointer ctx, int i_segment); + + /** Get the text of the specified segment from the state. */ + String whisper_full_get_segment_text_from_state(Pointer state, int i_segment); + + /** Get the number of tokens in the specified segment. */ + int whisper_full_n_tokens(Pointer ctx, int i_segment); + + /** Get the number of tokens in the specified segment from the state. */ + int whisper_full_n_tokens_from_state(Pointer state, int i_segment); + + /** Get the token text of the specified token in the specified segment. */ + String whisper_full_get_token_text(Pointer ctx, int i_segment, int i_token); + + + /** Get the token text of the specified token in the specified segment from the state. */ + String whisper_full_get_token_text_from_state(Pointer ctx, Pointer state, int i_segment, int i_token); + + /** Get the token ID of the specified token in the specified segment. */ + int whisper_full_get_token_id(Pointer ctx, int i_segment, int i_token); + + /** Get the token ID of the specified token in the specified segment from the state. */ + int whisper_full_get_token_id_from_state(Pointer state, int i_segment, int i_token); + + /** Get token data for the specified token in the specified segment. */ + WhisperTokenData whisper_full_get_token_data(Pointer ctx, int i_segment, int i_token); + + /** Get token data for the specified token in the specified segment from the state. */ + WhisperTokenData whisper_full_get_token_data_from_state(Pointer state, int i_segment, int i_token); + + /** Get the probability of the specified token in the specified segment. */ + float whisper_full_get_token_p(Pointer ctx, int i_segment, int i_token); + + /** Get the probability of the specified token in the specified segment from the state. */ + float whisper_full_get_token_p_from_state(Pointer state, int i_segment, int i_token); + + /** + * Benchmark function for memcpy. + * + * @param nThreads Number of threads to use for the benchmark. + * @return The result of the benchmark. + */ + int whisper_bench_memcpy(int nThreads); + + /** + * Benchmark function for memcpy as a string. + * + * @param nThreads Number of threads to use for the benchmark. + * @return The result of the benchmark as a string. + */ + String whisper_bench_memcpy_str(int nThreads); + + /** + * Benchmark function for ggml_mul_mat. + * + * @param nThreads Number of threads to use for the benchmark. + * @return The result of the benchmark. + */ + int whisper_bench_ggml_mul_mat(int nThreads); + + /** + * Benchmark function for ggml_mul_mat as a string. + * + * @param nThreads Number of threads to use for the benchmark. + * @return The result of the benchmark as a string. + */ + String whisper_bench_ggml_mul_mat_str(int nThreads); +} diff --git a/bindings/java/src/main/java/io/github/ggerganov/whispercpp/bean/WhisperSegment.java b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/bean/WhisperSegment.java new file mode 100644 index 0000000..da970b5 --- /dev/null +++ b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/bean/WhisperSegment.java @@ -0,0 +1,47 @@ +package io.github.ggerganov.whispercpp.bean; + +/** + * Created by litonglinux@qq.com on 10/21/2023_7:48 AM + */ +public class WhisperSegment { + private long start, end; + private String sentence; + + public WhisperSegment() { + } + + public WhisperSegment(long start, long end, String sentence) { + this.start = start; + this.end = end; + this.sentence = sentence; + } + + public long getStart() { + return start; + } + + public long getEnd() { + return end; + } + + public String getSentence() { + return sentence; + } + + public void setStart(long start) { + this.start = start; + } + + public void setEnd(long end) { + this.end = end; + } + + public void setSentence(String sentence) { + this.sentence = sentence; + } + + @Override + public String toString() { + return "[" + start + " --> " + end + "]:" + sentence; + } +} diff --git a/bindings/java/src/main/java/io/github/ggerganov/whispercpp/callbacks/GgmlAbortCallback.java b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/callbacks/GgmlAbortCallback.java new file mode 100644 index 0000000..244e419 --- /dev/null +++ b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/callbacks/GgmlAbortCallback.java @@ -0,0 +1,17 @@ +package io.github.ggerganov.whispercpp.callbacks; + +import com.sun.jna.Callback; + +/** + * Callback for aborting GGML computation + * Maps to the C typedef: bool (*ggml_abort_callback)(void * data) + */ +public interface GgmlAbortCallback extends Callback { + /** + * Return true to abort the computation, false to continue + * + * @param data User data passed to the callback + * @return true to abort, false to continue + */ + boolean invoke(com.sun.jna.Pointer data); +} diff --git a/bindings/java/src/main/java/io/github/ggerganov/whispercpp/callbacks/WhisperEncoderBeginCallback.java b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/callbacks/WhisperEncoderBeginCallback.java new file mode 100644 index 0000000..3d228cb --- /dev/null +++ b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/callbacks/WhisperEncoderBeginCallback.java @@ -0,0 +1,24 @@ +package io.github.ggerganov.whispercpp.callbacks; + +import com.sun.jna.Callback; +import com.sun.jna.Pointer; +import io.github.ggerganov.whispercpp.WhisperContext; +import io.github.ggerganov.whispercpp.model.WhisperState; + +/** + * Callback before the encoder starts. + * If not null, called before the encoder starts. + * If it returns false, the computation is aborted. + */ +public interface WhisperEncoderBeginCallback extends Callback { + + /** + * Callback method before the encoder starts. + * + * @param ctx The whisper context. + * @param state The whisper state. + * @param user_data User data. + * @return True if the computation should proceed, false otherwise. + */ + boolean callback(Pointer ctx, Pointer state, Pointer user_data); +} diff --git a/bindings/java/src/main/java/io/github/ggerganov/whispercpp/callbacks/WhisperLogitsFilterCallback.java b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/callbacks/WhisperLogitsFilterCallback.java new file mode 100644 index 0000000..9777c76 --- /dev/null +++ b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/callbacks/WhisperLogitsFilterCallback.java @@ -0,0 +1,25 @@ +package io.github.ggerganov.whispercpp.callbacks; + +import com.sun.jna.Callback; +import com.sun.jna.Pointer; +import io.github.ggerganov.whispercpp.model.WhisperTokenData; + +/** + * Callback to filter logits. + * Can be used to modify the logits before sampling. + * If not null, called after applying temperature to logits. + */ +public interface WhisperLogitsFilterCallback extends Callback { + + /** + * Callback method to filter logits. + * + * @param ctx The whisper context. + * @param state The whisper state. + * @param tokens The array of whisper_token_data. + * @param n_tokens The number of tokens. + * @param logits The array of logits. + * @param user_data User data. + */ + void callback(Pointer ctx, Pointer state, WhisperTokenData[] tokens, int n_tokens, float[] logits, Pointer user_data); +} diff --git a/bindings/java/src/main/java/io/github/ggerganov/whispercpp/callbacks/WhisperNewSegmentCallback.java b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/callbacks/WhisperNewSegmentCallback.java new file mode 100644 index 0000000..27b1c61 --- /dev/null +++ b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/callbacks/WhisperNewSegmentCallback.java @@ -0,0 +1,24 @@ +package io.github.ggerganov.whispercpp.callbacks; + +import com.sun.jna.Callback; +import com.sun.jna.Pointer; +import io.github.ggerganov.whispercpp.WhisperContext; +import io.github.ggerganov.whispercpp.model.WhisperState; + +/** + * Callback for the text segment. + * Called on every newly generated text segment. + * Use the whisper_full_...() functions to obtain the text segments. + */ +public interface WhisperNewSegmentCallback extends Callback { + + /** + * Callback method for the text segment. + * + * @param ctx The whisper context. + * @param state The whisper state. + * @param n_new The number of newly generated text segments. + * @param user_data User data. + */ + void callback(Pointer ctx, Pointer state, int n_new, Pointer user_data); +} diff --git a/bindings/java/src/main/java/io/github/ggerganov/whispercpp/callbacks/WhisperProgressCallback.java b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/callbacks/WhisperProgressCallback.java new file mode 100644 index 0000000..c64f0ab --- /dev/null +++ b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/callbacks/WhisperProgressCallback.java @@ -0,0 +1,22 @@ +package io.github.ggerganov.whispercpp.callbacks; + +import com.sun.jna.Callback; +import com.sun.jna.Pointer; +import io.github.ggerganov.whispercpp.WhisperContext; +import io.github.ggerganov.whispercpp.model.WhisperState; + +/** + * Callback for progress updates. + */ +public interface WhisperProgressCallback extends Callback { + + /** + * Callback method for progress updates. + * + * @param ctx The whisper context. + * @param state The whisper state. + * @param progress The progress value. + * @param user_data User data. + */ + void callback(Pointer ctx, Pointer state, int progress, Pointer user_data); +} diff --git a/bindings/java/src/main/java/io/github/ggerganov/whispercpp/ggml/GgmlTensor.java b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/ggml/GgmlTensor.java new file mode 100644 index 0000000..2569957 --- /dev/null +++ b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/ggml/GgmlTensor.java @@ -0,0 +1,4 @@ +package io.github.ggerganov.whispercpp.ggml; + +public class GgmlTensor { +} diff --git a/bindings/java/src/main/java/io/github/ggerganov/whispercpp/ggml/GgmlType.java b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/ggml/GgmlType.java new file mode 100644 index 0000000..363120e --- /dev/null +++ b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/ggml/GgmlType.java @@ -0,0 +1,18 @@ +package io.github.ggerganov.whispercpp.ggml; + +public enum GgmlType { + GGML_TYPE_F32, + GGML_TYPE_F16, + GGML_TYPE_Q4_0, + GGML_TYPE_Q4_1, + REMOVED_GGML_TYPE_Q4_2, // support has been removed + REMOVED_GGML_TYPE_Q4_3, // support has been removed + GGML_TYPE_Q5_0, + GGML_TYPE_Q5_1, + GGML_TYPE_Q8_0, + GGML_TYPE_Q8_1, + GGML_TYPE_I8, + GGML_TYPE_I16, + GGML_TYPE_I32, + GGML_TYPE_COUNT, +} diff --git a/bindings/java/src/main/java/io/github/ggerganov/whispercpp/model/EModel.java b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/model/EModel.java new file mode 100644 index 0000000..b2475b3 --- /dev/null +++ b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/model/EModel.java @@ -0,0 +1,10 @@ +package io.github.ggerganov.whispercpp.model; + +public enum EModel { + MODEL_UNKNOWN, + MODEL_TINY, + MODEL_BASE, + MODEL_SMALL, + MODEL_MEDIUM, + MODEL_LARGE, +} diff --git a/bindings/java/src/main/java/io/github/ggerganov/whispercpp/model/WhisperModel.java b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/model/WhisperModel.java new file mode 100644 index 0000000..497ef42 --- /dev/null +++ b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/model/WhisperModel.java @@ -0,0 +1,49 @@ +package io.github.ggerganov.whispercpp; + +import io.github.ggerganov.whispercpp.ggml.GgmlTensor; +import io.github.ggerganov.whispercpp.model.EModel; + +public class WhisperModel { +// EModel type = EModel.MODEL_UNKNOWN; +// +// WhisperHParams hparams; +// WhisperFilters filters; +// +// // encoder.positional_embedding +// GgmlTensor e_pe; +// +// // encoder.conv1 +// GgmlTensor e_conv_1_w; +// GgmlTensor e_conv_1_b; +// +// // encoder.conv2 +// GgmlTensor e_conv_2_w; +// GgmlTensor e_conv_2_b; +// +// // encoder.ln_post +// GgmlTensor e_ln_w; +// GgmlTensor e_ln_b; +// +// // decoder.positional_embedding +// GgmlTensor d_pe; +// +// // decoder.token_embedding +// GgmlTensor d_te; +// +// // decoder.ln +// GgmlTensor d_ln_w; +// GgmlTensor d_ln_b; +// +// std::vector<whisper_layer_encoder> layers_encoder; +// std::vector<whisper_layer_decoder> layers_decoder; +// +// // context +// struct ggml_context * ctx; +// +// // the model memory buffer is read-only and can be shared between processors +// std::vector<uint8_t> * buf; +// +// // tensors +// int n_loaded; +// Map<String, GgmlTensor> tensors; +} diff --git a/bindings/java/src/main/java/io/github/ggerganov/whispercpp/model/WhisperModelLoader.java b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/model/WhisperModelLoader.java new file mode 100644 index 0000000..82615d9 --- /dev/null +++ b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/model/WhisperModelLoader.java @@ -0,0 +1,62 @@ +package io.github.ggerganov.whispercpp.model; + +import com.sun.jna.Callback; +import com.sun.jna.Pointer; +import com.sun.jna.Structure; + + +public class WhisperModelLoader extends Structure { + public Pointer context; + public ReadFunction read; + public EOFFunction eof; + public CloseFunction close; + + public static class ReadFunction implements Callback { + public Pointer invoke(Pointer ctx, Pointer output, int readSize) { + // TODO + return ctx; + } + } + + public static class EOFFunction implements Callback { + public boolean invoke(Pointer ctx) { + // TODO + return false; + } + } + + public static class CloseFunction implements Callback { + public void invoke(Pointer ctx) { + // TODO + } + } + +// public WhisperModelLoader(Pointer p) { +// super(p); +// read = new ReadFunction(); +// eof = new EOFFunction(); +// close = new CloseFunction(); +// read.setCallback(this); +// eof.setCallback(this); +// close.setCallback(this); +// read.write(); +// eof.write(); +// close.write(); +// } + + public WhisperModelLoader() { + super(); + } + + public interface ReadCallback extends Callback { + Pointer invoke(Pointer ctx, Pointer output, int readSize); + } + + public interface EOFCallback extends Callback { + boolean invoke(Pointer ctx); + } + + public interface CloseCallback extends Callback { + void invoke(Pointer ctx); + } +} diff --git a/bindings/java/src/main/java/io/github/ggerganov/whispercpp/model/WhisperState.java b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/model/WhisperState.java new file mode 100644 index 0000000..af93772 --- /dev/null +++ b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/model/WhisperState.java @@ -0,0 +1,4 @@ +package io.github.ggerganov.whispercpp.model; + +public class WhisperState { +} diff --git a/bindings/java/src/main/java/io/github/ggerganov/whispercpp/model/WhisperTokenData.java b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/model/WhisperTokenData.java new file mode 100644 index 0000000..bfa83f9 --- /dev/null +++ b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/model/WhisperTokenData.java @@ -0,0 +1,50 @@ +package io.github.ggerganov.whispercpp.model; + +import com.sun.jna.Structure; + +import java.util.Arrays; +import java.util.List; + +/** + * Structure representing token data. + */ +public class WhisperTokenData extends Structure { + + /** Token ID. */ + public int id; + + /** Forced timestamp token ID. */ + public int tid; + + /** Probability of the token. */ + public float p; + + /** Log probability of the token. */ + public float plog; + + /** Probability of the timestamp token. */ + public float pt; + + /** Sum of probabilities of all timestamp tokens. */ + public float ptsum; + + /** + * Start time of the token (token-level timestamp data). + * Do not use if you haven't computed token-level timestamps. + */ + public long t0; + + /** + * End time of the token (token-level timestamp data). + * Do not use if you haven't computed token-level timestamps. + */ + public long t1; + + /** Voice length of the token. */ + public float vlen; + + @Override + protected List<String> getFieldOrder() { + return Arrays.asList("id", "tid", "p", "plog", "pt", "ptsum", "t0", "t1", "vlen"); + } +} diff --git a/bindings/java/src/main/java/io/github/ggerganov/whispercpp/params/BeamSearchParams.java b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/params/BeamSearchParams.java new file mode 100644 index 0000000..fd621dd --- /dev/null +++ b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/params/BeamSearchParams.java @@ -0,0 +1,19 @@ +package io.github.ggerganov.whispercpp.params; + +import com.sun.jna.Structure; + +import java.util.Arrays; +import java.util.List; + +public class BeamSearchParams extends Structure { + /** ref: <a href="https://github.com/openai/whisper/blob/f82bc59f5ea234d4b97fb2860842ed38519f7e65/whisper/transcribe.py#L265">...</a> */ + public int beam_size; + + /** ref: <a href="https://arxiv.org/pdf/2204.05424.pdf">...</a> */ + public float patience; + + @Override + protected List<String> getFieldOrder() { + return Arrays.asList("beam_size", "patience"); + } +} diff --git a/bindings/java/src/main/java/io/github/ggerganov/whispercpp/params/CBool.java b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/params/CBool.java new file mode 100644 index 0000000..1f6814b --- /dev/null +++ b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/params/CBool.java @@ -0,0 +1,30 @@ +package io.github.ggerganov.whispercpp.params; + +import com.sun.jna.IntegerType; + +import java.util.function.BooleanSupplier; + +public class CBool extends IntegerType implements BooleanSupplier { + public static final int SIZE = 1; + public static final CBool FALSE = new CBool(0); + public static final CBool TRUE = new CBool(1); + + + public CBool() { + this(0); + } + + public CBool(long value) { + super(SIZE, value, true); + } + + @Override + public boolean getAsBoolean() { + return intValue() == 1; + } + + @Override + public String toString() { + return intValue() == 1 ? "true" : "false"; + } +} diff --git a/bindings/java/src/main/java/io/github/ggerganov/whispercpp/params/GreedyParams.java b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/params/GreedyParams.java new file mode 100644 index 0000000..e3b0138 --- /dev/null +++ b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/params/GreedyParams.java @@ -0,0 +1,16 @@ +package io.github.ggerganov.whispercpp.params; + +import com.sun.jna.Structure; + +import java.util.Collections; +import java.util.List; + +public class GreedyParams extends Structure { + /** <a href="https://github.com/openai/whisper/blob/f82bc59f5ea234d4b97fb2860842ed38519f7e65/whisper/transcribe.py#L264">...</a> */ + public int best_of; + + @Override + protected List<String> getFieldOrder() { + return Collections.singletonList("best_of"); + } +} diff --git a/bindings/java/src/main/java/io/github/ggerganov/whispercpp/params/WhisperAhead.java b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/params/WhisperAhead.java new file mode 100644 index 0000000..39691dc --- /dev/null +++ b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/params/WhisperAhead.java @@ -0,0 +1,30 @@ +package io.github.ggerganov.whispercpp.params; +import com.sun.jna.*; +import java.util.Arrays; +import java.util.List; + +public class WhisperAhead extends Structure { + + public int n_text_layer; + + public int n_head; + + public WhisperAhead() { + super(); + } + + public WhisperAhead(int textLayer, int head) { + super(); + this.n_text_layer = textLayer; + this.n_head = head; + } + + @Override + protected List<String> getFieldOrder() { + return Arrays.asList("n_text_layer", "n_head"); + } + + public static class ByReference extends WhisperAhead implements Structure.ByReference {} + + public static class ByValue extends WhisperAhead implements Structure.ByValue {} +} diff --git a/bindings/java/src/main/java/io/github/ggerganov/whispercpp/params/WhisperAheads.java b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/params/WhisperAheads.java new file mode 100644 index 0000000..bca5eb0 --- /dev/null +++ b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/params/WhisperAheads.java @@ -0,0 +1,41 @@ +package io.github.ggerganov.whispercpp.params; +import com.sun.jna.*; +import java.util.Arrays; +import java.util.List; + +public class WhisperAheads extends Structure { + public NativeLong n_heads; + + public Pointer heads; + + public WhisperAheads() { + super(); + } + + /** + * Create alignment heads from an array of WhisperAhead objects + */ + public void setHeads(WhisperAhead[] aheadsArray) { + this.n_heads = new NativeLong(aheadsArray.length); + + int structSize = aheadsArray[0].size(); + Memory mem = new Memory(structSize * aheadsArray.length); + + for (int i = 0; i < aheadsArray.length; i++) { + aheadsArray[i].write(); + byte[] buffer = aheadsArray[i].getPointer().getByteArray(0, structSize); + mem.write(i * structSize, buffer, 0, buffer.length); + } + + this.heads = mem; + } + + @Override + protected List<String> getFieldOrder() { + return Arrays.asList("n_heads", "heads"); + } + + public static class ByReference extends WhisperAheads implements Structure.ByReference {} + + public static class ByValue extends WhisperAheads implements Structure.ByValue {} +} diff --git a/bindings/java/src/main/java/io/github/ggerganov/whispercpp/params/WhisperContextParams.java b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/params/WhisperContextParams.java new file mode 100644 index 0000000..66ec5d7 --- /dev/null +++ b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/params/WhisperContextParams.java @@ -0,0 +1,81 @@ +package io.github.ggerganov.whispercpp.params; +import com.sun.jna.*; +import java.util.Arrays; +import java.util.List; + +/** + * Parameters for the whisper_init_from_file_with_params() function. + * If you change the order or add new parameters, make sure to update the default values in whisper.cpp: + * whisper_context_default_params() + */ +public class WhisperContextParams extends Structure { + public WhisperContextParams(Pointer p) { + super(p); + } + + public WhisperContextParams() { + super(); + } + + /** Use GPU for inference (default = true) */ + public CBool use_gpu; + + /** Use flash attention (default = true) */ + public CBool flash_attn; + + /** CUDA device to use (default = 0) */ + public int gpu_device; + + /** [EXPERIMENTAL] Enable token-level timestamps with DTW (default = false) */ + public CBool dtw_token_timestamps; + + /** [EXPERIMENTAL] Alignment heads preset for DTW */ + public int dtw_aheads_preset; + + /** Number of top layers to use for DTW when using WHISPER_AHEADS_N_TOP_MOST preset */ + public int dtw_n_top; + + public WhisperAheads.ByValue dtw_aheads; + + /** DTW memory size (internal use) */ + public NativeLong dtw_mem_size; + + /** Use GPU for inference */ + public void useGpu(boolean enable) { + use_gpu = enable ? CBool.TRUE : CBool.FALSE; + } + + /** Use flash attention */ + public void useFlashAttn(boolean enable) { + flash_attn = enable ? CBool.TRUE : CBool.FALSE; + } + + /** Enable DTW token-level timestamps */ + public void enableDtwTokenTimestamps(boolean enable) { + dtw_token_timestamps = enable ? CBool.TRUE : CBool.FALSE; + } + + /** Set DTW alignment heads preset */ + public void setDtwAheadsPreset(int preset) { + dtw_aheads_preset = preset; + } + + @Override + protected List<String> getFieldOrder() { + return Arrays.asList( + "use_gpu", + "flash_attn", + "gpu_device", + "dtw_token_timestamps", + "dtw_aheads_preset", + "dtw_n_top", + "dtw_aheads", + "dtw_mem_size" + ); + } + + public static class ByValue extends WhisperContextParams implements Structure.ByValue { + public ByValue() { super(); } + public ByValue(Pointer p) { super(p); } + } +} diff --git a/bindings/java/src/main/java/io/github/ggerganov/whispercpp/params/WhisperFilters.java b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/params/WhisperFilters.java new file mode 100644 index 0000000..b035243 --- /dev/null +++ b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/params/WhisperFilters.java @@ -0,0 +1,10 @@ +package io.github.ggerganov.whispercpp.params; + +import java.util.List; + +public class WhisperFilters { + int n_mel; + int n_fft; + + List<Float> data; +} diff --git a/bindings/java/src/main/java/io/github/ggerganov/whispercpp/params/WhisperFullParams.java b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/params/WhisperFullParams.java new file mode 100644 index 0000000..76ce80f --- /dev/null +++ b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/params/WhisperFullParams.java @@ -0,0 +1,360 @@ +package io.github.ggerganov.whispercpp.params; + +import com.sun.jna.*; +import io.github.ggerganov.whispercpp.callbacks.WhisperEncoderBeginCallback; +import io.github.ggerganov.whispercpp.callbacks.WhisperLogitsFilterCallback; +import io.github.ggerganov.whispercpp.callbacks.WhisperNewSegmentCallback; +import io.github.ggerganov.whispercpp.callbacks.WhisperProgressCallback; +import io.github.ggerganov.whispercpp.callbacks.GgmlAbortCallback; + +import java.util.Arrays; +import java.util.List; + +/** + * Parameters for the whisper_full() function. + * If you change the order or add new parameters, make sure to update the default values in whisper.cpp: + * whisper_full_default_params() + */ +public class WhisperFullParams extends Structure { + + public WhisperFullParams() { + super(); + } + + public WhisperFullParams(Pointer p) { + super(p); + } + + /** Sampling strategy for whisper_full() function. */ + public int strategy; + + /** Number of threads. (default = 4) */ + public int n_threads; + + /** Maximum tokens to use from past text as a prompt for the decoder. (default = 16384) */ + public int n_max_text_ctx; + + /** Start offset in milliseconds. (default = 0) */ + public int offset_ms; + + /** Audio duration to process in milliseconds. (default = 0) */ + public int duration_ms; + + /** Translate flag. (default = false) */ + public CBool translate; + + /** The compliment of translateMode() */ + public void transcribeMode() { + translate = CBool.FALSE; + } + + /** The compliment of transcribeMode() */ + public void translateMode() { + translate = CBool.TRUE; + } + + /** Flag to indicate whether to use past transcription (if any) as an initial prompt for the decoder. (default = true) */ + public CBool no_context; + + /** Flag to indicate whether to use past transcription (if any) as an initial prompt for the decoder. (default = true) */ + public void enableContext(boolean enable) { + no_context = enable ? CBool.FALSE : CBool.TRUE; + } + + /** Generate timestamps or not? */ + public CBool no_timestamps; + + /** Flag to force single segment output (useful for streaming). (default = false) */ + public CBool single_segment; + + /** Flag to force single segment output (useful for streaming). (default = false) */ + public void singleSegment(boolean single) { + single_segment = single ? CBool.TRUE : CBool.FALSE; + } + + /** Flag to print special tokens (e.g., <SOT>, <EOT>, <BEG>, etc.). (default = false) */ + public CBool print_special; + + /** Flag to print special tokens (e.g., <SOT>, <EOT>, <BEG>, etc.). (default = false) */ + public void printSpecial(boolean enable) { + print_special = enable ? CBool.TRUE : CBool.FALSE; + } + + /** Flag to print progress information. (default = true) */ + public CBool print_progress; + + /** Flag to print progress information. (default = true) */ + public void printProgress(boolean enable) { + print_progress = enable ? CBool.TRUE : CBool.FALSE; + } + + /** Flag to print results from within whisper.cpp (avoid it, use callback instead). (default = true) */ + public CBool print_realtime; + + /** Flag to print results from within whisper.cpp (avoid it, use callback instead). (default = true) */ + public void printRealtime(boolean enable) { + print_realtime = enable ? CBool.TRUE : CBool.FALSE; + } + + /** Flag to print timestamps for each text segment when printing realtime. (default = true) */ + public CBool print_timestamps; + + /** Flag to print timestamps for each text segment when printing realtime. (default = true) */ + public void printTimestamps(boolean enable) { + print_timestamps = enable ? CBool.TRUE : CBool.FALSE; + } + + /** [EXPERIMENTAL] Flag to enable token-level timestamps. (default = false) */ + public CBool token_timestamps; + + /** [EXPERIMENTAL] Flag to enable token-level timestamps. (default = false) */ + public void tokenTimestamps(boolean enable) { + token_timestamps = enable ? CBool.TRUE : CBool.FALSE; + } + + /** [EXPERIMENTAL] Timestamp token probability threshold (~0.01). (default = 0.01) */ + public float thold_pt; + + /** [EXPERIMENTAL] Timestamp token sum probability threshold (~0.01). */ + public float thold_ptsum; + + /** Maximum segment length in characters. (default = 0) */ + public int max_len; + + /** Flag to split on word rather than on token (when used with max_len). (default = false) */ + public CBool split_on_word; + + /** Flag to split on word rather than on token (when used with max_len). (default = false) */ + public void splitOnWord(boolean enable) { + split_on_word = enable ? CBool.TRUE : CBool.FALSE; + } + + /** Maximum tokens per segment (0, default = no limit) */ + public int max_tokens; + + /** [EXPERIMENTAL] Enable debug mode for extra info */ + public CBool debug_mode; + + /** Enable debug mode */ + public void enableDebugMode(boolean enable) { + debug_mode = enable ? CBool.TRUE : CBool.FALSE; + } + + /** Overwrite the audio context size (0 = use default). */ + public int audio_ctx; + + /** Enable tinydiarize (default = false) */ + public CBool tdrz_enable; + + /** Enable tinydiarize (default = false) */ + public void tdrzEnable(boolean enable) { + tdrz_enable = enable ? CBool.TRUE : CBool.FALSE; + } + + /** Regular expression matching tokens to suppress. */ + public String suppress_regex; + + /** Tokens to provide to the whisper decoder as an initial prompt. + * These are prepended to any existing text context from a previous call. */ + public String initial_prompt; + /** Always prepend initial_prompt for every decode chunk. */ + public CBool carry_initial_prompt; + + /** Prompt tokens. (int*) */ + public Pointer prompt_tokens; + + public void setPromptTokens(int[] tokens) { + Memory mem = new Memory(tokens.length * 4L); + mem.write(0, tokens, 0, tokens.length); + prompt_tokens = mem; + } + + /** Number of prompt tokens. */ + public int prompt_n_tokens; + + /** Language for auto-detection. + * For auto-detection, set to `null`, `""`, or "auto". */ + public String language; + + /** Flag to indicate whether to detect language automatically. */ + public CBool detect_language; + + /** Flag to indicate whether to detect language automatically. */ + public void detectLanguage(boolean enable) { + detect_language = enable ? CBool.TRUE : CBool.FALSE; + } + + // Common decoding parameters. + + /** Flag to suppress blank tokens. */ + public CBool suppress_blank; + + public void suppressBlanks(boolean enable) { + suppress_blank = enable ? CBool.TRUE : CBool.FALSE; + } + + /** Flag to suppress non-speech tokens. */ + public CBool suppress_nst; + + /** Flag to suppress non-speech tokens. */ + public void suppressNonSpeechTokens(boolean enable) { + suppress_nst = enable ? CBool.TRUE : CBool.FALSE; + } + + /** Initial decoding temperature. */ + public float temperature; + + /** Maximum initial timestamp. */ + public float max_initial_ts; + + /** Length penalty. */ + public float length_penalty; + + // Fallback parameters. + + /** Temperature increment. */ + public float temperature_inc; + + /** Entropy threshold (similar to OpenAI's "compression_ratio_threshold"). */ + public float entropy_thold; + + /** Log probability threshold. */ + public float logprob_thold; + + /** No speech threshold. */ + public float no_speech_thold; + + /** Greedy decoding parameters. */ + public GreedyParams greedy; + + /** + * Beam search decoding parameters. + */ + public BeamSearchParams beam_search; + + public void setBestOf(int bestOf) { + if (greedy == null) { + greedy = new GreedyParams(); + } + greedy.best_of = bestOf; + } + + public void setBeamSize(int beamSize) { + if (beam_search == null) { + beam_search = new BeamSearchParams(); + } + beam_search.beam_size = beamSize; + } + + public void setBeamSizeAndPatience(int beamSize, float patience) { + if (beam_search == null) { + beam_search = new BeamSearchParams(); + } + beam_search.beam_size = beamSize; + beam_search.patience = patience; + } + + /** + * Callback for every newly generated text segment. + * WhisperNewSegmentCallback + */ + public Pointer new_segment_callback; + + /** + * User data for the new_segment_callback. + */ + public Pointer new_segment_callback_user_data; + + /** + * Callback on each progress update. + * WhisperProgressCallback + */ + public Pointer progress_callback; + + /** + * User data for the progress_callback. + */ + public Pointer progress_callback_user_data; + + /** + * Callback each time before the encoder starts. + * WhisperEncoderBeginCallback + */ + public Pointer encoder_begin_callback; + + /** + * User data for the encoder_begin_callback. + */ + public Pointer encoder_begin_callback_user_data; + + /** Callback used to abort GGML computation */ + public Pointer abort_callback; + + /** User data for the abort_callback */ + public Pointer abort_callback_user_data; + + public void setAbortCallback(GgmlAbortCallback callback) { + abort_callback = CallbackReference.getFunctionPointer(callback); + } + + /** + * Callback by each decoder to filter obtained logits. + * WhisperLogitsFilterCallback + */ + public Pointer logits_filter_callback; + + /** + * User data for the logits_filter_callback. + */ + public Pointer logits_filter_callback_user_data; + + + public void setNewSegmentCallback(WhisperNewSegmentCallback callback) { + new_segment_callback = CallbackReference.getFunctionPointer(callback); + } + + public void setProgressCallback(WhisperProgressCallback callback) { + progress_callback = CallbackReference.getFunctionPointer(callback); + } + + public void setEncoderBeginCallbackeginCallbackCallback(WhisperEncoderBeginCallback callback) { + encoder_begin_callback = CallbackReference.getFunctionPointer(callback); + } + + public void setLogitsFilterCallback(WhisperLogitsFilterCallback callback) { + logits_filter_callback = CallbackReference.getFunctionPointer(callback); + } + + /** Grammar stuff */ + public Pointer grammar_rules; + public long n_grammar_rules; + public long i_start_rule; + public float grammar_penalty; + + @Override + protected List<String> getFieldOrder() { + return Arrays.asList("strategy", "n_threads", "n_max_text_ctx", + "offset_ms", "duration_ms", "translate", "no_context", + "no_timestamps", "single_segment", "print_special", + "print_progress", "print_realtime", "print_timestamps", + "token_timestamps", "thold_pt", "thold_ptsum", "max_len", + "split_on_word", "max_tokens", "debug_mode", "audio_ctx", + "tdrz_enable", "suppress_regex", "initial_prompt", "carry_initial_prompt", + "prompt_tokens", "prompt_n_tokens", "language", "detect_language", + "suppress_blank", "suppress_nst", "temperature", + "max_initial_ts", "length_penalty", "temperature_inc", + "entropy_thold", "logprob_thold", "no_speech_thold", "greedy", + "beam_search", "new_segment_callback", "new_segment_callback_user_data", + "progress_callback", "progress_callback_user_data", + "encoder_begin_callback", "encoder_begin_callback_user_data", + "abort_callback", "abort_callback_user_data", + "logits_filter_callback", "logits_filter_callback_user_data", + "grammar_rules", "n_grammar_rules", "i_start_rule", "grammar_penalty"); + } + + public static class ByValue extends WhisperFullParams implements Structure.ByValue { + public ByValue() { super(); } + public ByValue(Pointer p) { super(p); } + } + +} diff --git a/bindings/java/src/main/java/io/github/ggerganov/whispercpp/params/WhisperHParams.java b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/params/WhisperHParams.java new file mode 100644 index 0000000..99feae0 --- /dev/null +++ b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/params/WhisperHParams.java @@ -0,0 +1,15 @@ +package io.github.ggerganov.whispercpp.params; + +public class WhisperHParams { + int n_vocab = 51864; + int n_audio_ctx = 1500; + int n_audio_state = 384; + int n_audio_head = 6; + int n_audio_layer = 4; + int n_text_ctx = 448; + int n_text_state = 384; + int n_text_head = 6; + int n_text_layer = 4; + int n_mels = 80; + int ftype = 1; +} diff --git a/bindings/java/src/main/java/io/github/ggerganov/whispercpp/params/WhisperSamplingStrategy.java b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/params/WhisperSamplingStrategy.java new file mode 100644 index 0000000..a32c793 --- /dev/null +++ b/bindings/java/src/main/java/io/github/ggerganov/whispercpp/params/WhisperSamplingStrategy.java @@ -0,0 +1,10 @@ +package io.github.ggerganov.whispercpp.params; + +/** Available sampling strategies */ +public enum WhisperSamplingStrategy { + /** similar to OpenAI's GreedyDecoder */ + WHISPER_SAMPLING_GREEDY, + + /** similar to OpenAI's BeamSearchDecoder */ + WHISPER_SAMPLING_BEAM_SEARCH +} diff --git a/bindings/java/src/test/java/io/github/ggerganov/whispercpp/WhisperCppTest.java b/bindings/java/src/test/java/io/github/ggerganov/whispercpp/WhisperCppTest.java new file mode 100644 index 0000000..e5b22cf --- /dev/null +++ b/bindings/java/src/test/java/io/github/ggerganov/whispercpp/WhisperCppTest.java @@ -0,0 +1,147 @@ +package io.github.ggerganov.whispercpp; + +import static org.junit.jupiter.api.Assertions.*; + +import io.github.ggerganov.whispercpp.bean.WhisperSegment; +import io.github.ggerganov.whispercpp.params.CBool; +import io.github.ggerganov.whispercpp.params.WhisperContextParams; +import io.github.ggerganov.whispercpp.params.WhisperFullParams; +import io.github.ggerganov.whispercpp.params.WhisperSamplingStrategy; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import java.io.File; +import java.io.FileNotFoundException; +import java.util.List; + +class WhisperCppTest { + private static WhisperCpp whisper = new WhisperCpp(); + private static boolean modelInitialised = false; + + @BeforeAll + static void init() throws FileNotFoundException { + // By default, models are loaded from ~/.cache/whisper/ and are usually named "ggml-${name}.bin" + // or you can provide the absolute path to the model file. + //String modelName = "../../models/ggml-tiny.bin"; + String modelName = "../../models/ggml-tiny.en.bin"; + try { + WhisperContextParams.ByValue contextParams = whisper.getContextDefaultParams(); + contextParams.useFlashAttn(false); // Disable flash attention + whisper.initContext(modelName, contextParams); + //whisper.getFullDefaultParams(WhisperSamplingStrategy.WHISPER_SAMPLING_GREEDY); + //whisper.getJavaDefaultParams(WhisperSamplingStrategy.WHISPER_SAMPLING_BEAM_SEARCH); + modelInitialised = true; + } catch (FileNotFoundException ex) { + System.out.println("Model " + modelName + " not found"); + } + } + + @Test + void testGetDefaultFullParams_BeamSearch() { + // When + WhisperFullParams params = whisper.getFullDefaultParams(WhisperSamplingStrategy.WHISPER_SAMPLING_BEAM_SEARCH); + + // Then + assertEquals(WhisperSamplingStrategy.WHISPER_SAMPLING_BEAM_SEARCH.ordinal(), params.strategy); + assertNotEquals(0, params.n_threads); + assertEquals(16384, params.n_max_text_ctx); + assertFalse(params.translate); + assertEquals(0.01f, params.thold_pt); + assertEquals(5, params.beam_search.beam_size); + assertEquals(-1.0f, params.beam_search.patience); + } + + @Test + void testGetDefaultFullParams_Greedy() { + // When + WhisperFullParams params = whisper.getFullDefaultParams(WhisperSamplingStrategy.WHISPER_SAMPLING_GREEDY); + + // Then + assertEquals(WhisperSamplingStrategy.WHISPER_SAMPLING_GREEDY.ordinal(), params.strategy); + assertNotEquals(0, params.n_threads); + assertEquals(16384, params.n_max_text_ctx); + assertEquals(5, params.greedy.best_of); + } + + @Test + void testFullTranscribe() throws Exception { + if (!modelInitialised) { + System.out.println("Model not initialised, skipping test"); + return; + } + + // Given + File file = new File(System.getProperty("user.dir"), "../../samples/jfk.wav"); + AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(file); + + byte[] b = new byte[audioInputStream.available()]; + float[] floats = new float[b.length / 2]; + + //WhisperFullParams params = whisper.getFullDefaultParams(WhisperSamplingStrategy.WHISPER_SAMPLING_GREEDY); + WhisperFullParams.ByValue params = whisper.getFullDefaultParams(WhisperSamplingStrategy.WHISPER_SAMPLING_BEAM_SEARCH); + params.setProgressCallback((ctx, state, progress, user_data) -> System.out.println("progress: " + progress)); + params.print_progress = CBool.FALSE; + //params.initial_prompt = "and so my fellow Americans um, like"; + + + try { + audioInputStream.read(b); + + for (int i = 0, j = 0; i < b.length; i += 2, j++) { + int intSample = (int) (b[i + 1]) << 8 | (int) (b[i]) & 0xFF; + floats[j] = intSample / 32767.0f; + } + + // When + String result = whisper.fullTranscribe(params, floats); + + // Then + System.err.println(result); + assertEquals("And so my fellow Americans ask not what your country can do for you " + + "ask what you can do for your country.", + result.replace(",", "")); + } finally { + audioInputStream.close(); + } + } + + @Test + void testFullTranscribeWithTime() throws Exception { + if (!modelInitialised) { + System.out.println("Model not initialised, skipping test"); + return; + } + + // Given + File file = new File(System.getProperty("user.dir"), "../../samples/jfk.wav"); + AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(file); + + byte[] b = new byte[audioInputStream.available()]; + float[] floats = new float[b.length / 2]; + + //WhisperFullParams params = whisper.getFullDefaultParams(WhisperSamplingStrategy.WHISPER_SAMPLING_GREEDY); + WhisperFullParams.ByValue params = whisper.getFullDefaultParams(WhisperSamplingStrategy.WHISPER_SAMPLING_BEAM_SEARCH); + params.setProgressCallback((ctx, state, progress, user_data) -> System.out.println("progress: " + progress)); + params.print_progress = CBool.FALSE; + //params.initial_prompt = "and so my fellow Americans um, like"; + + try { + audioInputStream.read(b); + + for (int i = 0, j = 0; i < b.length; i += 2, j++) { + int intSample = (int) (b[i + 1]) << 8 | (int) (b[i]) & 0xFF; + floats[j] = intSample / 32767.0f; + } + + List<WhisperSegment> segments = whisper.fullTranscribeWithTime(params, floats); + assertTrue(segments.size() > 0, "The size of segments should be greater than 0"); + for (WhisperSegment segment : segments) { + System.out.println(segment); + } + } finally { + audioInputStream.close(); + } + } + +} diff --git a/bindings/java/src/test/java/io/github/ggerganov/whispercpp/WhisperJnaLibraryTest.java b/bindings/java/src/test/java/io/github/ggerganov/whispercpp/WhisperJnaLibraryTest.java new file mode 100644 index 0000000..07a340c --- /dev/null +++ b/bindings/java/src/test/java/io/github/ggerganov/whispercpp/WhisperJnaLibraryTest.java @@ -0,0 +1,17 @@ +package io.github.ggerganov.whispercpp; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +class WhisperJnaLibraryTest { + + @Test + void testWhisperPrint_system_info() { + String systemInfo = WhisperCppJnaLibrary.instance.whisper_print_system_info(); + // eg: "AVX = 1 | AVX2 = 1 | AVX512 = 0 | FMA = 1 | NEON = 0 | ARM_FMA = 0 | F16C = 1 | FP16_VA = 0 + // | WASM_SIMD = 0 | BLAS = 0 | SSE3 = 1 | VSX = 0 | COREML = 0 | " + System.out.println("System info: " + systemInfo); + assertTrue(systemInfo.length() > 10); + } +} diff --git a/bindings/javascript/.gitignore b/bindings/javascript/.gitignore new file mode 100644 index 0000000..218a2e5 --- /dev/null +++ b/bindings/javascript/.gitignore @@ -0,0 +1,3 @@ +publish.log +# generated by CMake configure from package-tmpl.json +package.json diff --git a/bindings/javascript/CMakeLists.txt b/bindings/javascript/CMakeLists.txt new file mode 100644 index 0000000..6b5af79 --- /dev/null +++ b/bindings/javascript/CMakeLists.txt @@ -0,0 +1,41 @@ +set(TARGET libwhisper) + +add_executable(${TARGET} + emscripten.cpp + ) + +target_link_libraries(${TARGET} PRIVATE + whisper + ) + +unset(EXTRA_FLAGS) + +if (WHISPER_WASM_SINGLE_FILE) + set(EXTRA_FLAGS "-s SINGLE_FILE=1") + message(STATUS "Embedding WASM inside whisper.js") + + add_custom_command( + TARGET ${TARGET} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_BINARY_DIR}/bin/libwhisper.js + ${CMAKE_CURRENT_SOURCE_DIR}/whisper.js + ) + + add_custom_command( + TARGET ${TARGET} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_BINARY_DIR}/bin/libwhisper.worker.js + ${CMAKE_CURRENT_SOURCE_DIR}/libwhisper.worker.js + ) +endif() + +set_target_properties(${TARGET} PROPERTIES LINK_FLAGS " \ + --bind \ + -s MODULARIZE=1 \ + -s EXPORT_NAME=\"'whisper_factory'\" \ + -s FORCE_FILESYSTEM=1 \ + -s USE_PTHREADS=1 \ + -s PTHREAD_POOL_SIZE=8 \ + -s ALLOW_MEMORY_GROWTH=1 \ + ${EXTRA_FLAGS} \ + ") diff --git a/bindings/javascript/README.md b/bindings/javascript/README.md new file mode 100644 index 0000000..5e726e1 --- /dev/null +++ b/bindings/javascript/README.md @@ -0,0 +1,81 @@ +# whisper.cpp + +Node.js package for Whisper speech recognition + +Package: https://www.npmjs.com/package/whisper.cpp + +## Details + +The performance is comparable to when running `whisper.cpp` in the browser via WASM. + +The API is currently very rudimentary: [bindings/javascript/emscripten.cpp](/bindings/javascript/emscripten.cpp) + +For sample usage check [tests/test-whisper.js](/tests/test-whisper.js) + +## Package building + test + +```bash +# load emscripten +source /path/to/emsdk/emsdk_env.sh + +# clone repo +git clone https://github.com/ggerganov/whisper.cpp +cd whisper.cpp + +# grab base.en model +./models/download-ggml-model.sh base.en + +# prepare PCM sample for testing +ffmpeg -i samples/jfk.wav -f f32le -acodec pcm_f32le samples/jfk.pcmf32 + +# build +mkdir build-em && cd build-em +emcmake cmake .. && make -j + +# run test +node ../tests/test-whisper.js + +# For Node.js versions prior to v16.4.0, experimental features need to be enabled: +node --experimental-wasm-threads --experimental-wasm-simd ../tests/test-whisper.js + +# publish npm package +make publish-npm +``` + +## Sample run + +```text +$ node --experimental-wasm-threads --experimental-wasm-simd ../tests/test-whisper.js + +whisper_model_load: loading model from 'whisper.bin' +whisper_model_load: n_vocab = 51864 +whisper_model_load: n_audio_ctx = 1500 +whisper_model_load: n_audio_state = 512 +whisper_model_load: n_audio_head = 8 +whisper_model_load: n_audio_layer = 6 +whisper_model_load: n_text_ctx = 448 +whisper_model_load: n_text_state = 512 +whisper_model_load: n_text_head = 8 +whisper_model_load: n_text_layer = 6 +whisper_model_load: n_mels = 80 +whisper_model_load: f16 = 1 +whisper_model_load: type = 2 +whisper_model_load: adding 1607 extra tokens +whisper_model_load: mem_required = 506.00 MB +whisper_model_load: ggml ctx size = 140.60 MB +whisper_model_load: memory size = 22.83 MB +whisper_model_load: model size = 140.54 MB + +system_info: n_threads = 8 / 10 | AVX = 0 | AVX2 = 0 | AVX512 = 0 | NEON = 0 | F16C = 0 | FP16_VA = 0 | WASM_SIMD = 1 | BLAS = 0 | + +operator(): processing 176000 samples, 11.0 sec, 8 threads, 1 processors, lang = en, task = transcribe ... + +[00:00:00.000 --> 00:00:11.000] And so my fellow Americans, ask not what your country can do for you, ask what you can do for your country. + +whisper_print_timings: load time = 162.37 ms +whisper_print_timings: mel time = 183.70 ms +whisper_print_timings: sample time = 4.27 ms +whisper_print_timings: encode time = 8582.63 ms / 1430.44 ms per layer +whisper_print_timings: decode time = 436.16 ms / 72.69 ms per layer +whisper_print_timings: total time = 9370.90 ms +``` diff --git a/bindings/javascript/emscripten.cpp b/bindings/javascript/emscripten.cpp new file mode 100644 index 0000000..b442c1f --- /dev/null +++ b/bindings/javascript/emscripten.cpp @@ -0,0 +1,93 @@ +// +// This is the Javascript API of whisper.cpp +// +// Very crude at the moment. +// Feel free to contribute and make this better! +// +// See the tests/test-whisper.js for sample usage +// + +#include "whisper.h" + +#include <emscripten.h> +#include <emscripten/bind.h> + +#include <thread> +#include <vector> + +struct whisper_context * g_context; + +EMSCRIPTEN_BINDINGS(whisper) { + emscripten::function("init", emscripten::optional_override([](const std::string & path_model) { + if (g_context == nullptr) { + g_context = whisper_init_from_file_with_params(path_model.c_str(), whisper_context_default_params()); + if (g_context != nullptr) { + return true; + } else { + return false; + } + } + + return false; + })); + + emscripten::function("free", emscripten::optional_override([]() { + if (g_context) { + whisper_free(g_context); + g_context = nullptr; + } + })); + + emscripten::function("full_default", emscripten::optional_override([](const emscripten::val & audio, const std::string & lang, bool translate) { + if (g_context == nullptr) { + return -1; + } + + struct whisper_full_params params = whisper_full_default_params(whisper_sampling_strategy::WHISPER_SAMPLING_GREEDY); + + params.print_realtime = true; + params.print_progress = false; + params.print_timestamps = true; + params.print_special = false; + params.translate = translate; + params.language = whisper_is_multilingual(g_context) ? lang.c_str() : "en"; + params.n_threads = std::min(8, (int) std::thread::hardware_concurrency()); + params.offset_ms = 0; + + std::vector<float> pcmf32; + const int n = audio["length"].as<int>(); + + emscripten::val heap = emscripten::val::module_property("HEAPU8"); + emscripten::val memory = heap["buffer"]; + + pcmf32.resize(n); + + emscripten::val memoryView = audio["constructor"].new_(memory, reinterpret_cast<uintptr_t>(pcmf32.data()), n); + memoryView.call<void>("set", audio); + + // print system information + { + printf("\n"); + printf("system_info: n_threads = %d / %d | %s\n", + params.n_threads, std::thread::hardware_concurrency(), whisper_print_system_info()); + + printf("\n"); + printf("%s: processing %d samples, %.1f sec, %d threads, %d processors, lang = %s, task = %s ...\n", + __func__, int(pcmf32.size()), float(pcmf32.size())/WHISPER_SAMPLE_RATE, + params.n_threads, 1, + params.language, + params.translate ? "translate" : "transcribe"); + + printf("\n"); + } + + // run whisper + { + whisper_reset_timings(g_context); + whisper_full(g_context, params, pcmf32.data(), pcmf32.size()); + whisper_print_timings(g_context); + } + + return 0; + })); +} diff --git a/bindings/javascript/libwhisper.worker.js b/bindings/javascript/libwhisper.worker.js new file mode 100644 index 0000000..db2083a --- /dev/null +++ b/bindings/javascript/libwhisper.worker.js @@ -0,0 +1 @@ +"use strict";var Module={};var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string";if(ENVIRONMENT_IS_NODE){var nodeWorkerThreads=require("worker_threads");var parentPort=nodeWorkerThreads.parentPort;parentPort.on("message",data=>onmessage({data:data}));var fs=require("fs");Object.assign(global,{self:global,require:require,Module:Module,location:{href:__filename},Worker:nodeWorkerThreads.Worker,importScripts:f=>(0,eval)(fs.readFileSync(f,"utf8")+"//# sourceURL="+f),postMessage:msg=>parentPort.postMessage(msg),performance:global.performance||{now:Date.now}})}var initializedJS=false;function threadPrintErr(){var text=Array.prototype.slice.call(arguments).join(" ");if(ENVIRONMENT_IS_NODE){fs.writeSync(2,text+"\n");return}console.error(text)}function threadAlert(){var text=Array.prototype.slice.call(arguments).join(" ");postMessage({cmd:"alert",text:text,threadId:Module["_pthread_self"]()})}var err=threadPrintErr;self.alert=threadAlert;Module["instantiateWasm"]=(info,receiveInstance)=>{var module=Module["wasmModule"];Module["wasmModule"]=null;var instance=new WebAssembly.Instance(module,info);return receiveInstance(instance)};self.onunhandledrejection=e=>{throw e.reason||e};function handleMessage(e){try{if(e.data.cmd==="load"){let messageQueue=[];self.onmessage=e=>messageQueue.push(e);self.startWorker=instance=>{Module=instance;postMessage({"cmd":"loaded"});for(let msg of messageQueue){handleMessage(msg)}self.onmessage=handleMessage};Module["wasmModule"]=e.data.wasmModule;for(const handler of e.data.handlers){Module[handler]=(...args)=>{postMessage({cmd:"callHandler",handler:handler,args:args})}}Module["wasmMemory"]=e.data.wasmMemory;Module["buffer"]=Module["wasmMemory"].buffer;Module["ENVIRONMENT_IS_PTHREAD"]=true;if(typeof e.data.urlOrBlob=="string"){importScripts(e.data.urlOrBlob)}else{var objectUrl=URL.createObjectURL(e.data.urlOrBlob);importScripts(objectUrl);URL.revokeObjectURL(objectUrl)}whisper_factory(Module)}else if(e.data.cmd==="run"){Module["__emscripten_thread_init"](e.data.pthread_ptr,0,0,1);Module["__emscripten_thread_mailbox_await"](e.data.pthread_ptr);Module["establishStackSpace"]();Module["PThread"].receiveObjectTransfer(e.data);Module["PThread"].threadInitTLS();if(!initializedJS){Module["__embind_initialize_bindings"]();initializedJS=true}try{Module["invokeEntryPoint"](e.data.start_routine,e.data.arg)}catch(ex){if(ex!="unwind"){throw ex}}}else if(e.data.cmd==="cancel"){if(Module["_pthread_self"]()){Module["__emscripten_thread_exit"](-1)}}else if(e.data.target==="setimmediate"){}else if(e.data.cmd==="checkMailbox"){if(initializedJS){Module["checkMailbox"]()}}else if(e.data.cmd){err(`worker.js received unknown command ${e.data.cmd}`);err(e.data)}}catch(ex){if(Module["__emscripten_thread_crashed"]){Module["__emscripten_thread_crashed"]()}throw ex}}self.onmessage=handleMessage; diff --git a/bindings/javascript/package-tmpl.json b/bindings/javascript/package-tmpl.json new file mode 100644 index 0000000..33777d1 --- /dev/null +++ b/bindings/javascript/package-tmpl.json @@ -0,0 +1,26 @@ +{ + "name": "whisper.cpp", + "version": "@WHISPER_VERSION@", + "description": "Whisper speech recognition", + "main": "whisper.js", + "scripts": { + "test": "echo \"todo: add tests\" && exit 0" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/ggerganov/whisper.cpp" + }, + "keywords": [ + "openai", + "whisper", + "speech-to-text", + "speech-recognition", + "transformer" + ], + "author": "Georgi Gerganov", + "license": "MIT", + "bugs": { + "url": "https://github.com/ggerganov/whisper.cpp/issues" + }, + "homepage": "https://github.com/ggerganov/whisper.cpp#readme" +} diff --git a/bindings/javascript/whisper.js b/bindings/javascript/whisper.js new file mode 100644 index 0000000..77dd855 --- /dev/null +++ b/bindings/javascript/whisper.js @@ -0,0 +1,19 @@ + +var whisper_factory = (() => { + var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; + if (typeof __filename !== 'undefined') _scriptDir = _scriptDir || __filename; + return ( +function(moduleArg = {}) { + +function GROWABLE_HEAP_I8(){if(wasmMemory.buffer!=HEAP8.buffer){updateMemoryViews()}return HEAP8}function GROWABLE_HEAP_U8(){if(wasmMemory.buffer!=HEAP8.buffer){updateMemoryViews()}return HEAPU8}function GROWABLE_HEAP_I16(){if(wasmMemory.buffer!=HEAP8.buffer){updateMemoryViews()}return HEAP16}function GROWABLE_HEAP_U16(){if(wasmMemory.buffer!=HEAP8.buffer){updateMemoryViews()}return HEAPU16}function GROWABLE_HEAP_I32(){if(wasmMemory.buffer!=HEAP8.buffer){updateMemoryViews()}return HEAP32}function GROWABLE_HEAP_U32(){if(wasmMemory.buffer!=HEAP8.buffer){updateMemoryViews()}return HEAPU32}function GROWABLE_HEAP_F32(){if(wasmMemory.buffer!=HEAP8.buffer){updateMemoryViews()}return HEAPF32}function GROWABLE_HEAP_F64(){if(wasmMemory.buffer!=HEAP8.buffer){updateMemoryViews()}return HEAPF64}var Module=moduleArg;var readyPromiseResolve,readyPromiseReject;Module["ready"]=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject});var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var ENVIRONMENT_IS_WEB=typeof window=="object";var ENVIRONMENT_IS_WORKER=typeof importScripts=="function";var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string";var ENVIRONMENT_IS_PTHREAD=Module["ENVIRONMENT_IS_PTHREAD"]||false;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;if(ENVIRONMENT_IS_NODE){var fs=require("fs");var nodePath=require("path");if(ENVIRONMENT_IS_WORKER){scriptDirectory=nodePath.dirname(scriptDirectory)+"/"}else{scriptDirectory=__dirname+"/"}read_=(filename,binary)=>{filename=isFileURI(filename)?new URL(filename):nodePath.normalize(filename);return fs.readFileSync(filename,binary?undefined:"utf8")};readBinary=filename=>{var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}return ret};readAsync=(filename,onload,onerror,binary=true)=>{filename=isFileURI(filename)?new URL(filename):nodePath.normalize(filename);fs.readFile(filename,binary?undefined:"utf8",(err,data)=>{if(err)onerror(err);else onload(binary?data.buffer:data)})};if(!Module["thisProgram"]&&process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}arguments_=process.argv.slice(2);quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow};Module["inspect"]=()=>"[Emscripten Module object]";let nodeWorkerThreads;try{nodeWorkerThreads=require("worker_threads")}catch(e){console.error('The "worker_threads" module is not supported in this node.js build - perhaps a newer version is needed?');throw e}global.Worker=nodeWorkerThreads.Worker}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}else{scriptDirectory=""}if(!ENVIRONMENT_IS_NODE){read_=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=(url,onload,onerror)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=()=>{if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=title=>document.title=title}else{}if(ENVIRONMENT_IS_NODE){if(typeof performance=="undefined"){global.performance=require("perf_hooks").performance}}var defaultPrint=console.log.bind(console);var defaultPrintErr=console.error.bind(console);if(ENVIRONMENT_IS_NODE){defaultPrint=(...args)=>fs.writeSync(1,args.join(" ")+"\n");defaultPrintErr=(...args)=>fs.writeSync(2,args.join(" ")+"\n")}var out=Module["print"]||defaultPrint;var err=Module["printErr"]||defaultPrintErr;Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime=Module["noExitRuntime"]||true;if(typeof WebAssembly!="object"){abort("no native wasm support detected")}var wasmMemory;var wasmModule;var ABORT=false;var EXITSTATUS;function assert(condition,text){if(!condition){abort(text)}}var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateMemoryViews(){var b=wasmMemory.buffer;Module["HEAP8"]=HEAP8=new Int8Array(b);Module["HEAP16"]=HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);Module["HEAPF64"]=HEAPF64=new Float64Array(b)}var INITIAL_MEMORY=Module["INITIAL_MEMORY"]||16777216;assert(INITIAL_MEMORY>=65536,"INITIAL_MEMORY should be larger than STACK_SIZE, was "+INITIAL_MEMORY+"! (STACK_SIZE="+65536+")");if(ENVIRONMENT_IS_PTHREAD){wasmMemory=Module["wasmMemory"]}else{if(Module["wasmMemory"]){wasmMemory=Module["wasmMemory"]}else{wasmMemory=new WebAssembly.Memory({"initial":INITIAL_MEMORY/65536,"maximum":2147483648/65536,"shared":true});if(!(wasmMemory.buffer instanceof SharedArrayBuffer)){err("requested a shared WebAssembly.Memory but the returned buffer is not a SharedArrayBuffer, indicating that while the browser has SharedArrayBuffer it does not have WebAssembly threads support - you may need to set a flag");if(ENVIRONMENT_IS_NODE){err("(on node you may need: --experimental-wasm-threads --experimental-wasm-bulk-memory and/or recent version)")}throw Error("bad memory")}}}updateMemoryViews();INITIAL_MEMORY=wasmMemory.buffer.byteLength;var wasmTable;var __ATPRERUN__=[];var __ATINIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;var runtimeKeepaliveCounter=0;function keepRuntimeAlive(){return noExitRuntime||runtimeKeepaliveCounter>0}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;if(ENVIRONMENT_IS_PTHREAD)return;if(!Module["noFSInit"]&&!FS.init.initialized)FS.init();FS.ignorePermissions=false;TTY.init();callRuntimeCallbacks(__ATINIT__)}function postRun(){if(ENVIRONMENT_IS_PTHREAD)return;if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnInit(cb){__ATINIT__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function getUniqueRunDependency(id){return id}function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}what="Aborted("+what+")";err(what);ABORT=true;EXITSTATUS=1;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return filename.startsWith(dataURIPrefix)}function isFileURI(filename){return filename.startsWith("file://")}var wasmBinaryFile;wasmBinaryFile="data:application/octet-stream;base64,AGFzbQEAAAAByARKYAF/AX9gAX8AYAJ/fwBgA39/fwF/YAJ/fwF/YAN/f38AYAR/f39/AGAGf39/f39/AX9gBH9/f38Bf2AAAGAFf39/f38Bf2AFf39/f38AYAZ/f39/f38AYAh/f39/f39/fwF/YAABf2AHf39/f39/fwF/YAd/f39/f39/AGABfQF9YAV/fn5+fgBgA39+fwF+YAV/f39/fgF/YAR/f39/AX5gBH9+fn8AYAp/f39/f39/f39/AX9gB39/f39/fn4Bf2AGf39/f35+AX9gBX9/fn9/AGADf39/AXxgBH9/f38BfGAEf39+fgF/YAJ/fgBgAXwBfWACfH8BfGAFf39+fn4Bf2AIf39/f39/f38AYAx/f39/f39/f39/f38Bf2ADf35/AX9gD39/f39/f39/f39/f39/fwBgCn9/f39/f39/f38AYAt/f39/f39/f39/fwF/YAZ/fH9/f38Bf2AJf39/f39/f39/AGAFf39/f3wBf2AAAXxgA39/fgF/YAABfmACf3wAYAN/f30Bf2AEfn5+fgF/YAJ9fQF9YAJ+fwF/YAN/f3wBf2ABfABgCH9/fn5+f39/AX9gBX9/f399AGAGf39+fn9/AX9gAn5+AXxgAnx8AXxgBn9/f39+fgBgBH9/fn4AYAR/f35/AX9gA39/fwF9YAR/f39+AX5gA39/fgBgAn5+AX1gAn9/AX5gA35+fgF/YAF+AX9gAXwBfGABfwF+YAJ9fwF/YAV/f399fQBgBH9/f30AYAl/f39/f39/f38BfwKmAjABYQFiAAkBYQFjAAUBYQFkAAUBYQFlACsBYQFmAAEBYQFnAAsBYQFoABABYQFpAAQBYQFqAAABYQFrAAUBYQFsAAEBYQFtAAEBYQFuAAkBYQFvAAABYQFwAAgBYQFxAAMBYQFyAAIBYQFzAAUBYQF0AAgBYQF1AAABYQF2AAEBYQF3ABsBYQF4AAoBYQF5ABABYQF6AA4BYQFBAA4BYQFCAAoBYQFDAAQBYQFEAAQBYQFFAAABYQFGAAUBYQFHAAgBYQFIABwBYQFJAAEBYQFKAAEBYQFLAAgBYQFMAAMBYQFNAAgBYQFOAAkBYQFPAAEBYQFQAA4BYQFRAAIBYQFSAAYBYQFTAAIBYQFUAAEBYQFVAAYBYQFWAAQBYQFhAgOAAoCAAgOhB58HAQAF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AgQgBSAGGyEGA0ACQAJAAkACQAJAIAQiAigCFCACLQAbIgQgBMBBAEgiBxsiBCAGIAQgBkkiCRsiBQRAIAggAigCECACQRBqIAcbIgcgBRBKIgpFBEAgBCAGSw0CDAMLIApBAE4NAgwBCyAEIAZNDQILIAIhBSACKAIAIgQNBAwFCyAHIAggBRBKIgQNAQsgCQ0BDAQLIARBAE4NAwsgAigCBCIEDQALIAJBBGohBQtBIBAyIgQgAygCACIDKQIANwIQIAQgAygCCDYCGCADQgA3AgAgA0EANgIIIAQgAjYCCCAEQgA3AgAgBEEANgIcIAUgBDYCACAEIQIgASgCACgCACIDBEAgASADNgIAIAUoAgAhAgsgASgCBCACEKQBIAEgASgCCEEBajYCCEEBDAELIAIhBEEACzoABCAAIAQ2AgALBgAgABAvC4cFAgp/AX4jAEFAaiICJAAgAiAALQAIOgA4IAIgACkDADcDMCAAKAIUIQMgACgCECEEIABCADcDECAAKAIYIQUgAEEANgIYIAIgACgCRDYCKCACIAApAjw3AyAgAiAA/QACLP0LAxAgAiAA/QACHP0LAwAgACgCXCEGIAAoAlghByAAQgA3A1ggACgCTCEIIAAoAlAhCSAAKAJUIQogACgCSCELIAD9DAAAAAAAAAAAAAAAAAAAAAD9CwNIIAAgASkDADcDACAAIAEtAAg6AAggACABKAIQNgIQIAAgASgCFDYCFCAAIAEoAhg2AhggACkDYCEMIAFBADYCGCABQgA3AxAgACABKAJENgJEIAAgASkCPDcCPCAAIAH9AAIs/QsCLCAAIAH9AAIc/QsCHCAAQcgAaiABQcgAahCHAiABIAItADg6AAggASACKQMwNwMAIAEoAhAiAARAIAEgADYCFCAAEC8LIAEgBTYCGCABIAM2AhQgASAENgIQIAEgAigCKDYCRCABIAIpAyA3AjwgASAC/QADEP0LAiwgASAC/QADAP0LAhwgASgCSCIEBEAgASgCTCIDIAQiAEcEQANAIANBDGsiACgCACIFBEAgA0EIayAFNgIAIAUQLwsgACIDIARHDQALIAEoAkghAAsgASAENgJMIAAQLwsgASALNgJIIAEgCTYCUCABIAg2AkwgASgCVCIEBEAgASgCWCIDIAQiAEcEQANAIANBDGsiACgCACIFBEAg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//8Ba0GBgH5LDQELIAFQIAJC////////////AIMiC0KAgICAgIDA//8AVCALQoCAgICAgMD//wBRG0UEQCACQoCAgICAgCCEIQoMAgsgA1AgBEL///////////8AgyICQoCAgICAgMD//wBUIAJCgICAgICAwP//AFEbRQRAIARCgICAgICAIIQhCiADIQEMAgsgASALQoCAgICAgMD//wCFhFAEQCACIAOEUARAQoCAgICAgOD//wAhCkIAIQEMAwsgCkKAgICAgIDA//8AhCEKQgAhAQwCCyADIAJCgICAgICAwP//AIWEUARAIAEgC4QhGUIAIQEgGVAEQEKAgICAgIDg//8AIQoMAwsgCkKAgICAgIDA//8AhCEKDAILIAEgC4RQBEBCACEBDAILIAIgA4RQBEBCACEBDAILIAtC////////P1gEQCAFQdAAaiABIA0gASANIA1QIgYbeSAGQQZ0rXynIgZBD2sQZkEQIAZrIQYgBSkDWCINQiCIIQ4gBSkDUCEBCyACQv///////z9WDQAgBUFAayADIAwgAyAMIAxQIggbeSAIQQZ0rXynIghBD2sQZiAGIAhrQRBqIQYgBSkDSCEMIAUpA0AhAwsgA0IPhiILQoCA/v8PgyICIAFCIIgiBH4iECALQiCIIhMgAUL/////D4MiAX58Ig9CIIYiESABIAJ+fCILIBFUrSACIA1C/////w+DIg1+IhUgBCATfnwiESAMQg+GIhIgA0IxiIRC/////w+DIgMgAX58IhQgDyAQVK1CIIYgD0IgiIR8Ig8gAiAOQoCABIQiDH4iFiANIBN+fCIOIBJCIIhCgICAgAiEIgIgAX58IhAgAyAEfnwiEkIghnwiF3whASAHIAlqIAZqQf//AGshBgJAIAIgBH4iGCAMIBN+fCIEIBhUrSAEIAQgAyANfnwiBFatfCACIAx+fCAEIAQgESAVVK0gESAUVq18fCIEVq18IAMgDH4iAyACIA1+fCICIANUrUIghiACQiCIhHwgBCACQiCGfCICIARUrXwgAiACIBAgElatIA4gFlStIA4gEFatfHxCIIYgEkIgiIR8IgJWrXwgAiACIA8gFFStIA8gF1atfHwiAlatfCIEQoCAgICAgMAAg0IAUgRAIAZBAWohBgwBCyALQj+IIRogBEIBhiACQj+IhCEEIAJCAYYg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AisDACADokHAoAIrAwCgoCAEoiABQRd1t0GooAIrAwCiIAJBsJ4CaisDAKAgA6CgtiEACyAAC+4CAgZ/A34jAEFAaiIDJAACQAJAIAEpAxAgAikDEFINACACKQMgIgkgASkDIIFCAFINACACKQMoIgogASkDKIFCAFINACABKAIwIAEoAjRLDQECfwJAIAEoAogBDQAgAigCiAENAEEADAELQQELIQggAyABKQMYNwMgIAIpAxghCyADIAo3AzggAyAJNwMwIAMgCzcDKCAAQQAgASgCDCIEIAIoAgwiByAEIAdKGyADQSBqQQBBABBJIgRBFzYCQCAIBEAgACAEKAIAIAQoAgwgBEEQakEAQQAQSSEFCyAEIAE2AowBIAQgBTYCiAEgBCACNgKQASADQUBrJAAgBA8LQbzDAigCABAwGiADQZTXADYCGCADQecfNgIUIANB5CY2AhBBuMMCKAIAQcvkACADQRBqEDEQAAALQbzDAigCABAwGiADQdvXADYCCCADQegfNgIEIANB5CY2AgBBuMMCKAIAQcvkACADEDEQAAALoQICA38BfgJAIAAoAgwiAkEBcQRAQRgQMiECIAAoAiQoAgQhAyACQfysATYCACACIAM2AgQgAkEIaiAAKAIAIgM2AgAgA0EEakEB/h4CABogAiAAKQIEIgU3AgwgAiAFpyIDIAEgAygCACgCFBEEADoAFCAAKAIkIAI2AgQMAQsgAkEIcQRAQRgQMiECIAAoAiQoAgQhAyACQditATYCACACIAM2AgQgAkEIaiAAKAIAIgM2AgAgA0EEakEB/h4CABogACkCBCEFIAIgAToAFCACIAU3AgwgACgCJCACNgIEDAELQQwQMiECIAAoAiQiAygCBCEEIAIgAToACCACQbSuATYCACACIAQ2AgQgAyACNgIECyAAIAAoAiQoAgQ2AiQLdQEBfyMAQRBrIgIkACACIAE6AA8CQCAALQALQQd2BEAgACgCACEBIABBATYCBAwBCyAAIgEgAS0AC0GAAXFBAXI6AAsgASABLQALQf8AcToACwsgASACLQAPOgAAIAJBADoADiABIAItAA46AAEgAkEQaiQAC6sBAgJ+AX8gACkDICEBIAApAxghAiAAKAIAIgNBEGtBcU0EQCADQSRsQZibAWooAgAgACgCMCAAKAIQQQFrbGogACgCNCACp0EBa2xqIAAoAjggAadBAWtsaiAAKAI8IAAoAihBAWtsag8LIAA1AjQgAkIBfX4gACkDECAANQIwfiADQSRsQZSbAWo1AgB/fCAANQI4IAFCAX1+fCAANQI8IAApAyhCAX1+fKcLDQAgACgCABD7AxogAAsNACAAKAIAEP0DGiAACw4AIABB0ABqEDtB0ABqC8EBAQN/IwBBEGsiBSQAAkAgAiAALQALQQd2BH8gACgCCEH/////B3FBAWsFQQoLIgQCfyAALQALQQd2BEAgACgCBAwBCyAALQALQf8AcQsiA2tNBEAgAkUNAQJ/IAAtAAtBB3YEQCAAKAIADAELIAALIgQgA2ogASACEHcgACACIANqIgEQmQEgBUEAOgAPIAEgBGogBS0ADzoAAAwBCyAAIAQgAiAEayADaiADIANBACACIAEQzAELIAVBEGokACAAC3UBAX4gACABIAR+IAIgA358IANCIIgiAiABQiCIIgR+fCADQv////8PgyIDIAFC/////w+DIgF+IgVCIIggAyAEfnwiA0IgiHwgASACfiADQv////8Pg3wiAUIgiHw3AwggACAFQv////8PgyABQiCGhDcDAAsYACAALQAAQSBxRQRAIAEgAiAAEM4CGgsLqwEBBn8jAEEQayICJAAgACgCBCEDIAEgACgCACIEcCIFIQACQAJAA0ACQCADIABBAnRqIgYoAgAiB0UNACABIAdGDQAgAEEBaiIAQQAgACAERxsiACAFRw0BDAILCyAAQX9HDQELQbzDAigCABAwGiACQbUwNgIIIAJBlvEANgIEIAJB5CY2AgBBuMMCKAIAQcvkACACEDEQAAALIAYgATYCACACQRBqJAAgAAtGAQJ/IwBBEGsiBSQAIAUgAjYCDCAFIAQ2AgggBUEEaiAFQQxqEH8hBiAAIAEgAyAFKAIIELABIQAgBhB+IAVBEGokACAAC+0BAQJ/An8gAC0AC0EHdgRAIAAoAgQMAQsgAC0AC0H/AHELIQQCQCACIAFrQQVIDQAgBEUNACABIAIQ6gEgAkEEayEEAn8gAC0AC0EHdgRAIAAoAgQMAQsgAC0AC0H/AHELAn8gAC0AC0EHdgRAIAAoAgAMAQsgAAsiAmohBQJAA0ACQCACLAAAIQAgASAETw0AAkAgAEEATA0AIABB/wBODQAgACABKAIARw0DCyABQQRqIQEgAiAFIAJrQQFKaiECDAELCyAAQQBMDQEgAEH/AE4NASACLAAAIAQoAgBBAWtLDQELIANBBDYCAAsLdgEBfyMAQRBrIgIkACAALQALQQd2BEAgACAAKAIAIAAoAghB/////wdxEPEBCyAAIAEoAgg2AgggACABKQIANwIAIAEgAS0AC0GAAXE6AAsgASABLQALQf8AcToACyACQQA6AA8gASACLQAPOgAAIAJBEGokAAtQAQF+AkAgA0HAAHEEQCABIANBQGqthiECQgAhAQwBCyADRQ0AIAIgA60iBIYgAUHAACADa62IhCECIAEgBIYhAQsgACABNwMAIAAgAjcDCAtvAQF/IwBBgAJrIgUkAAJAIAIgA0wNACAEQYDABHENACAFIAFB/wFxIAIgA2siA0GAAiADQYACSSIBGxCgASABRQRAA0AgACAFQYACEGEgA0GAAmsiA0H/AUsNAAsLIAAgBSADEGELIAVBgAJqJAALegEDfwJAAkAgACIBQQNxRQ0AIAEtAABFBEBBAA8LA0AgAUEBaiIBQQNxRQ0BIAEtAAANAAsMAQsDQCABIgJBBGohASACKAIAIgNBf3MgA0GBgoQIa3FBgIGChHhxRQ0ACwNAIAIiAUEBaiECIAEtAAANAAsLIAEgAGsLOAIDfwF+IwBBEGsiACQAIAAQ1wEgACkDACEDIAAoAgghAiAAQRBqJAAgAkHoB22sIANCwIQ9fnwL9gIDA38BfAF9IwBBEGsiASQAAn0gALwiA0H/////B3EiAkHan6T6A00EQEMAAIA/IAJBgICAzANJDQEaIAC7EIQBDAELIAJB0aftgwRNBEAgAkHkl9uABE8EQEQYLURU+yEJQEQYLURU+yEJwCADQQBIGyAAu6AQhAGMDAILIAC7IQQgA0EASARAIAREGC1EVPsh+T+gEIMBDAILRBgtRFT7Ifk/IAShEIMBDAELIAJB1eOIhwRNBEAgAkHg27+FBE8EQEQYLURU+yEZQEQYLURU+yEZwCADQQBIGyAAu6AQhAEMAgsgA0EASARARNIhM3982RLAIAC7oRCDAQwCCyAAu0TSITN/fNkSwKAQgwEMAQsgACAAkyACQYCAgPwHTw0AGgJAAkACQAJAIAAgAUEIahCvBEEDcQ4DAAECAwsgASsDCBCEAQwDCyABKwMImhCDAQwCCyABKwMIEIQBjAwBCyABKwMIEIMBCyEFIAFBEGokACAFC9YBAQJ/IwBBEGsiBCQAAkACQCACQQtJBEAgACIDIAAtAAtBgAFxIAJyOgALIAAgAC0AC0H/AHE6AAsMAQsgAkHv////B0sNASAEQQhqIAAgAkELTwR/IAJBEGpBcHEiAyADQQFrIgMgA0ELRhsFQQoLQQFqEMMBIAQoAgwaIAAgBCgCCCIDNgIAIAAgACgCCEGAgICAeHEgBCgCDEH/////B3FyNgIIIAAgACgCCEGAgICAeHI2AgggACACNgIECyADIAEgAkEBahB3IARBEGokAA8LEE0ACwoAIABBlIo1EHkLBABBAAuJAgEHfyABIAAoAggiAyAAKAIEIgJrQQJ1TQRAIAAgAQR/IAJBACABQQJ0IgD8CwAgACACagUgAgs2AgQPCwJAIAIgACgCACIFa0ECdSIHIAFqIgRBgICAgARJBEBB/////wMgAyAFayIDQQF1IgggBCAEIAhJGyADQfz///8HTxsiAwRAIANBgICAgARPDQIgA0ECdBAyIQYLIAdBAnQgBmoiBEEAIAFBAnQiAfwLACABIARqIQEgAiAFRwRAA0AgBEEEayIEIAJBBGsiAioCADgCACACIAVHDQALCyAAIAYgA0ECdGo2AgggACABNgIEIAAgBDYCACAFBEAgBRAvCw8LEDYACxBHAAstACACRQRAIAAoAgQgASgCBEYPCyAAIAFGBEBBAQ8LIAAoAgQgASgCBBCfAUULUgEBfyABQQFrIgJBEE0EfyACQQJ0QdypA2ooAgAFQcYiCyECIABB6LUDNgIAIABB7LYDNgIAIABBBGogAhCKAyAAIAE2AgggAEGooQM2AgAgAAvHCQIEfwZ+IwBB8ABrIgYkACAEQv///////////wCDIQkCQAJAIAFQIgUgAkL///////////8AgyIKQoCAgICAgMD//wB9QoCAgICAgMCAgH9UIApQG0UEQCADQgBSIAlCgICAgICAwP//AH0iC0KAgICAgIDAgIB/ViALQoCAgICAgMCAgH9RGw0BCyAFIApCgICAgICAwP//AFQgCkKAgICAgIDA//8AURtFBEAgAkKAgICAgIAghCEEIAEhAwwCCyADUCAJQoCAgICAgMD//wBUIAlCgICAgICAwP//AFEbRQRAIARCgICA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Qd/bv4UETQRAIAC7IQQgA0EASARAIARE0iEzf3zZEkCgEIQBIQAMAwsgBETSITN/fNkSwKAQhAGMIQAMAgtEGC1EVPshGUBEGC1EVPshGcAgA0EASBsgALugEIMBIQAMAQsgAkGAgID8B08EQCAAIACTIQAMAQsCQAJAAkACQCAAIAFBCGoQrwRBA3EOAwABAgMLIAErAwgQgwEhAAwDCyABKwMIEIQBIQAMAgsgASsDCJoQgwEhAAwBCyABKwMIEIQBjCEACyABQRBqJAAgAAuPBAEDfyACQYAETwRAIAAgASAC/AoAACAADwsgACACaiEEAkAgACABc0EDcUUEQAJ/IAAgAEEDcUUNABogACACRQ0AGiAAIAEgAEF/cyICIAQgAEEBaiIDIAMgBEkbaiIDIAJBA3EiAiACIANLG0EBaiIC/AoAACABIAJqIQEgACACagshAgJAIARBfHEiA0HAAEkNACACIANBQGoiBUsNAANAIAIgASgCADYCACACIAEoAgQ2AgQgAiABKAIINgIIIAIgASgCDDYCDCACIAEoAhA2AhAgAiABKAIUNgIUIAIgASgCGDYCGCACIAEoAhw2AhwgAiABKAIgNgIgIAIgASgCJDYCJCACIAEoAig2AiggAiABKAIsNgIsIAIgASgCMDYCMCACIAEoAjQ2AjQgAiABKAI4NgI4IAIgASgCPDYCPCABQUBrIQEgAkFAayICIAVNDQALCyACIANPDQEgAiABIAJBf3MgAyACQQRqIgUgAyAFSxtqQXxxQQRqIgP8CgAAIAEgA2ohASACIANqIQIMAQsgBEEESQRAIAAhAgwBCyAAIARBBGsiA0sEQCAAIQIMAQsgACECA0AgAiABLQAAOgAAIAIgAS0AAToAASACIAEtAAI6AAIgAiABLQADOgADIAFBBGohASACQQRqIgIgA00NAAsLIAIgBEkEQCACIAEgBCACa/wKAAALIAALIAAjAEEQayIBJAAgAEIANwIAIABBADYCCCABQRBqJAALugcBCH8jAEHQAGsiBCQAIAQgACkCGDcDSAJAAkACQAJ/IwBBIGsiAiQAIAQoAkwhBiABIAQoAkgiB3AiCCEDAkADQAJAIAYgA0ECdGoiCSgCACIFRQ0AIAEgBUYNACADQQFqIgNBACADIAdHGyIDIAhHDQEMAgsLIANBf0YNAAJA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KAIAKAIkEQAADAELIAgtAAALwCIGQQBOBH8gAygCCCAGQf8BcUECdGooAgBBwABxQQBHBUEAC0UNACADIAZBACADKAIAKAIkEQMAIQEDQAJAIAAQXRogAUEwayEBIAAgB0EMahBDDQAgBEECSA0AAn8gACgCACIFKAIMIgYgBSgCEEYEQCAFIAUoAgAoAiQRAAAMAQsgBi0AAAvAIgVBAE4EfyADKAIIIAVB/wFxQQJ0aigCAEHAAHFBAEcFQQALRQ0DIARBAWshBCADIAVBACADKAIAKAIkEQMAIAFBCmxqIQEMAQsLQQIhBSAAIAdBDGoQQ0UNAQsgAiACKAIAIAVyNgIACyAHQRBqJAAgAQuZAQEEfyMAQRBrIgQkACAEIAE2AgwgBCADNgIIIARBBGogBEEMahB/IQcgBCgCCCEDIwBBEGsiASQAIAEgAzYCDCABIAM2AghBfyEFAkBBAEEAIAIgAxCwASIDQQBIDQAgACADQQFqIgMQOyIANgIAIABFDQAgACADIAIgASgCDBCwASEFCyABQRBqJAAgBxB+IARBEGokACAFCy4AAkAgACgCBEHKAHEiAARAIABBwABGBEBBCA8LIABBCEcNAUEQDwtBAA8LQQoL+QECA34CfyMAQRBrIgUkAAJ+IAG9IgNC////////////AIMiAkKAgICAgICACH1C/////////+//AFgEQCACQjyGIQQgAkIEiEKAgICAgICAgDx8DAELIAJCgICAgICAgPj/AFoEQCADQjyGIQQgA0IEiEKAgICAgIDA//8AhAwBCyACUARAQgAMAQsgBSACQgAgA6dnQSBqIAJCIIinZyACQoCAgIAQVBsiBkExahBmIAUpAwAhBCAFKQMIQoCAgICAgMAAhUGM+AAgBmutQjCGhAshAiAAIAQ3AwAgACACIANCgICAgICAgICAf4OENwMIIAVBEGokAAsJACAAQQEQtQELKAEBfyAAQdypATYCACAAKAIEIgEEQCABIAEoAgAoAgQRAQALIAAQLwsMACAAKAIAIAEQ6QQLbAECfyAAKAIEIgEEQCABKAIIIgIEQCACEC8LIAEoAgwiAgRAIAIQLwsgASgCFCICBEAgAhAvCyABKAIYIgIEQCACEC8LIAEQLwsgACgCACIBBEAgAS0ABARAIAEoAgAQpwELIAEQLwsgABAvC40F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AgsgACAINgIcIAAgCTYCGCAAIAU2AhQgAkUNASACEC8PCyAAKAIcIQMgACgCGCECAkAgAC0AWgRAIAIgA0kNBSACIAAoAhQiBmsiB0EBaiIFQQBIDQQgB0H/////ByADIAZrIgNBAXQiBCAFIAQgBUsbIANB/////wNPGyIEBH8gBBAyBUEACyIFaiIDIAE6AAAgBCAFaiEIIANBAWohCQJAIAIgBkYEQCADIQUMAQsCQAJAIAdBEEkNACAGIAIgBWprIAJqQRBJDQAgA0EQayEKIAJBEGshCyACIAdBcHEiBGshAiADIARrIQNBACEBA0AgCiABayALIAFr/QAAAP0LAAAgAUEQaiIBIARHDQALIAQgB0YNAQsgBkF/cyACaiENIAIgBmtBA3EiBARAQQAhAQNAIANBAWsiAyACQQFrIgItAAA6AAAgAUEBaiIBIARHDQALCyANQQNJDQADQCADQQFrIAJBAWstAAA6AAAgA0ECayACQQJrLQAAOgAAIANBA2sgAkEDay0AADoAACADQQRrIgMgAkEEayICLQAAOgAAIAIgBkcNAAsLIAAoAhQhAgsgACAINgIcIAAgCTYCGCAAIAU2AhQgAg0BDAILIAIgA0cNBCACIAAoAhQiB2siBkEBaiIDQQBIDQMgBkH/////ByAGQQF0IgUgAyADIAVJGyAGQf////8DTxsiBAR/IAQQMgVBAAsiBWoiAyABOgAAIAQgBWohCCADQQFqIQkCQCACIAdGBEAgAyEFDAELAkACQCAGQRBJDQAgByACIAVqayACakEQSQ0AIANBEGshCiACQRBrIQsgAiAGQXBxIgRrIQIgAyAEayEDQQAhAQNAIAogAWsgCyABa/0AAAD9CwAAIAFBEGoiASAERw0ACyAEIAZGDQELIAdBf3MgAmohDiACIAdrQQNxIgQEQEEAIQEDQCADQQFrIgMgAkEBayICLQAAOgAAIAFBAWoiASAERw0ACwsgDkEDSQ0AA0AgA0EBayACQQFrLQAAOgAAIANBAmsgAkECay0AADoAACADQQNrIAJBA2stAAA6AAAgA0EEayIDIAJBBGsiAi0AADoAACACIAdHDQALCyAAKAIUIQILIAAgCDYCHCAAIAk2AhggACAFNgIUIAJFDQELIAIQLwsPCxA2AAsQNgALIAIgAToAACAAIAJBAWo2AhgLlAQBBH8gASAAIAFGIgI6AAwCQCACDQADQCABKAIIIgItAAwNAQJAIAIgAigCCCIDKAIAIgRGBEACQCADKAIEIgRFDQAgBC0ADA0ADAILAkAgASACKAIARgRAIAIhAQwBCyACIAIoAgQiASgCACIANgIEIAEgAAR/IAAgAjYCCCACKAIIBSADCzYCCCACKAIIIgAgACgCACACR0ECdGogATYCACABIAI2AgAgAiABNgIIIAEoAggiAygCACECCyABQQE6AAwgA0EAOgAMIAMgAigCBCIANgIAIAAEQCAAIAM2AggLIAIgAygCCDYCCCADKAIIIgAgACgCACADR0ECdGogAjYCACACIAM2AgQgAyACNgIIDwsCQCAERQ0AIAQtAAwNAAwBCwJAIAEgAigCAEcEQCACIQEMAQsgAiABKAIEIgA2AgAgASAABH8gACACNgIIIAIoAggFIAMLNgIIIAIoAggiACAAKAIAIAJHQQJ0aiABNgIAIAEgAjYCBCACIAE2AgggASgCCCEDCyABQQE6AAwgA0EAOgAMIAMgAygCBCIAKAIAIgE2AgQgAQRAIAEgAzYCCAsgACADKAIINgIIIAMoAggiASABKAIAIANHQQJ0aiAANgIAIAAgAzYCACADIAA2AggMAgsgBEEMaiEFIAJBAToADCADIAAgA0Y6AAwgBUEBOgAAIAMiASAARw0ACwsLnAIBBH8gACgCiAEiAQRAIAAgATYCjAEgARAvCyAAKAJ8IgEEQCAAIAE2AoABIAEQLwsgACgCcCIBBEAgACABNgJ0IAEQLwsgACgCZCIBBEAgACABNgJoIAEQLwsgACgCRCIDBEAgAyECIAMgACgCSCIBRwRAA0AgAUEMayICKAIAIgQEQCABQQhrIAQ2AgAgBBAvCyACIgEgA0cNAAsgACgCRCECCyAAIAM2AkggAhAvCyAAKAI4IgMEQCADIQIgAyAAKAI8IgFHBEADQCABQQxrIgIoAgAiBARAIAFBCGsgBDYCACAEEC8LIAIiASADRw0ACyAAKAI4IQILIAAgAzYCPCACEC8LIAAoAgAiAQRAIAAgATYCBCABEC8LC3ABA38gARBoIgJB8P///wdJBEACQCACQQpNBEAgACACOgALIAAhAwwBCyACQQ9yQQFqIgQQMiEDIAAgBEGAgICAeHI2AgggACADNgIAIAAgAjYCBAsgAyABIAL8CgAAIAIgA2pBADoAACAADwsQTQALHgEBfyAABEAgACgCACIBBEAgACABEQEACyAAEC8LC3sBAn8jAEEQayICJAAgACgCBCEDIAAgARB9AkAgAyAAKAIEIgNODQAgACgCDCADQQJ0akEEaygCACABRg0AQbzDAigCABAwGiACQekTNgIIIAJByPkANgIEIAJB5CY2AgBBuMMCKAIAQcvkACACEDEQAAALIAJBEGokAAsTACABQQF0QdCqA2pBAiAAEIEEC84BAQF/An8jAEEwayIDJAAgASgCiAFFBEAgACABKAIAIAEoAgwgAUEQakEAQQAQSSIARQRAQbzDAigCABAwGiADQdcvNgIYIANBxhQ2AhQgA0HkJjYCEEG4wwIoAgBBy+QAIANBEGoQMRAAAAsgACABNgKMASAAQQA2AogBIABBEzYCQCAAIAI4AkQgA0EwaiQAIAAMAQtBvMMCKAIAEDAaIANBmyE2AiggA0HcHjYCJCADQeQmNgIgQbjDAigCAEHL5AAgA0EgahAxEAAACwv0AgIDfwh+IwBBMGsiAyQAAn8CQAJAIAEpAxAiBiACKQMQIgeBQgBSDQAgASkDGCIIIAIpAxgiCYFCAFINACABKQMgIgogAikDICILgUIAUg0AIAEpAygiDCACKQMoIg2BQgBSDQAgAUEQaiEEAkAgASgCiAENACACKAKIAQ0ADAILAkAgBiAHUg0AIAggCVINACAKIAtSDQBBASEFIAwgDVENAgtBvMMCKAIAEDAaIANBq9cANgIYIANByBo2AhQgA0HkJjYCEEG4wwIoAgBBy+QAIANBEGoQMRAAAAtBvMMCKAIAEDAaIANBpdgANgIoIANBwho2AiQgA0HkJjYCIEG4wwIoAgBBy+QAIANBIGoQMRAAAAsgACABKAIAIAEoAgwgBEEAQQAQSSIEQQY2AkBBACAFRQ0AGiAAIAQoAgAgBCgCDCAEQRBqQQBBABBJCyEAIAQgATYCjAEgBCAANgKIASAEIAI2ApABIANBMGokACAEC3YBAX8jAEEQayICJAAgAiAANgIMAkAgACABRg0AA0AgAiABQQFrIgE2AgggACABTw0BIAIoAgwiAC0AACEBIAAgAigCCCIALQAAOgAAIAAgAToAACACIAIoAgxBAWoiADYCDCACKAIIIQEMAAsACyACQRBqJAAL2wgBAX9BAEEB/h4CkNY0QQBKBEADQEEAQQH+JQKQ1jQaEAMQ1gFBAEEB/h4CkNY0QQBKDQALCwJAAkAgAEHguwNGDQAgAEGUvANGBEBBASEBDAELIABByLwDRgRAQQIhAQwBCyAAQfy8A0YEQEEDIQEMAQsgAEGwvQNGBEBBBCEBDAELIABB5L0DRgRAQQUhAQwBCyAAQZi+A0YEQEEGIQEMAQsgAEHMvgNGBEBBByEBDAELIABBgL8DRgRAQQghAQwBCyAAQbS/A0YEQEEJIQEMAQsgAEHovwNGBEBBCiEBDAELIABBnMADRgRAQQshAQwBCyAAQdDAA0YEQEEMIQEMAQsgAEGEwQNGBEBBDSEBDAELIABBuMEDRgRAQQ4hAQwBCyAAQezBA0YEQEEPIQEMAQsgAEGgwgNGBEBBECEBDAELIABB1MIDRgRAQREhAQwBCyAAQYjDA0YEQEESIQEMAQsgAEG8wwNGBEBBEyEBDAELIABB8MMDRgRAQRQhAQwBCyAAQaTEA0YEQEEVIQEMAQsgAEHYxANGBEBBFiEBDAELIABBjMUDRgRAQRchAQwBCyAAQcDFA0YEQEEYIQEMAQsgAEH0xQNGBEBBGSEBDAELIABBqMYDRgRAQRohAQwBCyAAQdzGA0YEQEEbIQEMAQsgAEGQxwNGBEBBHCEBDAELIABBxMcDRgRAQR0hAQwBCyAAQfjHA0YEQEEeIQEMAQsgAEGsyANGBEBBHyEBDAELIABB4MgDRgRAQSAhAQwBCyAAQZTJA0YEQEEhIQEMAQsgAEHIyQNGBEBBIiEBDAELIABB/MkDRgRAQSMhAQwBCyAAQbDKA0YEQEEkIQEMAQsgAEHkygNGBEBBJSEBDAELIABBmMsDRgRAQSYhAQwBCyAAQczLA0YEQEEnIQEMAQsgAEGAzANGBEBBKCEBDAELIABBtMwDRgRAQSkhAQwBCyAAQejMA0YEQEEqIQEMAQsgAEGczQNGBEBBKyEBDAELIABB0M0DRgRAQSwhAQwBCyAAQYTOA0YEQEEtIQEMAQsgAEG4zgNGBEBBLiEBDAELIABB7M4DRgRAQS8hAQwBCyAAQaDPA0YEQEEwIQEMAQsgAEHUzwNGBEBBMSEBDAELIABBiNADRgRAQTIhAQwBCyAAQbzQA0YEQEEzIQEMAQsgAEHw0ANGBEBBNCEBDAELIABBpNEDRgRAQTUhAQwBCyAAQdjRA0YEQEE2IQEMAQsgAEGM0gNGBEBBNyEBDAELIABBwNIDRgRAQTghAQwBCyAAQfTSA0YEQEE5IQEMAQsgAEGo0wNGBEBBOiEBDAELIABB3NMDRgRAQTshAQwBCyAAQZDUA0YEQEE8IQEMAQsgAEHE1ANGBEBBPSEBDAELIABB+NQDRgRAQT4hAQwBCyAAQazVA0cNAUE/IQELIAFBNGxB3LsDakEAOgAAIAAtAAhFDQAgACgCBBAvC0EAQQH+JQKQ1jQaC9sBAgF/An5BASEEAkAgAEIAUiABQv///////////wCDIgVCgICAgICAwP//AFYgBUKAgICAgIDA//8AURsNACACQgBSIANC////////////AIMiBkKAgICAgIDA//8AViAGQoCAgICAgMD//wBRGw0AIAAgAoQgBSAGhIRQBEBBAA8LIAEgA4NCAFkEQEF/IQQgACACVCABIANTIAEgA1EbDQEgACAChSABIAOFhEIAUg8LQX8hBCAAIAJWIAEgA1UgASADURsNACAAIAKFIAEgA4WEQgBSIQQLIAQLUAEBfgJAIANBwABxBEAgAiADQUBqrYghAUIAIQIMAQsgA0UNACACQcAAIANrrYYgASADrSIEiIQhASACIASIIQILIAAgATcDACAAIAI3AwgLqQEBA38jAEGgAWsiBCQAIAQgACAEQZ4BaiABGyIFNgKUAUF/IQAgBCABQQFrIgZBACABIAZPGzYCmAEgBEEAQZAB/AsAIARBfzYCTCAEQc8BNgIkIARBfzYCUCAEIARBnwFqNgIsIAQgBEGUAWo2AlQCQCABQQBIBEAjA0EcakE9NgIADAELIAVBADoAACAEIAIgA0HNAUHOARDDAiEACyAEQaABaiQAIAALqAEAAkAgAUGACE4EQCAARAAAAAAAAOB/oiEAIAFB/w9JBEAgAUH/B2shAQwCCyAARAAAAAAAAOB/oiEAQf0XIAEgAUH9F04bQf4PayEBDAELIAFBgXhKDQAgAEQAAAAAAABgA6IhACABQbhwSwRAIAFByQdqIQEMAQsgAEQAAAAAAABgA6IhAEHwaCABIAFB8GhMG0GSD2ohAQsgACABQf8Haq1CNIa/ogswAQF/IAEjAyICKAJIIABBAnRqIgAoAgBHBEAgACAB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NEHA1zQoAgBBAWo2AgBBAQs6AAQgACABNgIAIAVBEGokAAuIAQEDf0GcEBA7IQEgABDoBCEDIAEgACgCHCICIAIoAgQRAAAiAjYCDCABIAM2AgggAUEANgIEIAEgADYCACABQRxqQQBBgBD8CwAgAUEBNgIQIAEgAyACIAMgAnBrIAJwIgJqNgIUIAEgACgCJCACazYCGEEIEDsiABCCBDYCBCAAIAE2AgAgAAuTwwcF1gF/On4gewZ9BHwjAEFAaiIuJAACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCAABEACQCABKAJAIgJFDQACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIAJBAWsOSEMAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobRERERBwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ2NzU4OTo7PD0+P0BBQkQLIAEoAowBIQUgASgCkAEhAyMAQYADayIEJAACQAJAIAUoAgAiAkEOSw0AAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkBBASACdEHM+wFxRQRAAkACQAJAIAIEQCACQQFHDRggAygCAA4CAgEDCyAFKQMQItkBINkBIAMpAxAi2gF/It0BINoBflINBSAFKQMYItsBIAMpAxgi3gGBQgBSDQUgBSkDICLYASADKQMgIt8BgUIAUg0FIAUpAygi4AEgAykDKCLhAYFCAFINBSDZASABKQMQUg0FINsBIAEpAxhSDQUg2AEgASkDIFINBSDgASABKQMoUg0FAkAgACgCAA4DBQAFAAsgASgCMEEERw0GIAUoAjBBBEcNByABKAI8IQkgASgCOCEKIAEoAjQhCCADKAI8IQsgAygCOCEMIAMoAjQhDSAFKAI8IQ8gBSgCOCEOIAUoAjQhECDgASDYASDbAX4i3AF+pyICIAAoAggiB2pBAWsgB20iByAAKAIEbCIAIAdqIgcgAiACIAdKGyEHIAMoAjAiAkEERwRAIAAgB04N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y+QAIANBgAJqEDEMTgtBvMMCKAIAEDAaIANBycAANgKYAiADQevMADYClAIgA0HkJjYCkAJBuMMCKAIAQcvkACADQZACahAxDE0LQbzDAigCABAwGiADQb3AADYCGCADQbrOADYCFCADQeQmNgIQQbjDAigCAEHL5AAgA0EQahAxDEwLQbzDAigCABAwGiADQcnAADYCKCADQbnOADYCJCADQeQmNgIgQbjDAigCAEHL5AAgA0EgahAxDEsLQbzDAigCABAwGiADQd3QADYCOCADQbTOADYCNCADQeQmNgIwQbjDAigCAEHL5AAgA0EwahAxDEoLQbzDAigCABAwGiADQdfUADYCSCADQbHOADYCRCADQeQmNgJAQbjDAigCAEHL5AAgA0FAaxAxDEkLQbzDAigCABAwGiADQcg6NgJYIANBrs4ANgJUIANB5CY2AlBBuMMCKAIAQcvkACADQdAAahAxDEgLQbzDAigCABAwGiADQfE8NgJoIANBrc4ANgJkIANB5CY2AmBBuMMCKAIAQcvkACADQeAAahAxDEcLQbzDAigCABAwGiADQdQ6NgJ4IANBrM4ANgJ0IANB5CY2AnBBuMMCKAIAQcvkACADQfAAahAxDEYLQbzDAigCABAwGiADQf08NgKIASADQavOADYChAEgA0HkJjYCgAFBuMMCKAIAQcvkACADQYABahAxDEULINgBINsBfiDiAX4g2gF+pyIAQQBMDQELIAQoAtABQQAgAEECdPwLAAsgA0GgCGokAAwBC0G8wwIoAgAQMBogA0GbITYCCCADQafPADYCBCADQeQmNgIAQbjDAigCAEHL5AAgAxAxDEELDCwLIAEoAowBIQcgASgCkAEhBCABIQIjAEHQAGsiBSQAAkACQCAHKAIARQRAAkAgBygCMEEERw0AIAcoAjQiDK0i2QEgBykDECLYAUIChlINACAHNQI4ItsBINkBIAcpAxgi2gF+Ug0AIAc1Ajwg2wEgBykDICLZAX5SDQACQCABKAIwIgEgAigCAEEkbCIDQZibAWooAgBHDQAgAigCNCIIrSLbASACKQMQItwBIAGtfiADQZSbAWo0AgB/Ug0AIAI1Ajgi3QEg2wEgAikDGCLeAX5SDQAgAjUCPCDdASACKQMgItsBflINAAJAINgBINwBUg0AINoBIN4BUg0AINkBINsBUg0AIAcpAygi2wEgAikDKFINAAJAIAQpAxBCAVINACAEKQMYQgFSDQAgBCkDIEIBUg0AIAQpAyhCAVINAAJAIAAoAgAOAwcABwALINkBINoBfiDbAX6nIgEgACgCCCIDakEBayADbSIGIAAoAgRsIgMgBmoiACABIAAgAUgbIgsgA0wNBiDYAaciBkECdCENIAQoAtABKgIAIrIC/RMhkgIgBkFwcSIBQQBMDQUgASAGQQ9xIg4gBkEDcSIQayIRaiEEIAFBAWsiEkEEdkEBaiIAQf7///8BcSETIABBAXEhFgNAIAMgCGwhACACKALQASIJIAcoAtABIgpHBH8gACAJaiAKIAMgDGxqIA38CgAAIAIoAtABBSAJCyAAaiEKQQAhCUEAIQ8gEkEPRwRAA0AgCiAJQQJ0IhVqIgAgkgIgAP0AAAD95gH9CwAAIAAgkgIgAP0AABD95gH9CwAQIAAgkgIgAP0AACD95gH9CwAgIAAgkgIgAP0AADD95gH9CwAwIAogFUHAAHJqIgAgkgIgAP0AAAD95gH9CwAAIAAgkgIgAP0AABD95gH9CwAQIAAgkgIgAP0AACD95gH9CwAgIAAgkgIgAP0AADD95gH9CwAwIAlBIGohCSAPQQJqIg8gE0cNAAsLIBYEQCAKIAlBAnRqIgAgkgIgAP0AAAD95gH9CwAAIAAgkgIgAP0AABD95gH9CwAQIAAgkgIgAP0AACD95gH9CwAgIAAgkgIgAP0AADD95gH9CwAwCwJAIAEgBk4NAEEAIQkgASEAIA5BBE8EQANAIAogASAJakECdGoiACCSAiAA/QACAP3mAf0LAgAgCUEEaiIJIBFHDQALIAQhACAQRQ0BCwNAIAogAEECdGoiCSCyAiAJKgIAlDgCACAAQQFqIgAgBkcNAAsLIANBAWoiAyALRw0ACwwGC0G8wwIoAgAQMBogBUHU2AA2AgggBUG2zwA2AgQMOgtBvMMCKAIAEDAaIAVBuc8ANgIYIAVBtc8ANgIUDD4LQbzDAigCABAwGiAFQfTLADYCKCAFQbTPADYCJAw+C0G8wwIoAgAQMBogBUHf2QA2AjggBUGzzwA2AjQgBUHkJjYCMEG4wwIoAgBBy+QAIAVBMGoQMQxCC0G8wwIoAgAQMBogBUGbITYCSCAFQeXPADYCRCAFQeQmNgJAQbjDAigCAEHL5AAgBUFAaxAxDEELIAcoAtABIQQgAigC0AEhCSABIAZIBEAgBkEPcSEAIAQgCUcEQCABIAAgBkEDcSIOayIQaiEEIABBBEkhEQNAIAMgCGwhACAHKALQASIKIAlHBEAgACAJaiAKIAMgDGxqIA38CgAAIAIoAtABIQkLIAAgCWohD0EAIQogASEAAkAgEUUEQANAIA8gASAKakECdGoiACCSAiAA/QACAP3mAf0LAgAgCkEEaiIKIBBHDQALIAQhACAORQ0BCwNAIA8gAEECdGoiCiCyAiAKKgIAlDgCACAAQQFqIgAgBkcNAAsLIANBAWoiAyALRw0ACwwCCyABIAAgBkEDcSIHayIMaiECIABBBEkhDQNAIAkgAyAIbGohBEEAIQogASEAAkAgDUUEQANAIAQgASAKakECdGoiACCSAiAA/QACAP3mAf0LAgAgCkEEaiIKIAxHDQALIAIhACAHRQ0BCwNAIAQgAEECdGoiCiCyAiAKKgIAlDgCACAAQQFqIgAgBkcNAAsLIANBAWoiAyALRw0ACwwBCyAEIAlGDQAgA0EBaiG2ASALIANrQQFxBEAgAigC0AEiASAHKALQASIERwRAIAEgAyAIbGogBCADIAxsaiAN/AoAAAsgA0EBaiEDCyC2ASALRg0AA0AgAigC0AEiACAHKALQASIBRwRAIAAgAyAIbGogASADIAxsaiAN/AoAAAsgAigC0AEiACAHKALQASIBRwRAIAAgA0EBaiIEIAhsaiABIAQgDGxqIA38CgAACyADQQJqIgMgC0cNAAsLIAVB0ABqJAAMKwsgASgCjAEhAiABKAKQASEDIwBB0ABrIgQkAAJAAkACQAJAAkAgAigCAEUEQCACKQMQItkBIAEpAxBSDQEgAikDGCLYASABKQMYUg0BIAIpAyAi2gEgASkDIFINASACKQMoItsBIAEpAyhSDQEgASgCMCIFIAEoAgAiC0EkbCIHQZibAWooAgBHDQIgASgCNCIMrSLcASDZASAFrX4i3gEgB0GUmwFqKAIAIg2sf1INAiABKAI4Ig+tIt0BINgBINwBflINAiABKAI8Ig6tIt8BINoBIN0BflINAiACKAIwQQRHDQIgAjUCNCLgASDZAUIChlINAiACNQI4IuEBINgBIOABflINAiACNQI8INoBIOEBflINAiABKAJQIQYgASgCTCEJIAEoAkghCiABKAJEIQgCQCABKAJURQRAIAAoAgAiBw0BIAEoAtABIAIoAtABIAtBEGtBcU0EfyAMINgBp0EBa2wgBSDZAadsaiAPINoBp0EBa2xqIA4g2wGnQQFrbGoFINoBQgF9IN0BfiDYAUIBfSDcAX58INsBQgF9IN8Bfnwg3gEgDa1/fKcL/AoAAAsgACgCACEHCwJAAkAgBw4DAQABAAsgCCADKQMYItgBQgF9ItoBQgAg2AEg2gFaG6dsIAZqIAogAykDICLaAUIBfSLZAUIAINkBINoBWBunbGogCSADKQMoItkBQgF9ItsBQgAg2QEg2wFaG6dsaiACKAIAQSRsQZibAWooAgAgAykDECLbAUIBfSLcAUIAINsBINwBWhunbGohBSADKAI8IQ0gAygCOCEPIAMoAjQhDiABKQMoIdwBIAEpAxAh3QEgASkDICHeASABKQMYId8BIAMoAjAhtwEgACgCCCECIAAoAgQhCwJ/IAEoAgAiAEEQa0FxTQRAIABBJGxBmJsBaigCACABKAIwIN0Bp0EBa2xqIAEoAjQg3wGnQQFrbGogASgCOCDeAadBAWtsaiABKAI8INwBp0EBa2xqDAELIAE1AjQg3wFCAX1+IN0BIAE1AjB+IABBJGxBlJsBajUCAH98IAE1Ajgg3gFCAX1+fCABNQI8INwBQgF9fnynCyAFSQ0EILcBQQRHDQUgAiDZASDYASDaAX4i3AF+pyIAakEBayACbSICIAtsIgcgAiAHaiICIAAgACACShsiAE4NACDbAaciBUEATA0AIAMoAtABIQsgBUEDcSEMIAVBfHEhAiAGIAEoAtABIhBqIREgAKwh2QEgB6wh2gEgBUEQSSESA0AgCyANINoBINwBfyLbAaciAGwiE2ogDyDaASDbAcQg3AF+fSLbASDYAX8i3QGnIgNsIhZqIA4g2wEg2AEg3QF+faciB2wiFWohASAHIAhsIhggAyAKbCIaIBAgACAJbCIZampqIAZqIQNBACEHQQAhAAJAAkAgEg0AIBEgGGogGWogGmogCyAVaiATaiAWamtBEEkNAANAIAMgAEECdCITaiABIBNq/QACAP0LAgAgAEEEaiIAIAJHDQALIAIiACAFRg0BCyAAQX9zIAVqIbgBIAwEQANAIAMgAEECdCIWaiABIBZqKgIAOAIAIABBAWohACAHQQFqIgcgDEcNAAsLILgBQQNJDQADQCADIABBAnQiB2ogASAHaioCADgCACADIAdBBGoiE2ogASATaioCADgCACADIAdBCGoiE2ogASATaioCADgCACADIAdBDGoiB2ogASAHaioCADgCACAAQQRqIgAgBUcNAAsLINoBQgF8ItoBINkBUg0ACwsgBEHQAGokAAwFC0G8wwIoAgAQMBogBEGbITYCSCAEQcvQADYCRAw/C0G8wwIoAgAQMBogBEG5zwA2AjggBEHxzwA2AjQMPwtBvMMCKAIAEDAaIARBxNkANgIoIARB8s8ANgIkDD8LQbzDAigCABAwGiAEQYzMADYCCCAEQZrQADYCBAw2C0G8wwIoAgAQMBogBEGJ0QA2AhggBEGc0AA2AhQMPgsMKgsgACABKAKMASABEPUCDCkLIAAgASgCjAEgARD1AgwoCyABKAKMASEEIAEoApABIQIjAEEQayIFJAACQAJAAkACQAJAAkAgBCgCACIDQQZrQQlJDQAgAw4EAgEAAAQLAkAgACgCAA4DAwADAAsgAikDICLbAUIAVw0CIAIpAxgi3AFCAFcNAiACKQMQIt0BQgBXDQIgASgCPCEHIAEoAjghBiABKAI0IQkgAigCOCEKIAIoAjQhCCAEKAI8IQsgBCgCOCEMIAQoAjQhDSADQSRsQaCbAWooAgAhAyACKAIwIQ8gBCgCECEOA0AgByDaAaciAGwhECAAIAtsIREgACAKbCESQgAh2AEDQCAGINgBpyIAbCETIAAgDGwhFiAAIAhsIQBCACHZAQNAIAQoAtABIAIoAtABIA8g2QGnIhVsaiAAaiASaigCACANbGogFmogEWogASgC0AEgCSAVbGogE2ogEGogDiADEQUAINkBQgF8ItkBIN0BUg0ACyDYAUIBfCLYASDcAVINAAsg2gFCAXwi2gEg2wFSDQALDAILAkAgACgCAA4DAgACAAsgAikDICLbAUIAVw0BIAIpAxgi3AFCAFcNASACKQMQIt0BQgBXDQEgBCgCECIAQQBMDQEgASgCPCEGIAEoAjghCSABKAI0IQogAigCOCEIIAIoAjQhCyAEKAI8IQwgBCgCOCENIAQoAjQhDyACKAIwIQ4gASgC0AEhECAEKALQASERIAIoAtABIRIgAEF8cSETIABBA3EhBCAAQQRJIRYD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JAACQCAEKAIARQRAIAAgBCABQwAAAAAQ7QQgAkEQaiQADAELQbzDAigCABAwGiACQZshNgIIIAJBpdQANgIEDCoLDCMLIAEoAowBIQsgASgCkAEhDCABIQMjAEEQayINJAACQCALKAIARQRAAkACQCAAKAIADgMBAAEACyAAKAIIIQEgACgCBCECIAwEfiAMKQMYBUIBCyHaASABIAspAyggCykDICALKQMYfn6nIgRqQQFrIAFtIgEgAmwiBiABIAZqIgEgBCABIARIGyIJTg0AIAsoAhAiB0FwcSIBIAdBD3EiDyAHQQNxIgprIg5qIQQgACgCECAHQRBqIAJsQQJ0aiIFIAdBAnQiGmohGSAHQXxxIQIgB0EBayEQIAFBAWsiEUEEdkEBaiIAQf7///8BcSESIABBAXEhEyADKgJEIrQC/RMhkgIgCawh2QEgBqwh2AEgB0EISSEWA0AgAygCNCG8ASADKALQASEXIAsoAjQhACALKALQASEGIAwEfyAMKALQASAMKAI0INgBINoBgadsagVBAAshCSDYAachFQJAIAdBAEwiGA0AIAYgACAVbGohCEEAIQZBACEAAkAgFg0AIAUgCGtBEEkNAANAIAUgAEECdCIbaiAIIBtq/QACAP0LAgAgAEEEaiIAIAJHDQALIAcgAiIARg0BCyAQIABrIbsBIAoEQANAIAUgAEECdCIcaiAIIBxqKgIAOAIAIABBAWohACAGQQFqIgYgCkcNAAsLILsBQQNJDQADQCAFIABBAnQiBmogBiAIaioCADgCACAFIAZBBGoiG2ogCCAbaioCADgCACAFIAZBCGoiG2ogCCAbaioCADgCACAFIAZBDGoiBmogBiAIaioCADgCACAAQQRqIgAgB0cNAAsLAkAgAUEATCIbDQBBACEIQQAhBiARQQ9HBEADQCAFIAhBAnQiHGoiACCSAiAA/QAAAP3mAf0LAAAgACCSAiAA/QAAEP3mAf0LABAgACCSAiAA/QAAIP3mAf0LACAgACCSAiAA/QAAMP3mAf0LADAgBSAcQcAAcmoiACCSAiAA/QAAAP3mAf0LAAAgACCSAiAA/QAAEP3mAf0LABAgACCSAiAA/QAAIP3mAf0LACAgACCSAiAA/QAAMP3mAf0LADAgCEEgaiEIIAZBAmoiBiASRw0ACwsgE0UNACAFIAhBAnRq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swIgtAJeGyK0AiCyAiCyAiC0Al0bOAIAIABBAXIhAAsgwwEgDUYNAANAIAMgAEECdCILaiABIAtqKgIAIrQCILMCILMCILQCXhsitAIgsgIgsgIgtAJdGzgCACADIAtBBGoiC2ogASALaioCACK0AiCzAiCzAiC0Al4bIrQCILICILICILQCXRs4AgAgAEECaiIAIAdHDQALCyAGQQFqIQYgBSAKaiIFIAxIDQALCyAEQTBqJAAMAgtBvMMCKAIAEDAaIARBttEANgIIIARBr9cANgIEDCkLQbzDAigCABAwGiAEQd3QADYCGCAEQa7XADYCFAwxCwwdCyABKAKMASECIAEoApABIQMjAEGQAWsiBCQAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgAigCAA4CAQALCyADKAIADQIgASgCAA0DIAIoAjBBAkcNBCADKAIwQQRHDQUgAikDICLfASACKQMQItwBfiLiASACKQMYIuMBfqchByADKQMYIeEBIAMpAxAh4AECQAJAIAAoAgAOAwABAwELIAMoAjQhCiACKAI4IQYgAigCNCEJIAAoAhBBACAAKAIM/AsAIAAoAhAhBQJAIN8BQgBXDQAg4wFCAFcNACDcAUIAVw0AIN8BQgFSINwBQghUciEIINwBQgODIeQBINwBQniDIdoBINwBQgGGIeUBIAmtIeYBIAatIecBIAIoAtABIgutIegBIAWtIekBA0Ag2QEg5wF+IOgBfCHqASDZAUIBhiDpAXwh6wEgCyAGINkBp2xqIQxCACHdAQNAIAwgCSDdAadsaiEAIAUg3QEg4gF+p0EBdGohAkIAId4BQgAh2wFCACHYAQJAIAgg6wEg3QEg5QF+fKcg6gEg3QEg5gF+fKdrQRBJckUEQANAIAIg2QEg2wF8p0EBdGogACDbAadBAXRq/QABAP0LAQAg2wFCCHwi2wEg2gFSDQALINoBItgBINwBUQ0BCyDcASDYAUJ/hXwhiwIg5AFCAFIEQANAIAIg2AEg3wF+INkBfKdBAXRqIAAg2AGnQQF0ai8BADsBACDYAUIBfCHYASDeAUIBfCLeASDkAVINAAsLIIsCQgNUDQADQCACINgBIN8BfiDZAXynQQF0aiAAINgBp0EBdGovAQA7AQAgAiDYAUIBfCLbASDfAX4g2QF8p0EBdGogACDbAadB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ASAANQIwfiAHQSRsQZSbAWo1AgB/fCAAKAI4IgetINgBQgF9fnwgADUCPCAAKQMoQgF9fnynCyIAQQBMDQAgASkDGCLdAUIAVw0AIAEpAxAi2wFCAFcNAAJAAkAgA0ECRwRAIAAgBGohCSABKALQASEAINsBIN0BfqchCiAIIAtssiGyAiDbAachBiAIQQBMDQEgC0EATA0CIAutItgBQv7///8PgyHiASDYAUIBgyHjASDYAUIBfSHkASAIrSHlASADQQFrIQsDQEIAId8BA0AgACDfAaciASAGbEECdGohECABIA5sIA9rrCHmAUIAIeABA0AgECDgAaciCEECdGohAQJAAkACQCADDgIBAAILIAFBADYCACAIIA1sIAxrrCHhAUIAIdoBA0ACQCDaASDmAXwi2AFCAFMNACDeASDYAUL/////D4NXDQAgBCACINgBp2xqIQhCACHYAUIAIdwBIOQBQgBSBEADQAJAINgBIOEBfCLnAUIAUw0AINkBIOcBQv////8Pg1cNACABIAgg5wGnQQJ0aioCACABKgIAkjgCAAsCQCDYAUIBhCDhAXwi5wFCAFMNACDZASDnAUL/////D4NXDQAgASAIIOcBp0ECdGoqAgAgASoCAJI4AgALINgBQgJ8IdgBINwBQgJ8ItwBIOIBUg0ACwsg4wFQDQAg2AEg4QF8ItgBQgBTDQAg2QEg2AFC/////w+DVw0AIAEgCCDYAadBAnRqKgIAIAEqAgCSOAIACyDaAUIBfCLaASDlAVINAAsMAQsgAUH///97NgIAIAggDWwgDGusIeEBQgAh2gEDQAJAINoBIOYBfCLYAUIAUw0AIN4BINgBQv////8Pg1cNACAEIAIg2AGnbGohCEIAIdgBQgAh3AEg5AFCAFIEQANAAkAg2AEg4QF8IucBQgBTDQAg2QEg5wFC/////w+DVw0AIAgg5wGnQQJ0aioCACKzAiABKgIAXkUNACABILMCOAIACwJAINgBQgGEIOEBfCLnAUIAUw0AINkBIOcBQv////8Pg1cNACAIIOcBp0ECdGoqAgAiswIgASoCAF5FDQAgASCzAjgCAAsg2AFCAnwh2AEg3AFCAnwi3AEg4gFSDQALCyDjAVANACDYASDhAXwi2AFCAFMNACDZASDYAUL/////D4NXDQAgCCDYAadBAnRqKgIAIrMC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IAVBfnEhCQNAIAcgASgC0AEgASgCNCAAbGogAigC0AEgAigCNCAAbGogAxEFACAHIAEoAtABIABBAXIiCiABKAI0bGogAigC0AEgAigCNCAKbGogAxEFACAAQQJqIQAgBkECaiIGIAlHDQALCyAFQQFxRQ0AIAcgASgC0AEgASgCNCAAbGogAigC0AEgAigCNCAAbGogAxEFAAsgBEEgaiQADAILQbzDAigCABAwGiAEQZshNgIYIARB+eoANgIUDCALQbzDAigCABAwGiAEQbnPADYCCCAEQdrqADYCBAwWCwwLCyABKAKMASECIAEoApABIQMgASgCRCEFIwBBEGsiBCQAAkAgAigCAEUEQAJAAkAgACgCAA4DAQABAAsgAikDKCACKQMgIAIpAxh+fqciB0EATA0AIAIoAhAhBkEAIQADQCAGIAEoAtABIAEoAjQgAGxqIAIoAtABIAIoAjQgAGxqIAMoAtABIAMoAjQgAGxqIAURBgAgAEEBaiIAIAdHDQALCyAEQRBqJAAMAQtBvMMCKAIAEDAaIARBmyE2AgggBEGp6wA2AgQMFQsMCgsCQCAAKAIADgMKAAoACyABIAEoAowBIAEoAkQRAgAMCQsCQCAAKAIADgMJAAkACyABIAEoAowBIAEoApABIAEoAkQRBQAMCAsCQCAAKAIADgMIAAgACyABIAEoAowBIAEoApABIAEoApQBIAEoAkQRBgAMBwsgASgCjAEhAgJAAkAgACgCAA4DAQABAAsgASACIAAoAgQgACgCCCABKAJMIAEoAkQRCwALDAYLIAEoAowBIQIgASgCkAEhBAJAAkAgACgCAA4DAQABAAsgASACIAQgACgCBCAAKAIIIAEoAkwgASgCRBEMAAsMBQsgASgCjAEhAiABKAKQASEEIAEoApQBIQMCQAJAIAAoAgAOAwEAAQALIAEgAiAEIAMgACgCBCAAKAIIIAEoAkwgASgCRBEQAAsMBAsgACEEIAEoAowBIQkgASgCkAEhCiMAQeAAayIFJAACQAJAAn0CQCAJKAIARQRAAkAgCSgCMEEERw0AIAk1AjQi2QEgCSkDECLYAUIChlINACAJNQI4ItsBINkBIAkpAxgi2gF+Ug0AIAk1Ajwg2wEgCSkDICLZAX5SDQACQCAKKAIwIgAgCigCAEEkbCICQZibAWooAgBHDQAgCjUCNCLbASAKKQMQItwBIACtfiACQZSbAWo0AgB/Ug0AIAo1Ajgi3QEg2wEgCikDGCLeAX5SDQAgCjUCPCDdASAKKQMgItsBflINAAJAIAEpAxBCAVINACABKQMYQgFSDQAgASkDIEIBUg0AIAEpAyhCAVINAAJAINgBINwBUg0AINoBIN4BUg0AINkBINsBUg0AIAkpAygi2wEgCikDKFINACAEKAIIIgAg2AGnIgZBAnQiE0EEamwiAyAEKAIMTQRAINkBINoBfiDbAX6nIQIgBCgCECEHIAQoAgQhCAJAAkACQCAEKAIADgMAAgECCyAIDQogB0EAIAP8CwAMCgsgCA0JIAEoAtABIQlDAAAAACAAQQBMDQgaIABBA3EhBEEAIQMgAEEESQRAQQAhAAwICyAAQXxxIQpBACEAQQAhBgNAILgCIAcgAEECdCIBaioCALugIAcgAUEEcmoqAgC7oCAHIAFBCHJqKgIAu6AgByABQQxyaioCALugIbgCIABBBGohACAGQQRqIgYgCkcNAAsMBwsgACACakEBayAAbSIBIAhsIg0gASANaiIBIAIgASACSBsiFk4NCCAGQXBxIgIgBkEPcSIVIAZBA3EiDGsiGGohAyAAQQJ0IhogCEECdCIAQQRqIAZsaiEZIAZBfHEhASAGQX5xIRQgBkEBcSEXIAZBAWshDiAAIAdqIRAgAkEBayIbQQR2QQFqIgBB/v///wFxIRwgAEEBcSEdIAYgCGxBAnQhHwNAIAQoAhAiHiAaaiAfaiEHIAooAjQhIyAKKALQASERAkAgBkEATCIlBEBEAAAAAAAAAAAhuAIMAQsgCSgC0AEgCSgCNCANbGohC0MAAID/IbICQQAhD0EAIQBBACESIA5BA08EQANAILICIAsgAEECdCIIaioCACKzAiCyAiCzAl4bIrICIAsgCEEEcmoqAgAiswIgsgIgswJeGyKyAiALIAhBCHJqKgIAIrMCILICILMCXhsisgIgCyAIQQxyaioCACKzAiCyAiCzAl4bIbICIABBBGohACASQQRqIhIgAUcNAAsLIAwEQANAILICIAsgAEECdGoqAgAiswIgsgIgswJeGyGyAiAAQQFqIQAgD0EBaiIPIAxHDQALC0QAAAAAAAAAACG4AkEAIQBBACEIIA4EQANAQwAAAAAhswICQCALIABBAnQiD2oqAgAitAJDAACA/1sEQEMAAAAAIbQCDAELILgCILQCILICkxBFIrQCu6AhuAILIAcgD2ogtAI4AgAgCyAAQQFyQQJ0Ig9qKgIAIrQCQwAAgP9cBEAguAIgtAIgsgKTEEUiswK7oCG4AgsgByAPaiCzAjgCACAAQQJqIQAgCEECaiIIIBRHDQALCyAXRQ0AQwAAAAAhswIgCyAAQQJ0IgBqKgIAIrQCQwAAgP9cBEAguAIgtAIgsgKTEEUiswK7oCG4AgsgACAHaiCzAjgCAAtEoY92////7z8guAKjtiGyAgJAIAJBAEwNACCyAv0TIZICQQAhAEEAIQggG0EPRwRAA0AgByAAQQJ0Ig9qIgsgkgIgC/0AAAD95gH9CwAAIAsgkgIgC/0AABD95gH9CwAQIAsgkgIgC/0AACD95gH9CwAgIAsgkgIgC/0AADD95gH9CwAwIAcgD0HAAHJqIgsgkgIgC/0AAAD95gH9CwAAIAsgkgIgC/0AABD95gH9CwAQIAsgkgIgC/0AACD95gH9CwAgIAsgkgIgC/0AADD95gH9CwAwIABBIGohACAIQQJqIgggHEcNAAsLIB1FDQAgByAAQQJ0aiIAIJICIAD9AAAA/eYB/QsAACAAIJICIAD9AAAQ/eYB/QsAECAAIJICIAD9AAAg/eYB/QsAICAAIJICIAD9AAAw/eYB/QsAMAsCQCACIAZODQAgAiEAIBVBBE8EQCCyAv0TIZICQQAhAANAIAcgACACakECdGoiCCAI/QACACCSAv3mAf0LAgAgAEEEaiIAIBhHDQALIAMhACAMRQ0BCwNAIAcgAEECdGoiCCAIKgIAILIClDgCACAAQQFqIgAgBkcNAAsLIBAgJQR9QwAAAAAFQQAhAAJAIAZBBEkiCEUEQANAIAcgAEECdGoiCyAL/QACAP0MX3CJMF9wiTBfcIkwX3CJMP3kAf0LAgAgAEEEaiIAIAFHDQALIAEiACAGRg0BCwNAIAcgAEECdGoiCyALKgIAQ19wiTCSOAIAIABBAWoiACAGRw0ACwsgDSAjbCEPQQAhAAJAIAhFBEADQCAHIABBAnRqIgggCP0AAgAikgL9HwAQV/0TIJIC/R8BEFf9IAEgkgL9HwIQV/0gAiCSAv0fAxBX/SAD/QsCACAAQQRqIgAgAUcNAAsgASIAIAZGDQELA0AgByAAQQJ0aiIIIAgqAgAQVzgCACAAQQFqIgAgBkcNAAsLIA8gEWohC0EAIQhBACEAAkACQCAGQQhJDQAgCyAZIB5qSSARIBNqIA9qIAdLcQ0AA0AgByAAQQJ0Ig9qIhEgEf0AAgAgCyAPav0AAgD95gH9CwIAIABBBGoiACABRw0ACyABIgAgBkYNAQsgAEF/cyAGaiHVASAMBEADQCAHIABBAnQiEWoiEiASKgIAIAsgEWoqAgCUOAIAIABBAWohACAIQQFqIgggDEcNAAsLINUBQQJNDQADQCAHIABBAnQiCGoiDyAPKgIAIAggC2oqAgCUOAIAIAcgCEEEaiIPaiIRIBEqAgAgCyAPaioCAJQ4AgAgByAIQQhqIg9qIhEgESoCACALIA9qKgIAlDgCACAHIAhBDGoiCGoiDyAPKgIAIAggC2oqAgCUOAIAIABBBGoiACAGRw0ACwtEAAAAAAAAAAAhuAJBACEIQQAhAEEAIQ8gDkEDTwRAA0AguAIgByAAQQJ0IgtqKgIAu6AgByALQQRyaioCALugIAcgC0EIcmoqAgC7oCAHIAtBDHJqKgIAu6AhuAIgAEEEaiEAIA9BBGoiDyABRw0ACwsgDARAA0AguAIgByAAQQJ0aioCALugIbgCIABBAWohACAIQQFqIgggDEcNAAsLILgCtgsgECoCAJI4AgAgFiANQQFqIg1HDQALDAgLQbzDAigCABAwGiAFQanWADYCCCAFQabsADYCBCAFQeQmNgIAQbjDAigCAEHL5AAgBRAxDCALQbzDAigCABAwGiAFQYvZADYCGCAFQZvsADYCFCAFQeQmNgIQQbjDAigCAEHL5AAgBUEQahAxDB8LQbzDAigCABAwGiAFQeXNADYCKCAFQZrsADYCJCAFQeQmNgIgQbjDAigCAEHL5AAgBUEgahAxDB4LQbzDAigCABAwGiAFQbvYADYCOCAFQZnsADYCNCAFQeQmNgIwQbjDAigCAEHL5AAgBUEwahAxDB0LQbzDAigCABAwGiAFQd/ZADYCSCAFQZjsADYCRCAFQeQmNgJAQbjDAigCAEHL5AAgBUFAaxAxDBwLQbzDAigCABAwGiAFQZshNgJYIAVBie0ANgJUIAVB5CY2AlBBuMMCKAIAQcvkACAFQdAAahAxDBsLIAQEQANAILgCIAcgAEECdGoqAgC7oCG4AiAAQQFqIQAgA0EBaiIDIARHDQALCyC4ArYLIbICIAlDAACAvyACspUgsgKUOAIACyAFQeAAaiQADAALDAMLIAEoAowBIQkgASgCkAEhCiABKAKUASECIAEhAyMAQeAAayIFJAACQCAJKAIARQRAAkAgASgCMCIBIAMoAgBBJGwiBEGYmwFqKAIARw0AIAM1AjQi2QEgAykDECLYASABrX4gBEGUmwFqNAIAf1INACADNQI4ItsBINkBIAMpAxgi2gF+Ug0AIAM1Ajwg2wEgAykDICLZAX5SDQACQCAJKAIwQQRHDQAgCTUCNCLdASAJKQMQItsBQgKGUg0AIAk1Ajgi3gEg3QEgCSkDGCLcAX5SDQAgCTUCPCDeASAJKQMgIt0BflINAAJAIAooAjAiASAKKAIAQSRsIgRBmJsBaigCAEcNACAKNQI0It4BIAopAxAi3wEgAa1+IARBlJsBajQCAH9SDQAgCjUCOCLgASDeASAKKQMYIuEBflINACAKNQI8IOABIAopAyAi3gF+Ug0AAkAgAigCMCIBIAIoAgBBJGwiBEGYmwFqKAIARw0AIAI1AjQi4AEgAikDECABrX4gBEGUmwFqNAIAf1INACACNQI4IuIBIAIpAxgg4AF+Ug0AIAI1AjwgAikDICDiAX5SDQACQCDbASDfAVINACDcASDhAVINACDdASDeAVINACDZASDdAVINACDaASDcAVINACDYASDbAVINACAJKQMoItsBIAopAyhSDQAg2wEgAykDKFINAAJAAkAgACgCAA4DAQABAAsg2QEg2gF+INsBfiLZASAANAIIItoBfEIBfSDaAX8i2wEgADQCBH4i2gEg2gEg2wF8ItsBINkBINkBINsBVRsi3AFZDQAgAigC0AEhGCDYAaciBkFwcSIBIAZBD3EiDSAGQQNxIgxrIg9qIQQg2AFCfoMh3QEg2AFCAYMh3gEgBkF8cSECIAZBAnQhDiABQQFrIhBBBHZBAWoiAEH+////AXEhESAAQQFxIRIg2QG0IbUCIAZBAWtBA0khGgNAIAkoAtABINoBpyITIAkoAjRsaiEAIAMoAjQgE2whFkMAAID/IbICAkAgBkEATCIZDQBBACEIQQAhB0EAIRUgGkUEQANAILICIAAgB0ECdCILaioCACKzAiCyAiCzAl4bIrICIAAgC0EEcmoqAgAiswIgsgIgswJeGyKyAiAAIAtBCHJqKgIAIrMCILICILMCXhsisgIgACALQQxyaioCACKzAiCyAiCzAl4bIbICIAdBBGohByAVQQRqIhUgAkcNAAsLIAxFDQADQCCyAiAAIAdBAnRqKgIAIrMCILICILMCXhshsgIgB0EBaiEHIAhBAWoiCCAMRw0ACwsgCigCNCEUIAooAtABIRUgAygC0AEiFyAWaiEHAkAg2AFCAFcEQEQAAAAAAAAAACG4AgwBC0QAAAAAAAAAACG4AkIAIdkBQgAh2wEg2AFCAVIEQANAQwAAAAAhswICQCAAINkBpyIIQQJ0IgtqKgIAIrQCQwAAgP9bBEBDAAAAACG0AgwBCyC4AiC0AiCyApMQRSK0ArugIbgCCyAHIAtqILQCOAIAIAAgCEEBckECdCIIaioCACK0AkMAAID/XARAILgCILQCILICkxBFIrMCu6AhuAILIAcgCGogswI4AgAg2QFCAnwh2QEg2wFCAnwi2wEg3QFSDQALCyDeAVANAEMAAAAAIbMCIAAg2QGnQQJ0IghqKgIAIrQCQwAAgP9cBEAguAIgtAIgsgKTEEUiswK7oCG4AgsgByAIaiCzAjgCAAtEoY92////7z8guAKjtiGyAgJAIAFBAEwiGw0AILIC/RMhkgJBACEAQQAhCCAQQQ9HBEADQCAHIABBAnQiHGoiCyCSAiAL/QAAAP3mAf0LAAAgCyCSAiAL/QAAEP3mAf0LABAgCyCSAiAL/QAAIP3mAf0LACAgCyCSAiAL/QAAMP3mAf0LADAgByAcQcAAcmoiCyCSAiAL/QAAAP3mAf0LAAAgCyCSAiAL/QAAEP3mAf0LABAgCyCSAiAL/QAAIP3mAf0LACAgCyCSAiAL/QAAMP3mAf0LADAgAEEgaiEAIAhBAmoiCCARRw0ACwsgEkUNACAHIABBAnRqIgAgkgIgAP0AAAD95gH9CwAAIAAgkgIgAP0AABD95gH9CwAQIAAgkgIgAP0AACD95gH9CwAgIAAgkgIgAP0AADD95gH9CwAwCwJAIAEgBk4iHA0AIAEhACANQQRPBEAgsgL9EyGSAkEAIQADQCAHIAAgAWpBAnRqIgggCP0AAgAgkgL95gH9CwIAIABBBGoiACAPRw0ACyAEIQAgDEUNAQsDQCAHIABBAnRqIgggCCoCACCyApQ4AgAgAEEBaiIAIAZHDQALCwJAIBkNACATIBRsIRNBACEAAkAgBkEETwRAA0AgByAAQQJ0aiIIIAj9AAIA/QxfcIkwX3CJMF9wiTBfcIkw/eQB/QsCACAAQQRqIgAgAkcNAAsgBiACIgBGDQELA0AgByAAQQJ0aiIIIAgqAgBDX3CJMJI4AgAgAEEBaiIAIAZHDQALCyATIBVqIQtBACEIQQAhAAJAIAZBCEkNACALIA4gF2ogFmpJIA4gFWogE2ogB0txDQADQCAHIABBAnQiE2oiFiAW/QACACALIBNq/QACAP3lAf0LAgAgAEEEaiIAIAJHDQALIAYgAiIARg0BCyAAQX9zIAZqIdYBIAwEQANAIAcgAEECdCIWaiIVIBUqAgAgCyAWaioCAJM4AgAgAEEBaiEAIAhBAWoiCCAMRw0ACwsg1gFBA0kNAANAIAcgAEECdCIIaiITIBMqAgAgCCALaioCAJM4AgAgByAIQQRqIhNqIhYgFioCACALIBNqKgIAkzgCACAHIAhBCGoiE2oiFiAWKgIAIAsgE2oqAgCTOAIAIAcgCEEMaiIIaiITIBMqAgAgCCALaioCAJM4AgAgAEEEaiIAIAZHDQALCyAYKgIAILUClSGyAgJAIBsNACCyAv0TIZICQQAhAEEAIQggEEEPRwRAA0AgByAAQQJ0IhNqIgsgkgIgC/0AAAD95gH9CwAAIAsgkgIgC/0AABD95gH9CwAQIAsgkgIgC/0AACD95gH9CwAgIAsgkgIgC/0AADD95gH9CwAwIAcgE0HAAHJqIgsgkgIgC/0AAAD95gH9CwAAIAsgkgIgC/0AABD95gH9CwAQIAsgkgIgC/0AACD95gH9CwAgIAsgkgIgC/0AADD95gH9CwAwIABBIGohACAIQQJqIgggEUcNAAsLIBJFDQAgByAAQQJ0aiIAIJICIAD9AAAA/eYB/QsAACAAIJICIAD9AAAQ/eYB/QsAECAAIJICIAD9AAAg/eYB/QsAICAAIJICIAD9AAAw/eYB/QsAMAsCQCAcDQAgASEAIA1BBE8EQCCyAv0TIZICQQAhAANAIAcgACABakECdGoiCCCSAiAI/QACAP3mAf0LAgAgAEEEaiIAIA9HDQALIAQhACAMRQ0BCwNAIAcgAEECdGoiCCCyAiAIKgIAlDgCACAAQQFqIgAgBkcNAAsLINoBQgF8ItoBINwBUg0ACwsgBUHgAGokAAwGC0G8wwIoAgAQMBogBUHYzgA2AgggBUGa7QA2AgQgBUHkJjYCAEG4wwIoAgBBy+QAIAUQMQwcC0G8wwIoAgAQMBogBUGr2QA2AhggBUGZ7QA2AhQgBUHkJjYCEEG4wwIoAgBBy+QAIAVBEGoQMQwbC0G8wwIoAgAQMBogBUG72AA2AiggBUGY7QA2AiQgBUHkJjYCIEG4wwIoAgBBy+QAIAVBIGoQMQwaC0G8wwIoAgAQMBogBUHf2QA2AjggBUGX7QA2AjQgBUHkJjYCMEG4wwIoAgBBy+QAIAVBMGoQMQwZC0G8wwIoAgAQMBogBUH0ywA2AkggBUGW7QA2AkQgBUHkJjYCQEG4wwIoAgBBy+QAIAVBQGsQMQwYC0G8wwIoAgAQMBogBUGbITYCWCAFQfftADYCVCAFQeQmNgJQQbjDAigCAEHL5AAgBUHQAGoQMQwXCwwCC0G8wwIoAgAQMBogLkGbITYCGCAuQcvwADYCFCAuQeQmNgIQQbjDAigCAEHL5AAgLkEQahAxDBULIAAgASgCjAEgARD1AgsgLkFAayQADwtBvMMCKAIAEDAaIC5BxRA2AgggLkH/7QA2AgQgLkHkJjYCAEG4wwIoAgBBy+QAIC4QMQwSC0G8wwIoAgAQMBogLkGpPjYCKCAuQebvADYCJCAuQeQmNgIgQbjDAigCAEHL5AAgLkEgahAxDBELQbzDAigCABAwGiAuQak+NgI4IC5B8e8ANgI0IC5B5CY2AjBBuMMCKAIAQcvkACAuQTBqEDEMEAsgAkHkJjYCIEG4wwIoAgBBy+QAIAJBIGoQMQwPCyACQeQmNgIQQbjDAigCAEHL5AAgAkEQahAxDA4LIAJB5CY2AgBBuMMCKAIAQcvkACACEDEMDQsgAkHkJjYCMEG4wwIoAgBBy+QAIAJBMGoQMQwMCyACQeQmNgJAQbjDAigCAEHL5AAgAkFAaxAxDAsLIAVB5CY2AgBBuMMCKAIAQcvkACAFEDEMCgsgBEHkJjYCAEG4wwIoAgBBy+QAIAQQMQwJCyADQeQmNgIAQbjDAigCAEHL5AAgAxAxDAgLIANB5CY2AhBBuMMCKAIAQcvkACADQRBqEDEMBwsgA0HkJjYCIEG4wwIoAgBBy+QAIANBIGoQMQwGCyAFQeQmNgIQQbjDAigCAEHL5AAgBUEQahAxDAULIAVB5CY2AiBBuMMCKAIAQcvkACAFQSBqEDEMBAsgBEHkJjYCQEG4wwIoAgBBy+QAIARBQGsQMQwDCyAEQeQmNgIwQbjDAigCAEHL5AAgBEEwahAxDAILIARB5CY2AiBBuMMCKAIAQcvkACAEQSBqEDEMAQsgBEHkJjYCEEG4wwIoAgBBy+QAIARBEGoQMQsQAAALiAEBAX8gAC0AC0EHdgR/IAAoAghB/////wdxQQFrBUEKCyIDIAJPBEACfyAALQALQQd2BEAgACgCAAwBCyAACyIDIAEgAvwKAAAgACADIAIQnAMPCyAAIAMgAiADawJ/IAAtAAtBB3YEQCAAKAIEDAELIAAtAAtB/wBxCyIAQQAgACACIAEQzAELiAMBBX8jAEEQayIIJAAgAiABQX9zQe////8Hak0EQAJ/IAAtAAtBB3YEQCAAKAIADAELIAALIQogCEEEaiIJIAAgAUHn////A0kEfyAIIAFBAXQ2AgwgCCABIAJqNgIEIwBBEGsiAiQAIAkoAgAgCEEMaiILKAIASSEMIAJBEGokACALIAkgDBsoAgAiAkELTwR/IAJBEGpBcHEiAiACQQFrIgIgAkELRhsFQQoLQQFqBUHv////BwsQwwEgCCgCBCECIAgoAggaIAQEQCACIAogBBB3CyAGBEAgAiAEaiAHIAYQdwsgAyAEIAVqIglrIQcgAyAJRwRAIAIgBGogBmogBCAKaiAFaiAHEHcLIAFBAWoiAUELRwRAIAAgCiABEPEBCyAAIAI2AgAgACAAKAIIQYCAgIB4cSAIKAIIQf////8HcXI2AgggACAAKAIIQYCAgIB4cjYCCCAAIAQgBmogB2oiADYCBCAIQQA6AAwgACACaiAILQAMOgAAIAhBEGokAA8LEE0ACwsAIAQgAjYCAEEDC+kCAgV/AX4CfyMAQTBrIgMkAAJAAkAgAikDEEIBUg0AIAIpAxhCAVINACACKQMgQgFSDQAgAikDKEIBUg0AIAEoAjAgASgCACIEQSRsQZibAWooAgBHDQEgATUCOCIIIAEpAxggATUCNH5SDQEgATUCPCABKQMgIAh+Ug0BAn8CQCABKAKIAQ0AIAIoAogBDQBBAAwBC0EBCyEHIAAgBCABKAIMIAFBEGpBAEEAEEkiBEEaNgJAIAcEQCAAIAQoAgAgBCgCDCAEQRBqQQBBABBJIQULIAQgATYCjAEgBCAFNgKIASAEIAI2ApABIANBMGokACAEDAILQbzDAigCABAwGiADQYLXADYCKCADQcogNgIkIANB5CY2AiBBuMMCKAIAQcvkACADQSBqEDEQAAALQbzDAigCABAwGiADQfLXADYCGCADQcsgNgIUIANB5CY2AhBBuMMCKAIAQcvkACADQRBqEDEQAAALC3gBAn8jAEEQayIDJAAgA0EMaiIEIAEoAhwiATYCACABQQRqQQH+HgIAGiACIAQQvwEiASABKAIAKAIQEQAANgIAIAAgASABKAIAKAIUEQIAIAQoAgAiAEEEakF//h4CAEUEQCAAIAAoAgAoAggRAQALIANBEGokAAtwAQJ/IwBBEGsiAiQAIAJBDGoiAyAAKAIcIgA2AgAgAEEEakEB/h4CABogAxBsIgBB0OoCQerqAiABIAAoAgAoAjARCAAaIAMoAgAiAEEEakF//h4CAEUEQCAAIAAoAgAoAggRAQALIAJBEGokACABC3gBAn8jAEEQayIDJAAgA0EMaiIEIAEoAhwiATYCACABQQRqQQH+HgIAGiACIAQQwQEiASABKAIAKAIQEQAAOgAAIAAgASABKAIAKAIUEQIAIAQoAgAiAEEEakF//h4CAEUEQCAAIAAoAgAoAggRAQALIANBEGokAAsLACAAEJoEIAAQLwspAAJAIAAoAgBBAE4NACAAQf////8H/h4CAEGBgICAeEYNACAAEI4BCwtIAQJ/AkAgAigCTEEASARAIAAgASACEM4CIQAMAQsgAhCCASEEIAAgASACEM4CIQAgBEUNACACEIEBCyAAIAFGBEAgAQ8LIAALjwICAX8CfCAAQQNxBH9BZAVEAAAAAAAAAAAQ1gEjBkUEQAJ/EAMhBUEAQQAgAP5IApDxNBoCQBADIgQgBSACoCICZA0AAn8DQEEAIABBACAAQQD+SAKQ8TQiAyAAIANGG0UNARogBBDWASABIAD+EAIARgRAQQBBACAA/kgCkPE0GhADIgQgAmQNAwwBCwtBegsMAQtBACAAQQD+SAKQ8TQaQbd/Cw8LIAJEAAAAAAAA8H9iIQNBekG3f0EAIAAgAQJ+IAJEAAAAAABAj0CiRAAAAAAAQI9AoiICmUQAAAAAAADgQ2MEQCACsAwBC0KAgICAgICAgIB/C0J/IAMb/gECACIAQQJGGyAAQQFGGwsLJQACQCMFBEBBjPE0/hACAA0BENoGCw8LQYzxNP4QAgAQJxAmAAuyAQMCfAF/AX4jASIDLQAIRQRAIAMQKDoACSADQQE6AAgLIAACfgJ8IwEtAAkEQBADDAELIwNBHGpBHDYCAA8LIgFEAAAAAABAj0CjIgKZRAAAAAAAAOBDYwRAIAKwDAELQoCAgICAgICAgH8LIgQ3AwAgAAJ/IAEgBELoB365oUQAAAAAAECPQKJEAAAAAABAj0CiIgGZRAAAAAAAAOBBYwRAIAGqDAELQYCAgIB4CzYCCAvKBAICfwJ8IwBBEGshBCACKwMAIQYCfwJAIAErAwAiByAAKwMAZEUEQEEAIAYgB2RFDQIaIAQgAf0AAwD9CwMAIAEgAigCCDYCCCABIAIpAwA3AwAgAiAEKAIINgIIIAIgBCkDADcDAEEBIAErAwAgACsDAGRFDQIaIAQgAP0AAwD9CwMAIAAgASgCCDYCCCAAIAEpAwA3AwAgASAEKAIINgIIIAEgBCkDADcDAAwBCyAGIAdkBEAgBCAA/QADAP0LAwAgACACKAIINgIIIAAgAikDADcDACACIAQoAgg2AgggAiAEKQMANwMAQQEMAgsgBCAA/QADAP0LAwAgACABKAIINgIIIAAgASkDADcDACABIAQoAgg2AgggASAEKQMANwMAQQEgAisDACABKwMAZEUNARogBCAB/QADAP0LAwAgASACKAIINgIIIAEgAikDADcDACACIAQoAgg2AgggAiAEKQMANwMAC0ECCyEFIAMrAwAgAisDAGQEfyAEIAL9AAMA/QsDACACIAMoAgg2AgggAiADKQMANwMAIAMgBCgCCDYCCCADIAQpAwA3AwAgAisDACABKwMAZEUEQCAFQQFqDwsgBCAB/QADAP0LAwAgASACKAIINgIIIAEgAikDADcDACACIAQoAgg2AgggAiAEKQMANwMAIAErAwAgACsDAGRFBEAgBUECag8LIAQgAP0AAwD9CwMAIAAgASgCCDYCCCAAIAEpAwA3AwAgASAEKAIINgIIIAEgBCkDADcDACAFQQNqBSAFCwunAwELfwJAAkAgACgCBCIDIAAoAgBHBEAgAyECDAELIAAoAggiBCAAKAIMIgJJBEAgBCACIARrQQJ1QQFqQQJtQQJ0IgZqIAQgA2siBWsiAiADIAX8CgAAIAAgAjYCBCAAIAAoAgggBmo2AggMAQtBASACIANrQQF1IAIgA0YbIgVBgICAgARPDQEgBUECdCICEDIiByACaiEMIAcgBUEDaiIIQXxxaiICIQkCQCADIARGDQAgAiAEIANrIgRBfHFqIQkgAiEGIAMhBQJAIARBBGsiBEEcSQ0AIAhBfHEgB2ogA2tBEEkNACADIARBAnZBAWoiCEH8////B3EiCkECdCIGaiEFIAIgBmohBgNAIAIgC0ECdCIEaiADIARq/QACAP0LAgAgC0EEaiILIApHDQALIAggCkYNAQsDQCAGIAUoAgA2AgAgBUEEaiEFIAZBBGoiBiAJRw0ACwsgACAMNgIMIAAgCTYCCCAAIAI2AgQgACAHNgIAIANFDQAgAxAvIAAoAgQhAgsgAkEEayABKAIANgIAIAAgACgCBEEEazYCBA8LEEcAC78VARh/IAAoAgAhEyAAKAIEIgMoAgghAAJAIAEoAhgiCyADKAIETQRAIAtBA3QhBCALQQJ0IQIMAQsgAARAIAAQLwsgAygCDCIABEAgABAvCyALQQJ0IgIQOyEAIAMgCzYCBCADIAA2AgggAyALQQN0IgQQOzYCDAsgAEEAIAL8CwAgAygCDEEAIAT8CwAgAyATNgIAQQAhACMAQfAAayIFJAAgAygCHCENIAMoAhghEiABIgsoAgQiCEEASgRAA0ACQCALKAIMIABBAnRqKAIAIgQoAsgBIgFFDQAgBSADKQIENwNoIAVB6ABqIAEQYiEBIAMoAgwgAUEDdGoiASABKAIEQQFqNgIEIAQoAggNACAEKALQAUUNACADIARBARB4CwJAIAQoAowBIgJFDQAgBSADKQIENwNgIAVB4ABqIAIQYiEBIAMoAgwgAUEDdGoiASABKAIAQQFqNgIAAkAgAigCyAFFDQAgAigCCA0AIAIoAtABRQ0AIAMgAkEBEHgLIAQoApABIgJFDQAgBSADKQIENwNYIAVB2ABqIAIQYiEBIAMoAgwgAUEDdGoiASABKAIAQQFqNgIAAkAgAigCyAFFDQAgAigCCA0AIAIoAtABRQ0AIAMgAkEBEHgLIAQoApQBIgJFDQAgBSADKQIENwNQIAVB0ABqIAIQYiEBIAMoAgwgAUEDdGoiASABKAIAQQFqNgIAAkAgAigCyAFFDQAgAigCCA0AIAIoAtABRQ0AIAMgAkEBEHgLIAQoApgBIgJFDQAgBSADKQIENwNIIAVByABqIAIQYiEBIAMoAgwgAUEDdGoiASABKAIAQQFqNgIAAkAgAigCyAFFDQAgAigCCA0AIAIoAtABRQ0AIAMgAkEBEHgLIAQoApwBIgJFDQAgBSADKQIENwNAIAVBQGsgAhBiIQEgAygCDCABQQN0aiIBIAEoAgBBAWo2AgACQCACKALIAUUNACACKAIIDQAgAigC0AFFDQAgAyACQQEQeAsgBCgCoAEiAkUNACAFIAMpAgQ3AzggBUE4aiACEGIhASADKAIMIAFBA3RqIgEgASgCAEEBajYCAAJAIAIoAsgBRQ0AIAIoAggNACACKALQAUUNACADIAJBARB4CyAEKAKkASICRQ0AIAUgAykCBDcDMCAFQTBqIAIQYiEBIAMoAgwgAUEDdGoiASABKAIAQQFqNgIAAkAgAigCyAFFDQAgAigCCA0AIAIoAtABRQ0AIAMgAkEBEHgLIAQoAqgBIgJFDQAgBSADKQIENwMoIAVBKGogAhBiIQEgAygCDCABQQN0aiIBIAEoAgBBAWo2AgACQCACKALIAUUNACACKAIIDQAgAigC0AFFDQAgAyACQQEQeAsgBCgCrAEiAkUNACAFIAMpAgQ3AyAgBUEgaiACEGIhASADKAIMIAFBA3RqIgEgASgCAEEBajYCAAJAIAIoAsgBRQ0AIAIoAggNACACKALQAUUNACADIAJBARB4CyAEKAKwASICRQ0AIAUgAykCBDcDGCAFQRhqIAIQYiEBIAMoAgwgAUEDdGoiASABKAIAQQFqNgIAIAIoAsgBRQ0AIAIoAggNACACKALQAUUNACADIAJBARB4CyAAQQFqIgAgCygCBCIISA0ACwsgDSAIIA0bIhVBAEoEQEEAIQgDQCAIIQAgFQJ/AkAgDQRAIBIgAEECdGooAgAiAEF/Rg0BCwJAIAsoAgwgAEECdGooAgAiASgCjAEiAEUNACADIAAQhgEgASgCkAEiAEUNACADIAAQhgEgASgClAEiAEUNACADIAAQhgEgASgCmAEiAEUNACADIAAQhgEgASgCnAEiAEUNACADIAAQhgEgASgCoAEiAEUNACADIAAQhgEgASgCpAEiAEUNACADIAAQhgEgASgCqAEiAEUNACADIAAQhgEgASgCrAEiAEUNACADIAAQhgEgASgCsAEiAEUNACADIAAQhgELIAMgARCGASANRQ0AIBIgCEECdGooAgBBf0YNACAIQQFqDAELIBEgCCANGyIAIAggCEEBaiIUIA0bIhZIBEADQCAAIQIgCygCDCANBH8gEiAAQQJ0aigCAAUgAgtBAnRqKAIAIRdBACEIA0AgFyAIQQJ0aigCjAEiAARAIAUgAykCBDcDECAFQRBqIAAQYiEBIAMoAgwgAUEDdGoiBCAEKAIAQQFrIgE2AgACQCABDQAgBCgCBA0AIAAoAsgBIgEEQCAFIAMpAgQ3AwggBUEIaiABEGIhACADKAIMIABBA3RqIgQgBCgCBEEBayIANgIEIAANASABIQAgBCgCAA0BCyMAQSBrIg8kACADKAIAIgZFBEAgAygCFCEYIA8gAykCBDcDGCAYIA9BGGogABBiQQJ0aigCACEGCwJAAkACQCAGKAIAIgEgACgCCEcNACAAKALQASEQIAEgABDnBCIAIAYoAgwiASAAIAFwayABcGohDgJAIAYoAhAiAUEATARAQQAhAAwBCyAOIBBqIQRBACEAA0AgECAGIABBA3RqIgooAhQiDCAKKAIYIgdqRgRAIAogByAOaiIJNgIYIAAgAUEBayIOTg0DIAYgAEEBaiIEQQN0aiIHKAIUIAkgDGpHDQMgCiAHKAIYIAlqNgIYIAYgDjYCECAEIA5ODQMCQCABIABrQQJrIgxBAkkEQCAEIQAMAQsgBCAMQX5xIglqIQBBACEBA0AgBiABIARqQQN0aiIHIAf9AAIc/QsCFCABQQJqIgEgCUcNAAsgCSAMRg0ECyAGQRRqIQEDQCABIABBA3RqIAEgAEEBaiIAQQN0aikCADcCACAAIA5HDQALDAMLIAQgDEYEQCAKIBA2AhQgCiAHIA5qIgc2AhggAEUNAyAQIABBA3QgBmoiCSgCDCAJKAIQIgRqRw0DIAkgBCAHajYCECAGIAFBAWsiDDYCECAAIAxODQMCQCAMIABrIglBAkkEQCAAIQEMAQsgACAJQX5xIgdqIQFBACEKA0AgBiAAIApqQQN0aiIEIAT9AAIc/QsCFCAKQQJqIgogB0cNAAsgByAJRg0ECyAGQRRqIQADQCAAIAFBA3RqIAAgAUEBaiIBQQN0aikCADcCACABIAxHDQALDAMLIABBAWoiACABRw0ACyABQYACTg0CQQAhAAJAA0AgBiAAQQN0aigCFCAQTw0BIABBAWoiACABRw0ACyABIQAMAQsgACABTg0AIAEgAEF/c2ohGSABIABrQQNxIgQEQEEAIQogBkEUaiEJA0AgCSABQQN0aiAJIAFBAWsiAUEDdGopAgA3AgAgCkEBaiIKIARHDQALCyAZQQJLBEAgBkEUaiEHA0AgByABQQN0aiIEIARBCGspAgA3AgAgBEEQayAEQRhrIgT9AAIA/QsCACAEIAcgAUEEayIBQQN0aikCADcCACAAIAFIDQALCyAGKAIQIQELIAYgAEEDdGoiACAONgIYIAAgEDYCFCAGIAFBAWo2AhALIA9BIGokAAwBC0G8wwIoAgAQMBogD0GG3QA2AgggD0HPATYCBCAPQdonNgIAQbjDAigCAEHL5AAgDxAxEAAACwsgCEEBaiIIQQpHDQELCyACQQFqIgAgFkcNAAsLIBQgESANGyERIBQLIghHDQALCyAFQfAAaiQAIANBADYCACATKAKUEBoLggUCA38BfiMAQeAAayICJAAgASEDAkACQAJAAkACQAJAAkACQAJAIAAoAkAOSQcICAgIBwgIBwcHBwcHBwcHCAgICAgICAgIBwcIBwcHBwcHBwcICAEICAgHBwgICAcHCAgIBwgICAcHBwgABwcHBwcCAwQICAUGC0EBIQMjAEEQayIEJAAgACgCQEE9RwRAQbzDAigCABAwGiAEQb8qNgIIIARB6hc2AgQgBEHkJjYCAEG4wwIoAgBBy+QAIAQQMRAAAAsgACgCRCEAIARBEGokACAAQQdJDQcgASEDIABBB2tBAk0NB0G8wwIoAgAQMBogAkGbITYCSCACQcT8ADYCRCACQeQmNgJAQbjDAigCAEHL5AAgAkFAaxAxEAAAC0EEIAEgAUEEThsiASAAKAKMASIAKQMgIAApAxh+IAApAyh+IgWnIAUgAaxVGyEDDAYLIAEgACgCSCIAIAEgACABSBsgAEF/RhshAwwFCyABIAAoAkgiACABIAAgAUgbIABBf0YbIQMMBAsgASAAKAJIIgAgASAAIAFIGyAAQX9GGyEDDAMLQbzDAigCABAwGiACQZshNgJYIAJB9/0ANgJUIAJB5CY2AlBBuMMCKAIAQcvkACACQdAAahAxEAAACyACQcwQNgIwQbjDAigCACIBQYLiACACQTBqEDECQCAAKAJAIgBBxwBNBEAgAiAAQQJ0QcCgAWooAgA2AhAgAUHw5AAgAkEQahAxDAELIAIgADYCICABQaDuACACQSBqEDELQbzDAigCABAwGiACQZshNgIIIAJBgf4ANgIEIAJB5CY2AgAgAUHL5AAgAhAxEAAAC0EBIQMLIAJB4ABqJAAgAwtJAQJ/IAAoAgQiBUEIdSEGIAAoAgAiACABIAVBAXEEfyAGIAIoAgBqKAIABSAGCyACaiADQQIgBUECcRsgBCAAKAIAKAIYEQsACxkBAX8gACgCACIBBEAgARCKBQsgARAvIAALewEBfyMAQRBrIgMkAAJAIAJBCk0EQCAAIAAtAAtBgAFxIAJyOgALIAAgAC0AC0H/AHE6AAsgACABIAIQdyADQQA6AA8gACACaiADLQAPOgAADAELIABBCiACQQprIAAtAAtB/wBxIgBBACAAIAIgARDMAQsgA0EQaiQAC3gBAn8jAEEQayIEJAACQCACIAAoAghB/////wdxIgNJBEAgACgCACEDIAAgAjYCBCADIAEgAhB3IARBADoADyACIANqIAQtAA86AAAMAQsgACADQQFrIAIgA2tBAWogACgCBCIAQQAgACACIAEQzAELIARBEGokAAsJACAAIAEQmwMLUQEBfyMAQSBrIggkACAIIAM3AwggCCACNwMAIAggBDcDECAAIAFBAyAIIAcQqwIiACAEpyAGbDYCPCAAIAY2AjggACAFNgI0IAhBIGokACAAC4sDAgd/A34jAEHQAGsiBSQAAkACQCABKAIwIgYgASgCACIIQSRsIgdBmJsBaigCAEcNACABNQI0IgwgASkDECIOIAatfiAHQZSbAWo0AgB/Ug0AIAE1AjgiDSAMIAEpAxgiDH5SDQAgATUCPCANIAEpAyAiDX5SDQAgASkDKCAMIA5+IA1+fiACIAN+IAR+Ug0BIAEoAogBIQsgBSAENwNAIAUgAzcDOCAFIAI3AzBBACEHIAAgCEEDIAVBMGogAUEAEEkhCiAFIAFB1AFqNgIAIApBktUAIAUQcyIGQR42AkAgCwRAIAAgBigCACAGKAIMIAZBEGpBAEEAEEkhBwsgBiABNgKMASAGIAc2AogBIAVB0ABqJAAgBg8LQbzDAigCABAwGiAFQcXXADYCKCAFQZcjNgIkIAVB5CY2AiBBuMMCKAIAQcvkACAFQSBqEDEQAAALQbzDAigCABAwGiAFQYs7NgIYIAVBmCM2AhQgBUHkJjYCEEG4wwIoAgBBy+QAIAVBEGoQMRAAAAsEAEEEC/8CAgd/A34jAEFAaiIEJAACQAJAIAEoAjAiBSABKAIAIgdBJGwiBkGYmwFqKAIARw0AIAE1AjQiCyABKQMQIg0gBa1+IAZBlJsBajQCAH9SDQAgATUCOCIMIAsgASkDGCILflINACABNQI8IAwgASkDICIMflINACABKQMoIAsgDX4gDH5+IAIgA35SDQEgASgCiAEhCiAEIAM3AzggBCACNwMwQQAhBiAAIAdBAiAEQTBqIAFBABBJIQkgBCABQdQBajYCACAJQZLVACAEEHMiBUEeNgJAIAoEQCAAIAUoAgAgBSgCDCAFQRBqQQBBABBJIQYLIAUgATYCjAEgBSAGNgKIASAEQUBrJAAgBQ8LQbzDAigCABAwGiAEQcXXADYCKCAEQf0iNgIkIARB5CY2AiBBuMMCKAIAQcvkACAEQSBqEDEQAAALQbzDAigCABAwGiAEQaU9NgIYIARB/iI2AhQgBEHkJjYCEEG4wwIoAgBBy+QAIARBEGoQMRAAAAukAQEBfwJAQYyKNf4SAABBAXENAEGMijUQVEUNAAJAQYCKNf4SAABBAXENAEGAijUQVEUNABDUBUH4iTVB6JY1NgIAQfyJNUH4iTU2AgBBgIo1EFMLQYSKNUH8iTUoAgAoAgAiATYCACABQQRqQQH+HgIAGkGIijVBhIo1NgIAQYyKNRBTCyAAQYiKNSgCACgCACIBNgIAIAFBBGpBAf4eAgAaIAALXwEEfyABKAKIASEFIAAgASgCACABKAIMIAFBEGpBAEEAEEkiAkK9gICA8AA3A0AgBQRAIAAgAigCACACKAIMIAJBEGpBAEEAEEkhAwsgAiABNgKMASACIAM2AogBIAILRwEBfyMAQRBrIgIkAAJAIAEtAAtBB3ZFBEAgACABKAIINgIIIAAgASkCADcCAAwBCyAAIAEoAgAgASgCBBBrCyACQRBqJAALCABB/////wcLBQBB/wALdgEBfyMAQRBrIgIkACACIAA2AgwCQCAAIAFGDQADQCACIAFBBGsiATYCCCAAIAFPDQEgAigCDCIAKAIAIQEgACACKAIIIgAoAgA2AgAgACABNgIAIAIgAigCDEEEaiIANgIMIAIoAgghAQwACwALIAJBEGokAAv2BAEIfyMAQRBrIgskACAGEGwhCSALQQRqIgcgBhC/ASIIIAgoAgAoAhQRAgACQAJ/IActAAtBB3YEQCAHKAIEDAELIActAAtB/wBxC0UEQCAJIAAgAiADIAkoAgAoAjARCAAaIAUgAyACIABrQQJ0aiIGNgIADAELIAUgAzYCAAJAAkAgACIKLQAAIgZBK2sOAwABAAELIAkgBsAgCSgCACgCLBEEACEHIAUgBSgCACIGQQRqNgIAIAYgBzYCACAAQQFqIQoLAkAgAiAKa0ECSA0AIAotAABBMEcNACAKLQABQSByQfgARw0AIAlBMCAJKAIAKAIsEQQAIQcgBSAFKAIAIgZBBGo2AgAgBiAHNgIAIAkgCiwAASAJKAIAKAIsEQQAIQcgBSAFKAIAIgZBBGo2AgAgBiAHNgIAIApBAmohCgsgCiACEKwBIAggCCgCACgCEBEAACEOQQAhByAKIQYDfyACIAZNBH8gAyAKIABrQQJ0aiAFKAIAEOoBIAUoAgAFAkACfyALQQRqIggtAAtBB3YEQCAIKAIADAELIAgLIAdqLQAARQ0AIAwCfyAILQALQQd2BEAgCCgCAAwBCyAICyAHaiwAAEcNACAFIAUoAgAiDUEEajYCACANIA42AgAgByAHAn8gCC0AC0EHdgRAIAgoAgQMAQsgCC0AC0H/AHELQQFrSWohB0EAIQwLIAkgBiwAACAJKAIAKAIsEQQAIQ0gBSAFKAIAIghBBGo2AgAgCCANNgIAIAZBAWohBiAMQQFqIQwMAQsLIQYLIAQgBiADIAEgAGtBAnRqIAEgAkYbNgIAIAtBBGoQMxogC0EQaiQAC9ABAQJ/IAJBgBBxBEAgAEErOgAAIABBAWohAAsgAkGACHEEQCAAQSM6AAAgAEEBaiEACyACQYQCcSIDQYQCRwRAIABBrtQAOwAAIABBAmohAAsgAkGAgAFxIQIDQCABLQAAIgQEQCAAIAQ6AAAgAEEBaiEAIAFBAWohAQwBCwsgAAJ/AkAgA0GAAkcEQCADQQRHDQFBxgBB5gAgAhsMAgtBxQBB5QAgAhsMAQtBwQBB4QAgAhsgA0GEAkYNABpBxwBB5wAgAhsLOgAAIANBhAJHC+0EAQh/IwBBEGsiCyQAIAYQciEJIAtBBGoiByAGEMEBIgggCCgCACgCFBECAAJAAn8gBy0AC0EHdgRAIAcoAgQMAQsgBy0AC0H/AHELRQRAIAkgACACIAMgCSgCACgCIBEIABogBSADIAIgAGtqIgY2AgAMAQsgBSADNgIAAkACQCAAIgotAAAiBkEraw4DAAEAAQsgCSAGwCAJKAIAKAIcEQQAIQcgBSAFKAIAIgZBAWo2AgAgBiAHOgAAIABBAWohCgsCQCACIAprQQJIDQAgCi0AAEEwRw0AIAotAAFBIHJB+ABHDQAgCUEwIAkoAgAoAhwRBAAhByAFIAUoAgAiBkEBajYCACAGIAc6AAAgCSAKLAABIAkoAgAoAhwRBAAhByAFIAUoAgAiBkEBajYCACAGIAc6AAAgCkECaiEKCyAKIAIQrAEgCCAIKAIAKAIQEQAAIQ5BACEHIAohBgN/IAIgBk0EfyADIAogAGtqIAUoAgAQrAEgBSgCAAUCQAJ/IAtBBGoiCC0AC0EHdgRAIAgoAgAMAQsgCAsgB2otAABFDQAgDAJ/IAgtAAtBB3YEQCAIKAIADAELIAgLIAdqLAAARw0AIAUgBSgCACINQQFqNgIAIA0gDjoAACAHIAcCfyAILQALQQd2BEAgCCgCBAwBCyAILQALQf8AcQtBAWtJaiEHQQAhDAsgCSAGLAAAIAkoAgAoAhwRBAAhDSAFIAUoAgAiCEEBajYCACAIIA06AAAgBkEBaiEGIAxBAWohDAwBCwshBgsgBCAGIAMgASAAa2ogASACRhs2AgAgC0EEahAzGiALQRBqJAAL7QUBC38jAEGAAWsiCSQAIAkgATYCfCAJQfYBNgIQIAlBCGpBACAJQRBqIggQUSELAkACQCADIAJrQQxtIgpB5QBPBEAgChA7IghFDQEgCygCACEBIAsgCDYCACABBEAgASALKAIEEQEACwsgCCEHIAIhAQNAIAEgA0YEQANAIAAgCUH8AGoQQkEBIAobBEAgACAJQfwAahBCBEAgBSAFKAIAQQJyNgIACwwFCwJ/IAAoAgAiBygCDCIBIAcoAhBGBEAgByAHKAIAKAIkEQAADAELIAEoAgALIQ4gBkUEQCAEIA4gBCgCACgCHBEEACEOCyAPQQFqIQ1BACEQIAghByACIQEDQCABIANGBEAgDSEPIBBFDQIgABBcGiAIIQcgAiEBIAogDGpBAkkNAgNAIAEgA0YEQAwEBQJAIActAABBAkcNAAJ/IAEtAAtBB3YEQCABKAIEDAELIAEtAAtB/wBxCyAPRg0AIAdBADoAACAMQQFrIQwLIAdBAWohByABQQxqIQEMAQsACwAFAkAgBy0AAEEBRw0AAn8gAS0AC0EHdgRAIAEoAgAMAQsgAQsgD0ECdGooAgAhEQJAIAYEfyARBSAEIBEgBCgCACgCHBEEAAsgDkYEQEEBIRACfyABLQALQQd2BEAgASgCBAwBCyABLQALQf8AcQsgDUcNAiAHQQI6AAAgDEEBaiEMDAELIAdBADoAAAsgCkEBayEKCyAHQQFqIQcgAUEMaiEBDAELAAsACwAFIAdBAkEBAn8gAS0AC0EHdgRAIAEoAgQMAQsgAS0AC0H/AHELRSINGzoAACAHQQFqIQcgAUEMaiEBIAwgDWohDCAKIA1rIQoMAQsACwALEEYACwJAAkADQCACIANGDQEgCC0AAEECRwRAIAhBAWohCCACQQxqIQIMAQsLIAIhAwwBCyAFIAUoAgBBBHI2AgALIAsoAgAhACALQQA2AgAgAARAIAAgCygCBBEBAAsgCUGAAWokACADC/QFAQt/IwBBgAFrIgkkACAJIAE2AnwgCUH2ATYCECAJQQhqQQAgCUEQaiIIEFEhCwJAAkAgAyACa0EMbSIKQeUATwRAIAoQOyIIRQ0BIAsoAgAhASALIAg2AgAgAQRAIAEgCygCBBEBAAsLIAghByACIQEDQCABIANGBEADQCAAIAlB/ABqEENBASAKGwRAIAAgCUH8AGoQQwRAIAUgBSgCAEECcjYCAAsMBQsCfyAAKAIAIgcoAgwiASAHKAIQRgRAIAcgBygCACgCJBEAAAwBCyABLQAAC8AhDiAGRQRAIAQgDiAEKAIAKAIMEQQAIQ4LIA9BAWohDUEAIRAgCCEHIAIhAQNAIAEgA0YEQCANIQ8gEEUNAiAAEF0aIAghByACIQEgCiAMakECSQ0CA0AgASADRgRADAQFAkAgBy0AAEECRw0AAn8gAS0AC0EHdgRAIAEoAgQMAQsgAS0AC0H/AHELIA9GDQAgB0EAOgAAIAxBAWshDAsgB0EBaiEHIAFBDGohAQwBCwALAAUCQCAHLQAAQQFHDQACfyABLQALQQd2BEAgASgCAAwBCyABCyAPai0AACERAkAgDkH/AXEgBgR/IBEFIAQgEcAgBCgCACgCDBEEAAtB/wFxRgRAQQEhEAJ/IAEtAAtBB3YEQCABKAIEDAELIAEtAAtB/wBxCyANRw0CIAdBAjoAACAMQQFqIQwMAQsgB0EAOgAACyAKQQFrIQoLIAdBAWohByABQQxqIQEMAQsACwALAAUgB0ECQQECfyABLQALQQd2BEAgASgCBAwBCyABLQALQf8AcQtFIg0bOgAAIAdBAWohByABQQxqIQEgDCANaiEMIAogDWshCgwBCwALAAsQRgALAkACQANAIAIgA0YNASAILQAAQQJHBEAgCEEBaiEIIAJBDGohAgwBCwsgAiEDDAELIAUgBSgCAEEEcjYCAAsgCygCACEAIAtBADYCACAABEAgACALKAIEEQEACyAJQYABaiQAIAMLvgIBBH8gA0GIiDUgAxsiBSgCACEDAkACfwJAIAFFBEAgAw0BQQAPC0F+IAJFDQEaAkAgAwRAIAIhBAwBCyABLQAAIgPAIgRBAE4EQCAABEAgACADNgIACyAEQQBHDwsjAygCYCgCAEUEQEEBIABFDQMaIAAgBEH/vwNxNgIAQQEPCyADQcIBayIDQTJLDQEgA0ECdEGwzwJqKAIAIQMgAkEBayIERQ0DIAFBAWohAQsgAS0AACIGQQN2IgdBEGsgA0EadSAHanJBB0sNAANAIARBAWshBCAGQYABayADQQZ0ciIDQQBOBEAgBUEANgIAIAAEQCAAIAM2AgALIAIgBGsPCyAERQ0DIAFBAWoiAS0AACIGQcABcUGAAUYNAAsLIAVBADYCACMDQRxqQRk2AgBBfwsPCyAFIAM2AgBBfgsJACABQQEQ9AMLcQEBfyAAQdTIAjYCACAAEPcDGgJAIAAtAGBFDQAgACgCICIBRQ0AIAEQLwsCQCAALQBhRQ0AIAAoAjgiAUUNACABEC8LIABB5McCNgIAIAAoAgQiAUEEakF//h4CAEUEQCABIAEoAgAoAggRAQALIAALDwAgACAAKAIQIAFyELsCC7YLAQZ/IAAgAWohBQJAAkAgACgCBCICQQFxDQAgAkEDcUUNASAAKAIAIgIgAWohAQJAAkACQCAAIAJrIgBBlIQ1KAIARwRAIAJB/wFNBEAgAkEDdiEEIAAoAggiAyAAKAIMIgJHDQJBgIQ1QYCENSgCAEF+IAR3cTYCAAwFCyAAKAIYIQYgACAAKAIMIgJHBEBBkIQ1KAIAGiAAKAIIIgMgAjYCDCACIAM2AggMBAsgAEEUaiIEKAIAIgNFBEAgACgCECIDRQ0DIABBEGohBAsDQCAEIQcgAyICQRRqIgQoAgAiAw0AIAJBEGohBCACKAIQIgMNAAsgB0EANgIADAMLIAUoAgQiAkEDcUEDRw0DQYiENSABNgIAIAUgAkF+cTYCBCAAIAFBAXI2AgQgBSABNgIADwsgAyACNgIMIAIgAzYCCAwCC0EAIQILIAZFDQACQCAAKAIcIgNBAnRBsIY1aiIEKAIAIABGBEAgBCACNgIAIAINAUGEhDVBhIQ1KAIAQX4gA3dxNgIADAILIAZBEEEUIAYoAhAgAEYbaiACNgIAIAJFDQELIAIgBjYCGCAAKAIQIgMEQCACIAM2AhAgAyACNgIYCyAAKAIUIgNFDQAgAiADNgIUIAMgAjYCGAsCQAJAAkACQCAFKAIEIgJBAnFFBEBBmIQ1KAIAIAVGBEBBmIQ1IAA2AgBBjIQ1QYyENSgCACABaiIBNgIAIAAgAUEBcjYCBCAAQZSENSgCAEcNBkGIhDVBADYCAEGUhDVBADYCAA8LQZSENSgCACAFRgRAQZSENSAANgIAQYiENUGIhDUoAgAgAWoiATYCACAAIAFBAXI2AgQgACABaiABNgIADwsgAkF4cSABaiEBIAJB/wFNBEAgAkEDdiEEIAUoAgwiAiAFKAIIIgNGBEBBgIQ1QYCENSgCAEF+IAR3cTYCAAwFCyADIAI2AgwgAiADNgIIDAQLIAUoAhghBiAFIAUoAgwiAkcEQEGQhDUoAgAaIAUoAggiAyACNgIMIAIgAzYCCAwDCyAFQRRqIgQoAgAiA0UEQCAFKAIQIgNFDQIgBUEQaiEECwNAIAQhByADIgJBFGoiBCgCACIDDQAgAkEQaiEEIAIoAhAiAw0ACyAHQQA2AgAMAgsgBSACQX5xNgIEIAAgAUEBcjYCBCAAIAFqIAE2AgAMAwtBACECCyAGRQ0AAkAgBSgCHCIDQQJ0QbCGNWoiBCgCACAFRgRAIAQgAjYCACACDQFBhIQ1QYSENSgCAEF+IAN3cTYCAAwCCyAGQRBBFCAGKAIQIAVGG2ogAjYCACACRQ0BCyACIAY2AhggBSgCECIDBEAgAiADNgIQIAMgAjYCGAsgBSgCFCIDRQ0AIAIgAzYCFCADIAI2AhgLIAAgAUEBcjYCBCAAIAFqIAE2AgAgAEGUhDUoAgBHDQBBiIQ1IAE2AgAPCyABQf8BTQRAIAFBeHFBqIQ1aiECAn9BgIQ1KAIAIgNBASABQQN2dCIBcUUEQEGAhDUgASADcjYCACACDAELIAIoAggLIQEgAiAANgIIIAEgADYCDCAAIAI2AgwgACABNgIIDwtBHyEDIAFB////B00EQCABQSYgAUEIdmciAmt2QQFxIAJBAXRrQT5qIQMLIAAgAzYCHCAAQgA3AhAgA0ECdEGwhjVqIQICQAJAQYSENSgCACIEQQEgA3QiB3FFBEBBhIQ1IAQgB3I2AgAgAiAANgIAIAAgAjYCGAwBCyABQRkgA0EBdmtBACADQR9HG3QhAyACKAIAIQIDQCACIgQoAgRBeHEgAUYNAiADQR12IQIgA0EBdCEDIAQgAkEEcWoiB0EQaigCACICDQALIAcgADYCECAAIAQ2AhgLIAAgADYCDCAAIAA2AggPCyAEKAIIIgEgADYCDCAEIAA2AgggAEEANgIYIAAgBDYCDCAAIAE2AggLC5ADAQZ/IAAtAABBD3FFBEAgAEEEakEAQQr+SAIAQQpxDwsCfyAAKAIAIQICQAJAAkAjAyIBKAIYIgQgACgCBCIDQf////8DcSIGRw0AAkAgAkEIcUUNACAAKAIUQQBODQAgAEEANgIUIANBgICAgARxIQMMAgsgAkEDcUEBRw0AQQYhBSAAKAIUIgFB/v///wdLDQIgACABQQFqNgIUQQAMAwtBOCEFIAZB/////wNGDQECQCAGDQBBACADIAJBBHEbDQAgAyAAQQRqIAMgAkGAAXEEfyABKAJQRQRAIAFBdDYCUAsgACgCCCEGIAEgAEEQajYCVCAEQYCAgIB4ciAEIAYbBSAECyADQYCAgIAEcXL+SAIARg0BIAFBADYCVCACQQxxQQxHDQAgACgCCA0CC0EKDAILIAEoAkwhAiAAIAFBzABqIgU2AgwgACACNgIQIABBEGohBCACIAVHBEAgAkEEayAENgIACyABIAQ2AkxBACEFIAFBADYCVCADRQ0AIABBADYCFEE+DAELIAULC5sFAQV/IwBBMGsiBSQAAkAgAEUEQEEcIQQMAQtBmPM0KAIARQRAQZjzNEErNgIAC0GZ8TQtAABFBEAQ/AEoAgAiAwRAA0ACQCADRQ0AIAMoAkxBAE4NACADQQA2AkwLIAMoAjgiAw0ACwtB1PI0ENMBAkBBlPE0KAIAIgNFDQAgAygCTEEATg0AIANBADYCTAsCQEHAuwMoAgAiA0UNACADKAJMQQBODQAgA0EANgJMCwJAQai6AygCACIDRQ0AIAMoAkxBAE4NACADQQA2AkwLQZnxNEEBOgAACyAFQQhqQQBBKPwLACAFQYy5AygCACIDNgIEQZC5AygCAEGaAWoiBEEAIANBD2ogBSgCDBtqIgYQOyIDQQAgBBCgASADIAY2AjAgAyADNgIsIAMgAzYCAEGY8zRBmPM0KAIAIgRBAWo2AgAgAyADQcwAajYCTCADIAQ2AhggA0G48TQ2AmAgA0EDQQIgBSgCEBs2AiAgAyAFKAIEIgY2AjggAyADQYcBakF8cSIENgJ0IARBEGohBEGQuQMoAgAEQCADIARBA2pBfHEiBDYCSEGQuQMoAgAgBGohBAsgAyAFKAIMIgcgBCAGakEPakFwcSAHGzYCNCADEIwEIwMhBBDKAiAEKAIMIQYgAyAENgIIIAMgBjYCDCAGIAM2AgggAygCCCADNgIMEMkCQZzxNEGc8TQoAgAiBEEBajYCACAERQRAQZvxNEEBOgAACyADIAVBBGogASACEB8iBARAQZzxNEGc8TQoAgBBAWsiADYCACAARQRAQZvxNEEAOgAACxDKAiADKAIMIgAgAygCCDYCCCADKAIIIAA2AgwgAyADNgIMIAMgAzYCCBDJAgwBCyAAIAM2AgALIAVBMGokACAECxUAIABBAP5BAgBBAkYEQCAAEI4BCwsyACAAQQBBAf5IAgAEQCAAQQFBAv5IAgAaA0AgAEEAQQIQoQEgAEEAQQL+SAIADQALCwvnAQICfwN8IwBBEGsiAiQAAkACfwJAAkAjBSIDDQAjAyIELQAoQQFHDQAgBC0AKUUNAQtBAUHkACADG7chBRADRAAAAAAAAPB/oCEHIwMhAwNAAkAgAygCJEUEQCAHEAOhIgZEAAAAAAAAAABlRQ0BQckADAQLQQshAAwECyAAIAEgBSAGIAUgBmMbENUBIgRBt39GDQALQQAgBGsMAQtBACAAIAFEAAAAAAAA8H8Q1QFrCyIAQQAgAEFvcUELRhsgACAAQckARxsiAEEbRw0AQRtBAEGI8zQoAgAbIQALIAJBEGokACAACw0AIABB/////wcQtQELrgQBBX8jAEEQayIEJAAgABBWGgJ/AkAgACABEJwEIgNFBEAgACgCHCIDIAAoAiBGBEAgACgCGCADQQF0QQEgAxsiA0ECdBDEASIFRQ0CIAAgAzYCICAAIAU2AhgLIAEQoQQiA0UNASAAIAAoAhwiAUEBajYCHCAAKAIYIAFBAnRqIAM2AgALIAMMAQtBAAshASAAEFIaIAEEfyAEIAIoAgg2AgggBCACKQIANwMAIwBBMGsiAiQAAkACfyABIgAoAhwiA/4QAnwhAQNAQQAgAUUNARogASADIAEgAUEBav5IAnwiAUcNAAtBAQtFBEBBACEDDAELIABBBGoiARBWGiACIAQoAgg2AiAgAiAEKQIANwMYIAAgAkEYahCdBCEDIAEQUhoCfyADBH8gAEEC/kECACEFQQEhAyAAKAIcIgEgBUECRg0BGiACIAA2AiwgAiAANgIQIAJBvgE2AiggAkG/ATYCJCACIAIpAiQ3AwgjAEEQayIDJAAgASgCeEEEahBWGiABKAJ4IQYgAyACKAIQNgIIIAMgAikCCDcDACAGIAMQnQQaIAEoAnhBBGoQUhoCQCABKAJ4QQL+QQIAQQJGDQAgAf4QAoABBEAgAUF//gACABoMAQsgASMDQdDxNBAeCyADQRBqJABBAQVBAAshAyAAKAIcCyIAQQH+JQJ8QQFGBEAgAEH8AGpB/////wcQtQELCyACQTBqJAAgAwVBAAshByAEQRBqJAAgBwsNAEHU8jQQpQRB2PI0C+4BAwJ8An8BfgJ9AkAgALwiA0EUdkH/D3EiBEGwCEkNAEMAAAAAIANBgICAfEYNARogBEH4D08EQCAAIACSDwsgAEMAAAAAXgRAIwBBEGsiA0MAAABwOAIMIAMqAgxDAAAAcJQPCyAAQwAAFsNfRQ0AIwBBEGsiA0MAAAAQOAIMIAMqAgxDAAAAEJQPC0HY2gErAwAgALsiASABQdDaASsDACIBoCICIAGhoSIBokHg2gErAwCgIAEgAaKiQejaASsDACABokQAAAAAAADwP6CgIAK9IgVCL4YgBadBH3FBA3RB0NgBaikDAHy/orYLCxQAQQwQXkEFEHBB0KkDQdIAEAIACxQAQQwQXkEEEHBB0KkDQdIAEAIACxQAQQwQXkEDEHBB0KkDQdIAEAIAC6UEAQR/AkACQAJAIAAoAgQgACgCACICa0E0bSIFQQFqIgNBxZ2xJ0kEQEHEnbEnIAAoAgggAmtBNG0iAkEBdCIEIAMgAyAESRsgAkHiztgTTxsiAwR/IANBxZ2xJ08NAiADQTRsEDIFQQALIgQgBUE0bGoiAiAB/QACAP0LAgAgAiAB/QACEP0LAhAgAiABKAIgNgIgIAIgASgCJDYCJCABQgA3AiAgAf0MAAAAAAAAAAAAAAAAAAAAAP0LAhAgAiABLQAwOgAwIAIgASkCKDcCKCAEIANBNGxqIQMgAkE0aiEFIAAoAgQiASAAKAIAIgRGDQIDQCACQTRrIgIgAUE0ayIB/QACAP0LAgAgAkEANgIYIAIgASgCEDYCECACIAEoAhQ2AhQgAiABKAIYNgIYIAFBADYCGCABQgA3AhAgAkEANgIkIAIgASgCHDYCHCACIAEoAiA2AiAgAiABKAIkNgIkIAFBADYCJCABQgA3AhwgAiABLQAwOgAwIAIgASkCKDcCKCABIARHDQALIAAgAzYCCCAAKAIEIQMgACAFNgIEIAAoAgAhASAAIAI2AgAgASADRg0DA0AgA0E0ayIAKAIcIgIEQCADQRRrIAI2AgAgAhAvCyADQSRrKAIAIgIEQCADQSBrIAI2AgAgAhAvCyAAIgMgAUcNAAsMAwsQNgALEEcACyAAIAM2AgggACAFNgIEIAAgAjYCAAsgAQRAIAEQLwsLqQUBCH8gASAAKAIIIgMgACgCACIEa0EMbU0EQAJAIAAoAgQiBSAEa0EMbSIIIAEgASAISxsiB0UNACAEIQMgByEGIAdBA3EiCQRAA0AgAyACKAIANgIAIAMgAigCBDYCBCADIAItAAg6AAggBkEBayEGIANBDGohAyAKQQFqIgogCUcNAAsLIAdBBEkNAANAIAMgAigCADYCACADIAIoAgQ2AgQgAyACLQAIOgAIIAMgAigCADYCDCADIAIoAgQ2AhAgAyACLQAIOgAUIAMgAigCADYCGCADIAIoAgQ2AhwgAyACLQAIOgAgIAMgAigCADYCJCADIAIoAgQ2AiggAyACLQAIOgAsIANBMGohAyAGQQRrIgYNAAsLIAEgCEsEQCAFIAEgCGtBDGxqIQEDQCAFIAIpAgA3AgAgBSACKAIINgIIIAVBDGoiBSABRw0ACyAAIAE2AgQPCyAAIAQgAUEMbGo2AgQPCyAEBEAgACAENgIEIAQQLyAAQQA2AgggAEIANwIAQQAhAwsCQCABQdaq1aoBTw0AQdWq1aoBIANBDG0iA0EBdCIEIAEgASAESRsgA0Gq1arVAE8bIgNB1qrVqgFPDQAgACADQQxsIgMQMiIENgIEIAAgBDYCACAAIAMgBGo2AgggBCEDIAFBDGwiAUEMayIGQQxuQQFqQQNxIgcEQANAIAMgAikCADcCACADIAIoAgg2AgggA0EMaiEDIAVBAWoiBSAHRw0ACwsgASAEaiEBIAZBJE8EQANAIAMgAikCADcCACADIAIoAgg2AgggAyACKAIINgIUIAMgAikCADcCDCADIAIoAgg2AiAgAyACKQIANwIYIAMgAikCADcCJCADIAIoAgg2AiwgA0EwaiIDIAFHDQALCyAAIAE2AgQPCxA2AAsUAEEMEF5BBhBwQdCpA0HSABACAAsCAAv9BwEHfyAAKAIQIQcgACgCJCEIAkAgAiABIgNGDQACQAJAIAMsAAAiBEEkaw5ZAgEBAQIAAgIBAQIBAQEBAQEBAQEBAQEBAQEBAgEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQICAQIBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAgIBCyAAKAIYDQELIAAgBBBZIANBAWohAwsCQAJAAkACQAJAIAEgA0cNAAJ/AkAgAiABIgNGDQAgA0EBaiIFIAJGDQAgAy0AAEHcAEcNAAJAAkAgBSwAACIEQSRrDloAAQEBAAAAAAEBAAEBAQEBAQEBAQEBAQEBAQEAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAABAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAABCyAAIAQQWSADQQJqDAILIAAoAgxB8AdxQcAARgRAIAAgBSACQQAQ1wIMAgsgA0ECQQACf0EAIQUCQAJAIARBeHFBMEcgBEH+AXFBOEdxDQAgBEH/AXEiBEExa0EISw0AIARBMGsiBCAAKAIQSw0BIAAgBBDaAkEBIQULIAUMAQsQ/wEACxtqIQMLIAMLIgMgAUcNAAJAIAEgAkYNACABLQAAQS5HDQBBCBAyIQMgACgCJCIEKAIEIQUgA0GktQE2AgAgAyAFNgIEIAQgAzYCBAwCCyAAIAEgAhDiAiEDCwJAAkAgASADRw0AIAIgA0YNAAJAAkAgASwAACIDQSRrDgUDBgYGAQALIANB3gBHDQVBDBAyIQMgACgCDCEEIAAoAiQiBSgCBCEGIANB9KkBNgIAIAMgBjYCBCADIARB8A9xQYAMRjoACCAFIAM2AgQMAwsCQCAALQAMQQJxBEAgACgCECEEDAELQQwQMiEDIAAgACgCEEEBaiIENgIQIAAoAiQiBSgCBCEGIAMgBDYCCCADQcCxATYCACADIAY2AgQgBSADNgIEIAAgACgCJCgCBDYCJAsgACAAKAIYQQFqNgIYIAAgAUEBaiACEIYCIgUgAkYNBSAFLQAAQSlHDQUgAC0ADEECcUUEQEEMEDIhAyAAKAIkIgYoAgQhCSADIAQ2AgggA0GQsgE2AgAgAyAJNgIEIAYgAzYCBCAAIAAoAiQoAgQ2AiQLIAAgACgCGEEBazYCGCAFQQFqIQMLIAEgA0cNAgwDC0EMEDIhAyAAKAIMIQQgACgCJCIFKAIEIQYgA0G8qgE2AgAgAyAGNgIEIAMgBEHwD3FBgAxGOgAIIAUgAzYCBAsgACAAKAIkKAIENgIkIAFBAWohAwsgACADIAIgCCAHQQFqIAAoAhBBAWoQ4wIhAQsgAQ8LEIMCAAuxAgEEfyAAKAIkIQYCQCAAIAEgAhCFAiIDIAFGDQADQCAAIAMiBCACEIUCIgMgBEcNAAsgASAERg0AIAIgBEcEQANAIAQtAABB/ABHBEAgBA8LIAAoAiQhBSAAIARBAWoiASACEIUCIgMgAUYNAgNAIAAgAyIEIAIQhQIiAyAERw0ACyABIARGDQJBDBAyIQMgBigCBCEBIAMgBSgCBDYCCCADIAE2AgQgA0GctAE2AgAgBiADNgIEIAVBADYCBEEIEDIhAyAAKAIkKAIEIQEgA0HIqAE2AgAgAyABNgIEIAUgAzYCBCAAKAIkQQA2AgRBCBAyIQMgBSgCBCEBIANB3LQBNgIAIAMgATYCBCAAKAIkIAM2AgQgACAFKAIENgIkIAIgBEcNAAsLIAIPCxDGBAALrgIBBH8gACgCACIDBEAgAyECIAMgACgCBCIERwRAA0AgBEEMayICKAIAIgUEQCAEQQhrIAU2AgAgBRAvCyACIgQgA0cNAAsgACgCACECCyAAIAM2AgQgAhAvIABBADYCCCAAQgA3AgALIAAgASgCADYCACAAIAEoAgQ2AgQgACABKAIINgIIIAFBADYCCCABQgA3AgAgACgCDCIDBEAgAyECIAMgACgCECIERwRAA0AgBEEMayICKAIAIgUEQCAEQQhrIAU2AgAgBRAvCyACIgQgA0cNAAsgACgCDCECCyAAIAM2AhAgAhAvIABBADYCFCAAQgA3AgwLIAAgASgCDDYCDCAAIAEoAhA2AhAgACABKAIUNgIUIAFBADYCFCABQgA3AgwgACABKQIYNwIYC/ABAgR/AX4jAEFAaiICJAAgAiABQsD8FX8iBj4CACACIAZCgKOkfn4gAUIKfnwiAULg1AN/IgY+AgQgAiAGQqCrfH4gAXwiAULoB38iBj4CCCACIAZCmPj//w9+IAF8PgIQIAJB0coANgIMIAJBIGoiBEEgQdslIAIQxQIaIAQQaCIDQfD///8HSQRAAkAgA0EKTQRAIAAgAzoACwwBCyADQQ9yQQFqIgUQMiEEIAAgBUGAgICAeHI2AgggACAENgIAIAAgAzYCBCAEIQALIAAgAkEgaiAD/AoAACAAIANqQQA6AAAgAkFAayQADwsQTQAL60EEGX8FfQN8AX4jAEGAAWsiBiQAAkACQCAAKAIAQQH+HgIAIhQgACgCBCgCAEgEQCAGQdwAaiERIAZB0ABqIRIDQAJAIAAoAggoAgAgFEHYFGxqIggtALECDQAgCC0AsAINAAJAAkACQAJAAkACQCAAKAIMIgEoAgAOAgABBgsgACgCFCgCACILKAL8ASECIAsoAsABIQogACgCECoCAEO9N4Y1XUUNAUQAAAAAAAAAACEfQQAhBEEAIQNEAAAAAAAAAAAhICACIApODQMgCiACayIBQQFxIQsgCCgCtAIhByACQQFqIApGBEAgAiEBDAMLIAFBfnEhDCACIQFBACEFA0ACfwJAIAcgAUECdGoqAgAiGkMAAID/Ww0AICAgGrsiIaAhICAfICFjRQ0AIAEMAQsgHyEhIAMLIQkCQCAHIAFBAWoiA0ECdGoqAgAiGkMAAID/XARAICAgGrsiH6AhICAfICFkDQELIAkhAyAhIR8LIAFBAmohASAMIAVBAmoiBUcNAAsMAgsgASgCdCENAkAgACgCFCgCACITKALAASIMIAgoAtwCIgIgCCgC2AIiBGtBBHUiAUsEQEEAIQpBACEHAkAgDCABayIBIAgoAuACIgUgAmtBBHVNBEACQCABRQ0AIAIhAyABQQdxIgQEQEEAIQUDQCADQQA2AgggA0IANwMAIANBEGohAyAFQQFqIgUgBEcNAAsLIAFBBHQgAmohAiABQQFrQf////8AcUEHSQ0AA0AgA0IANwNwIANCADcDYCADQgA3A1AgA0IANwNAIANCADcDMCADQgA3AyAgA0IANwMQIANBADYCCCADQgA3AwAgA0EANgJ4IANBADYCaCADQQA2AlggA0EANgJIIANBADYCOCADQQA2AiggA0EANgIYIANBgAFqIgMgAkcNAAsLIAggAjYC3AIMAQsCQCACIAgoAtgCIgRrQQR1IgsgAWoiA0GAgICAAUkEQEH/////ACAFIARrIgVBA3UiCSADIAMgCUkbIAVB8P///wdPGyIJBEAgCUGAgICAAU8NAiAJQQR0EDIhBwsgByALQQR0aiIFIQMgAUEHcSILBEADQCADQQA2AgggA0IANwMAIANBEGohAyAKQQFqIgogC0cNAAsLIAFBBHQgBWohCiABQQFrQf////8AcUEHTwRAA0AgA0IANwNwIANCADcDYCADQgA3A1AgA0IANwNAIANCADcDMCADQgA3AyAgA0IANwMQIANBADYCCCADQgA3AwAgA0EANgJ4IANBADYCaCADQQA2AlggA0EANgJIIANBADYCOCADQQA2AiggA0EANgIYIANBgAFqIgMgCkcNAAsLIAIgBEcEQANAIAVBEGsiBSACQRBrIgIpAwA3AwAgBSACKAIINgIIIAIgBEcNAAsLIAggByAJQQR0ajYC4AIgCCAKNgLcAiAIKALYAiEBIAggBTYC2AIgAQRAIAEQLwsMAgsMDAsQRwALIAgoAtgCIQQMAQsgASAMTQ0AIAggBCAMQQR0ajYC3AILAkAgDEEATA0AIAgoAsACIQJBACEBIAxBAUcEQCAMQX5xIQVBACELA0AgAiABQQJ0aioCACEaIAQgAUEEdGoiAyABNgIIIAMgGrs5AwAgAiABQQFyIgNBAnRqKgIAIRogBCADQQR0aiIJIAM2AgggCSAauzkDACABQQJqIQEgC0ECaiILIAVHDQALCyAMQQFxRQ0AIAIgAUECdGoqAgAhGiAEIAFBBHRqIgIgATYCCCACIBq7OQMACyAGAn8gDUUEQEEAIQlBAAwBCyAEIA1B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QQJ0IgIgACgCJGooAgAgACgCICACaigCABEFAAwBCwsgACgCHCIBQQRqQX/+HgIARQRAIAEgASgCACgCCBEBAAsgACgCIBAvIAAoAiQQLyAAKAIwEC8gACgCPBAvIAALIAAgACAAKAIYRSABciIBNgIQIAAoAhQgAXEEQBBGAAsLRgAjAEEQayIBJAAgASADNgIMIAQgAiADIAJrIgL8CgAAIAEgAiAEajYCCCAAIAEoAgw2AgAgACABKAIINgIEIAFBEGokAAs2AQF/IwBBEGsiAyQAIAMgATYCDCADIAI2AgggACADKAIMNgIAIAAgAygCCDYCBCADQRBqJAALOwEBfyAAQfDKAigCACIBNgIAIAAgAUEMaygCAGpB/MoCKAIANgIAIABBCGoQ8gEaIABB7ABqEMACIAALDAAgAEEIahDAAiAACwgAIAAQugIaC9IDAgJ+An8jAEEgayIEJAACQCABQv///////////wCDIgNCgICAgICAwIA8fSADQoCAgICAgMD/wwB9VARAIAFCBIYgAEI8iIQhAyAAQv//////////D4MiAEKBgICAgICAgAhaBEAgA0KBgICAgICAgMAAfCECDAILIANCgICAgICAgIBAfSECIABCgICAgICAgIAIUg0BIAIgA0IBg3whAgwBCyAAUCADQoCAgICAgMD//wBUIANCgICAgICAwP//AFEbRQRAIAFCBIYgAEI8iIRC/////////wODQoCAgICAgID8/wCEIQIMAQtCgICAgICAgPj/ACECIANC////////v//DAFYNAEIAIQIgA0IwiKciBUGR9wBJDQAgBEEQaiAAIAFC////////P4NCgICAgICAwACEIgIgBUGB9wBrEGYgBCAAIAJBgfgAIAVrEK8BIAQpAwhCBIYgBCkDACIAQjyIhCECIAQpAxAgBCkDGIRCAFKtIABC//////////8Pg4QiAEKBgICAgICAgAhaBEAgAkIBfCECDAELIABCgICAgICAgIAIUg0AIAJCAYMgAnwhAgsgBEEgaiQAIAIgAUKAgICAgICAgIB/g4S/C4gCAAJAIAAEfyABQf8ATQ0BAkAjAygCYCgCAEUEQCABQYB/cUGAvwNGDQMMAQsgAUH/D00EQCAAIAFBP3FBgAFyOgABIAAgAUEGdkHAAXI6AABBAg8LIAFBgEBxQYDA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fyMAQRBrIgEkACAAQQE2AiAgAEEEaiICEFYaIAAoAiwgACgCMEcEQANAIAFBBGogABCfBCACEFIaIAEoAgwgASgCBBEBACACEFYaIAAoAiwgACgCMEcNAAsLIAIQUhogAEEANgIgIAFBEGokAAueDAMGfAN+CH8jAEEQayIOJAACQAJAIAG9IglCNIinIg1B/w9xIg9BvghrIhBB/35LIAC9IghCNIinIgtB/w9rQYJwT3ENACAJQgGGIgpCgICAgICAgBB8QoGAgICAgIAQVARARAAAAAAAAPA/IQIgCEKAgICAgICA+D9RDQIgClANAiAKQoGAgICAgIBwVCAIQgGGIghCgICAgICAgHBYcUUEQCAAIAGgIQIMAwsgCEKAgICAgICA8P8AUQ0CRAAAAAAAAAAAIAEgAaIgCEL/////////7/8AViAJQgBZcxshAgwCCyAIQgGGQoCAgICAgIAQfEKBgICAgICAEFQEQCAAIACiIQIgCEIAUwRAIAKaIAIgCRCkBEEBRhshAgsgCUIAWQ0CIwBBEGsiC0QAAAAAAADwPyACozkDCCALKwMIIQIMAgsgCEIAUwRAIAkQpAQiDEUEQCAAIAChIgAgAKMhAgwDCyALQf8PcSELIAxBAUZBEnQhDCAIQv///////////wCDIQgLIBBB/35NBEBEAAAAAAAA8D8hAiAIQoCAgICAgID4P1ENAiAPQb0HTQRAIAEgAZogCEKAgICAgICA+D9WG0QAAAAAAADwP6AhAgwDCyANQYAQSSAIQoGAgICAgID4P1RHBEAjAEEQayILRAAAAAAAAABwOQMIIAsrAwhEAAAAAAAAAHCiIQIMAwsjAEEQayILRAAAAAAAAAAQOQMIIAsrAwhEAAAAAAAAABCiIQIMAgsgCw0AIABEAAAAAAAAMEOivUL///////////8Ag0KAgICAgICAoAN9IQgLAnwgCUKAgIBAg78iBSEHIA4gCEKAgICA0Kql8z99IglCNIentyIDQdCgAisDAKIgCUItiKdB/wBxQQV0IgtBqKECaisDAKAgCCAJQoCAgICAgIB4g30iCEKAgICACHxCgICAgHCDvyIAIAtBkKECaisDACIEokQAAAAAAADwv6AiAiAIvyAAoSAEoiIEoCIAIANByKACKwMAoiALQaChAmorAwCgIgMgACADoCIDoaCgIAQgAEHYoAIrAwAiBKIiBiACIASiIgSgoqAgAiAEoiICIAMgAyACoCICoaCgIAAgACAGoiIDoiADIAMgAEGIoQIrAwCiQYChAisDAKCiIABB+KACKwMAokHwoAIrAwCgoKIgAEHooAIrAwCiQeCgAisDAKCgoqAiACACIAIgAKAiAqGgOQMIIAcgAr1CgICAQIO/IgOiIQAgASAFoSADoiAOKwMIIAIgA6GgIAGioCEBAkAgAL1CNIinQf8PcSILQckHa0E/SQ0AIAtByQdJBEAgAEQAAAAAAADwP6AiAJogACAMGwwCCyALQYkISSERQQAhCyARDQAgAL1CAFMEQCMAQRBrIgtEAAAAAAAAAJBEAAAAAAAAABAgDBs5AwggCysDCEQAAAAAAAAAEKIMAgsjAEEQayILRAAAAAAAAADwRAAAAAAAAABwIAwbOQMIIAsrAwhEAAAAAAAAAHCiDAELQZjbASsDACAAokGg2wErAwAiAqAiAyACoSICQbDbASsDAKIgAkGo2wErAwCiIACgoCABoCIAIACiIgEgAaIgAEHQ2wErAwCiQcjbASsDAKCiIAEgAEHA2wErAwCiQbjbASsDAKCiIAO9IgmnQQR0QfAPcSINQYjcAWorAwAgAKCgoCEAIA1BkNwBaikDACAJIAytfEIthnwhCCALRQRAAnwgCUKAgICACINQBEAgCEKAgICAgICAiD99vyIBIACiIAGgRAAAAAAAAAB/ogwBCyAIQoCAgICAgIDwP3wiCL8iASAAoiIDIAGgIgCZRAAAAAAAAPA/YwR8IwBBEGsiCyESIAtEAAAAAAAAEAA5AwggEiALKwMIRAAAAAAAABAAojkDCCAIQoCAgICAgICAgH+DvyAARAAAAAAAAPC/RAAAAAAAAPA/IABEAAAAAAAAAABjGyICoCIFIAMgASAAoaAgACACIAWhoKCgIAKhIgAgAEQAAAAAAAAAAGEbBSAAC0QAAAAAAAAQAKILDAELIAi/IgEgAKIgAaALIQILIA5BEGokACACC8EBAQN/AkAgASACKAIQIgMEfyADBSACENICDQEgAigCEAsgAigCFCIFa0sEQCACIAAgASACKAIkEQMADwsCQCACKAJQQQBIBEBBACEDDAELIAEhBANAIAQiA0UEQEEAIQMMAgsgACADQQFrIgRqLQAAQQpHDQALIAIgACADIAIoAiQRAwAiBCADSQ0BIAAgA2ohACABIANrIQEgAigCFCEFCyAFIAAgARB7GiACIAIoAhQgAWo2AhQgASADaiEECyAECzcBAn8gACgCTEEASARAIAAgASACEKkEDwsgABCCASEEIAAgASACEKkEIQIgBARAIAAQgQELIAILfAECfyAAIAAoAkgiAUEBayABcjYCSCAAKAIUIAAoAhxHBEAgAEEAQQAgACgCJBEDABoLIABBADYCHCAAQgA3AxAgACgCACIBQQRxBEAgACABQSByNgIAQX8PCyAAIAAoAiwgACgCMGoiAjYCCCAAIAI2AgQgAUEbdEEfdQuNAQECfyMAQRBrIgAkACAAQQo6AA8CQAJAQcC6AygCACIBBH8gAQVBsLoDENICDQJBwLoDKAIAC0HEugMoAgAiAUYNAEGAuwMoAgBBCkYNAEHEugMgAUEBajYCACABQQo6AAAMAQtBsLoDIABBD2pBAUHUugMoAgARAwBBAUcNACAALQAPGgsgAEEQaiQAC1kBAX8gACAAKAJIIgFBAWsgAXI2AkggACgCACIBQQhxBEAgACABQSByNgIAQX8PCyAAQgA3AgQgACAAKAIsIgE2AhwgACABNgIUIAAgASAAKAIwajYCEEEAC9MBAgF/AnwgAisDICEFAn8CQCABKwMgIgYgACsDIGRFBEBBACAFIAZkRQ0CGiABIAIQPkEBIAErAyAgACsDIGRFDQIaIAAgARA+DAELIAUgBmQEQCAAIAIQPkEBDAILIAAgARA+QQEgAisDICABKwMgZEUNARogASACED4LQQILIQQgAysDICACKwMgZAR/IAIgAxA+IAIrAyAgASsDIGRFBEAgBEEBag8LIAEgAhA+IAErAyAgACsDIGRFBEAgBEECag8LIAAgARA+IARBA2oFIAQLC34BAX8gACABIAIgAxDTAiEFIAQrAyAgAysDIGQEfyADIAQQPiADKwMgIAIrAyBkRQRAIAVBAWoPCyACIAMQPiACKwMgIAErAyBkRQRAIAVBAmoPCyABIAIQPiABKwMgIAArAyBkRQRAIAVBA2oPCyAAIAEQPiAFQQRqBSAFCwtIAQF/IABBwLMBNgIAIAAoAggiAQRAIAEgASgCACgCBBEBAAsgAEHcqQE2AgAgACgCBCIBBEAgASABKAIAKAIEEQEACyAAEC8L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A0AgBEEEayIEIAFBBGsiASgCADYCACABIAZHDQALCyAHIA0gCyANayIB/AoAACAAIAU2AgggACABIAdqNgIEIAAgBDYCACAGBEAgBhAvCwsPCxA2AAsQRwALIAohBSAEIQELA0AgASAFKAIANgIAIAFBBGohASAFQQRqIgUgC0kNAAsLIAAgATYCBCAEIAlBAnRrIA0gCPwKAAAgDSACIAcgAmv8CgAAC+kaAgl/A3wjAEEQayIDJAADQCABQRBrIQcDQCAAIQQDQAJAAkACQAJAAkACQAJAAkAgASAEayIIQQR1IgkOBgcHAAEEAgMLIAFBEGsiACsDACAEKwMAZEUNBiADIAT9AAMA/QsDACAEIAAoAgg2AgggBCAAKQMANwMAIAAgAygCCDYCCCAAIAMpAwA3AwAMBgsgBEEQaiEAIAFBEGsiASsDACEMIAQrAxAiDSAEKwMAZEUEQCAMIA1kRQ0GIAMgAP0AAwD9CwMAIAAgASgCCDYCCCAAIAEpAwA3AwAgASADKAIINgIIIAEgAykDADcDACAEKwMQIAQrAwBkRQ0GIAMgBP0AAwD9CwMAIAQgACgCCDYCCCAEIAApAwA3AwAgACADKAIINgIIIAAgAykDADcDAAwGCyAMIA1kBEAgAyAE/QADAP0LAwAgBCABKAIINgIIIAQgASkDADcDACABIAMoAgg2AgggASADKQMANwMADAYLIAMgBP0AAwD9CwMAIAQgACgCCDYCCCAEIAApAwA3AwAgACADKAIINgIIIAAgAykDADcDACABKwMAIAQrAxBkRQ0FIAMgAP0AAwD9CwMAIAAgASgCCDYCCCAAIAEpAwA3AwAgASADKAIINgIIIAEgAykDADcDAAwFCyAEIARBEGoiAiAEQSBqIgAgBEEwaiIFENgBGiABQRBrIgErAwAgBCsDMGRFDQQgAyAF/QADAP0LAwAgBSABKAIINgIIIAUgASkDADcDACABIAMoAgg2AgggASADKQMANwMAIAUrAwAgACsDAGRFDQQgAyAA/QADAP0LAwAgACAFKAIINgIIIAAgBSkDADcDACAFIAMoAgg2AgggBSADKQMANwMAIAArAwAgAisDAGRFDQQgAyAC/QADAP0LAwAgAiAAKAIINgIIIAIgACkDADcDACAAIAMoAgg2AgggACADKQMANwMAIAQrAxAgBCsD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EC8LIAAoAvinASIBBEAgAEH8pwFqIAE2AgAgARAvCyAAKALspwEiAQRAIABB8KcBaiABNgIAIAEQLwsgAEHUpwFqKAIAIgEEQCAAQdinAWogATYCACABEC8LIABBwKcBaigCACIBBEAgAEHEpwFqIAE2AgAgARAvCyAAQaynAWooAgAiAQRAIABBsKcBaiABNgIAIAEQLwsgAEGYpwFqKAIAIgEEQCAAQZynAWogATYCACABEC8LIABBuJIBahClASAAQeD9AGoQpQEgAEGI6QBqEKUBIABBsNQAahClASAAQdg/ahClASAAQYArahClASAAQagWahClASAAQdABahClASAAKAKoASIBBEAgACABNgKsASABEC8LIAAoAoABIgMEQCAAKAKEASICIAMiAUcEQANAIAJBDGsgAkEQayICKAIIEJYBIAIgA0cNAAsgACgCgAEhAQsgACADNgKEASABEC8LIAAoAlgiAwRAIAAoAlwiAiADIgFHBEADQCACQQxrIAJBEGsiAigCCBCWASACIANHDQALIAAoAlghAQsgACADNgJcIAEQLwsgAAtDACABBEAgACABKAIAEPMCIAAgASgCBBDzAiABLAArQQBIBEAgASgCIBAvCyABLAAbQQBIBEAgASgCEBAvCyABEC8LCzEAIAEEQCAAIAEoAgAQ9AIgACABKAIEEPQCIAEsAB9BAEgEQCABKAIUEC8LIAEQLwsLmUgEF34XfwR7AX0jAEGQAWsiIyQAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIAEoAjAiHSABKAIAIhpBJGwiG0GYmwFqKAIARyImDQAgATUCNCIDIAEpAxAgHa1+IBtBlJsBajQCAH9SDQAgATUCOCIFIAEpAxggA35SDQAgATUCPCABKQMgIAV+Ug0AIAIoAjAiGyACKAIAIhxBJGwiIkGYmwFqKAIARw0AIAI1AjQiAyACKQMQIButfiAiQZSbAWo0AgB/Ug0AIAI1AjgiBSACKQMYIAN+Ug0AIBogHEcNACACNQI8IAIpAyAgBX5RDQELAkACQAJAIBoOAgIAAQsgAikDKCIRIAIpAyAiDyACKQMYIhAgAikDECIMfn5+IAEpAygiDSABKQMgIgcgASkDGCIOIAEpAxAiCX5+flEEQAJAIAAoAgAOAw0ADQALIAIoAjwhISACKAI4IR8g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IBogHUcNACAOQgBXDQggDUIAVw0IIBsgHE4NCCAdIAynbCEAIBysIQsgG6whBQNAIB4gBKciGmwhGyAaICRsIRpCACEGA0AgKCAGpyIdbCEcIB0gIGwhHSAFIQMDQCACKALQASAhIAOnIh9saiAdaiAaaiABKALQASAfICJsaiAcaiAbaiAA/AoAACADQgF8IgMgC1INAAsgBkIBfCIGIA1SDQALIA4gBEIBfCIEUg0ACwwICwJAIBogAEEkbCImQZibAWooAgBHDQAgIa0iBSAMIBqtfiAmQZSbAWo0AgAiC39SDQAgIK0iCSAFIBB+Ug0AICStIAkgD35SDQAgHUEERgRAIABFBEAgDkIAVw0KIA1CAFcNCiAbIBxODQogAigC0AEhHSAbIAenQQJ0IgBsISEgHyAcayAAbCEfIBsgImwhJSAcIBtrQQFxISZBACECIBwgG0EBaiInRiEpA0AgHiAIp2whJEIAIQMDQCACICFqIRogKCADp2whICAmBH8gGiAdaiABKALQASAlaiAgaiAkaiAA/AoAACAAIBpqIRogJwUgGwshAiApRQRAA0AgGiAdaiABKALQASACICJsaiAgaiAkaiAA/AoAACAdIAAgGmoiGmogASgC0AEgAkEBaiAibGogIGogJGogAPwKAAAgACAaaiEaIAJBAmoiAiAcRw0ACwsgGiAfaiECIANCAXwiAyANUg0ACyAOIAhCAXwiCFINAAsMCgtBsYAcIAB2QQFxRQRAIAcgC38hAyAOQgBXDQogDUIAVw0KIBsgHE4NCiAAQSRsQaSbAWooAgAhHSACKALQASEkIBogA6dsIhogG2whJSAaIB8gHGtsISYgB6chICAbQQFqIScgGyAibCEpIBwgG2siKkEBcSErQQAhAANAIB4gCKdsISFCACEDA0AgACAlaiECICggA6dsIR8gKwR/IAEoAtABIClqIB9qICFqIAIgJGogICAdEQUAIAIgGmohAiAnBSAbCyEAICpBAUcEQANAIAEoAtABIAAgImxqIB9qICFqIAIgJGogICAdEQUAIAEoAtABIABBAWogImxqIB9qICFqICQgAiAaaiICaiAgIB0RBQAgAiAaaiECIABBAmoiACAcRw0ACwsgAiAmaiEAIANCAXwiAyANUg0ACyAOIAhCAXwiCFINAAsMCgtBvMMCKAIAEDAa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KAIMNgIAIAhBEGokAA8LEE0ACw0AIAAgASABEGgQywELIAAgAEHotQM2AgAgAEHYtgM2AgAgAEEEaiABEIoDIAALPQECfyABEGgiA0ENahAyIgJBADYCCCACIAM2AgQgAiADNgIAIAJBDGoiAiABIANBAWr8CgAAIAAgAjYCAAsSACAAKAIAIAEoAgAQnwFBH3YLgAMCBX8DfiMAQdAAayIDJAACQAJAIAEpAyAiCCACKQMYUQRAIAIpAyhCAVINASACKAIAQRJHDQICfwJAIAEoAogBDQAgAigCiAENAEEADAELQQELIQcgASkDECEJIAIpAxAhCiADIAIpAyA3A0ggAyAINwNAIAMgCjcDOCADIAk3AzAgAEEAQQQgA0EwakEAQQAQSSIEQSI2AkAgBwRAIAAgBCgCACAEKAIMIARBEGpBAEEAEEkhBQsgBCABNgKMASAEIAU2AogBIAQgAjYCkAEgA0HQAGokACAEDwtBvMMCKAIAEDAaIANBkyo2AiggA0GRJTYCJCADQeQmNgIgQbjDAigCAEHL5AAgA0EgahAxEAAAC0G8wwIoAgAQMBogA0HLPjYCGCADQZIlNgIUIANB5CY2AhBBuMMCKAIAQcvkACADQRBqEDEQAAALQbzDAigCABAwGiADQfE7NgIIIANBkyU2AgQgA0HkJjYCAEG4wwIoAgBBy+QAIAMQMRAAAAsMACAAEJQCGiAAEC8LCQAgACABEJQECwcAIAAQUhoLCAAgABCmAhoLFgAgACABIAJCgICAgICAgICAfxDgAwsJACAAEEQ2AgALIwECfyAAIQEDQCABIgJBBGohASACKAIADQALIAIgAGtBAnULBwAgACgCBAswACMAQRBrIgIkAAJAIAAgAUYEQCABQQA6AHgMAQsgAkEPaiABEJsDCyACQRBqJAALJgEBfyAAKAIEIQIDQCABIAJHBEAgAkEEayECDAELCyAAIAE2AgQLSwEBfyMAQRBrIgMkAAJAAkAgAkEeSw0AIAEtAHgNACABQQE6AHgMAQsgA0EPaiACEJ0DIQELIANBEGokACAAIAI2AgQgACABNgIAC18BBX8jAEEQayIAJAAgAEH/////AzYCDCAAQf////8HNgIIIwBBEGsiASQAIABBCGoiAigCACAAQQxqIgMoAgBJIQQgAUEQaiQAIAIgAyAEGygCACEF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AyAAIAJBAWo2AgggAiABQQx2QeABcjoAACAAIAAoAggiAkEBajYCCCACIAFBBnZBP3FBgAFyOgAAIAAgACgCCCICQQFqNgIIIAIgAUE/cUGAAXI6AAALIAAgACgCDEECaiICNgIMDAELC0ECDAILQQEMAQsgBQshCSAEIAAoAgw2AgAgByAAKAIINgIAIABBEGokACAJCz4BA38jAEEQayIBJAAgASAANgIMIAFBCGogAUEMahB/IQNBBEEBIwMoAmAoAgAbIQIgAxB+IAFBEGokACACCzwBAn8jAEEQayIFJAAgBSAENgIMIAVBCGogBUEMahB/IQYgACABIAIgAxDwASEAIAYQfiAFQRBqJAAgAAsSACAEIAI2AgAgByAFNgIAQQMLKAEBfyAAQdzsAjYCAAJAIAAoAggiAUUNACAALQAMRQ0AIAEQLwsgAAsEACABC0ABAn8gACgCACgCACIAKAIAIAAoAggiAkEBdWohASAAKAIEIQAgASACQQFxBH8gASgCACAAaigCAAUgAAsRAQALLgAgASAAQQhqIgAoAgQgACgCACIAa0ECdUkEfyABQQJ0IABqKAIAQQBHBUEACwvLAQEDfyAAQcjsAjYCACAAQQhqIQMDQCACIAAoAgwgACgCCCIBa0ECdUkEQCACQQJ0IAFqKAIAIgEEQCABQQRqQX/+HgIARQRAIAEgASgCACgCCBEBAAsLIAJBAWohAgwBCwsgAEGYAWoQMxojAEEQayICJAAgAiADNgIMIAIoAgwiASgCBBogASgCCBogASgCABogASgCAARAIAEQsAMgAigCDEEQaiACKAIMIgEoAgAgASgCCCABKAIAa0ECdRCVAwsgAkEQaiQAIAALDAAgACAAKAIAEJYDC3ABAX8jAEEQayICJAAgAiAANgIEIAIgACgCBCIANgIIIAIgACABQQJ0ajYCDCACKAIIIQEgAigCDCEAA0AgACABRgRAIAIoAgQgAigCCDYCBCACQRBqJAAFIAFBADYCACACIAFBBGoiATYCCAwBCwsLIAAgAEGY9QI2AgAgACgCCBBERwRAIAAoAggQ4wMLIAALBABBfwvgAQEIfyMAQRBrIgUkACMAQSBrIgMkACADQRhqIAAgARCaAyADQRBqIANBDGogAygCGCADKAIcIAIQvAIgAygCECEEIwBBEGsiASQAIAEg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DUIANwIAIA1BADYCCCABQRBqJAAjAEEQayIBJAAgC0EkaiIMQgA3AgAgDEEANgIIIAFBEGokACMAQRBrIgEkACALQRhqIhBCADcCACAQQQA2AgggAUEQaiQAIwBBEGsiCiQAIAsCfyACBEAgCkEEaiICIAMQuwMiASABKAIAKAIsEQIAIAsgCigCBDYAXCACIAEgASgCACgCIBECACAMIAIQiAEgAhBQGiACIAEgASgCACgCHBECACANIAIQiAEgAhBQGiALIAEgASgCACgCDBEAADYCWCALIAEgASgCACgCEBEAADYCVCACIAEgASgCACgCFBECACAPIAIQZSACEDMaIAIgASABKAIAKAIYEQIAIA4gAhCIASACEFAaIAEgASgCACgCJBEAAAwBCyAKQQRqIgIgAxC6AyIBIAEoAgAoAiwRAgAgCyAKKAIENgBcIAIgASABKAIAKAIgEQIAIAwgAhCIASACEFAaIAIgASABKAIAKAIcEQIAIA0gAhCIASACEFAaIAsgASABKAIAKAIMEQAANgJYIAsgASABKAIAKAIQEQAANgJUIAIgASABKAIAKAIUEQIAIA8gAhBlIAIQMxogAiABIAEoAgAoAhgRAgAgDiACEIgBIAIQUBogASABKAIAKAIkEQAACzYCFCAKQRBqJAAgCSAIKAIANgIAIARBgARxIRJBACEDQQAhAQNAIAEhAgJAAkACQAJAIANBBEYNACAAIAtBjARqEEINAEEAIQoCQAJAAkACQAJAAkAgC0HcAGogA2osAAAOBQEABAMFCQsgA0EDRg0HIAdBAQJ/IAAoAgAiASgCDCIEIAEoAhBGBEAgASABKAIAKAIkEQAADAELIAQoAgALIAcoAgAoAgwRAwAEQCALQQxqIAAQvAMgECALKAIMEKECDAILIAUgBSgCAEEEcjYCAEEAIQAMBgsgA0EDRg0GCwNAIAAgC0GMBGoQQg0GIAdBAQJ/IAAoAgAiASgCDCIEIAEoAhBGBEAgASABKAIAKAIkEQAADAELIAQoAgALIAcoAgAoAgwRAwBFDQYgC0EMaiAAELwDIBAgCygCDBChAgwACwALAkACfyANLQALQQd2BEAgDSgCBAwBCyANLQALQf8AcQtFDQACfyAAKAIAIgEoAgwiBCABKAIQRgRAIAEgASgCACgCJBEAAAwBCyAEKAIACwJ/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CyADKAIACwJ/IAItAAtBB3YEQCACKAIADAELIAILIApBAnRqKAIARg0BCyAFIAUoAgBBBHI2AgBBACEADAMLIAAQXBogCkEBaiEKDAALAAtBASEAIBEoAgAgCygCZEYNAEEAIQAgC0EANgIMIA8gESgCACALKAJkIAtBDGoQZCALKAIMBEAgBSAFKAIAQQRyNgIADAELQQEhAAsgEBBQGiAMEFAaIA0QUBogDhBQGiAPEDMaIBEoAgAhASARQQA2AgAgAQRAIAEgESgCBBEBAAsMAwsgAiEBCyADQQFqIQMMAAsACyALQZAEaiQAIAALOQECfyABKAIAIQMgAUEANgIAIAAoAgAhAiAAIAM2AgAgAgRAIAIgACgCBBEBAAsgACABKAIENgIECwoAIABBoIk1EHkLCgAgAEGoiTUQeQvkAQEGfyMAQRBrIgUkACAAKAIEIQNBAQJ/IAIoAgAgACgCAGsiBEH/////B0kEQCAEQQF0DAELQX8LIgQgBEEBTRshBCABKAIAIQcgACgCACEIIANB9wFGBH9BAAUgACgCAAsgBBDEASIGBEAgA0H3AUcEQCAAKAIAGiAAQQA2AgALIAVB9gE2AgQgACAFQQhqIAYgBUEEahBRIgMQvgMgAygCACEGIANBADYCACAGBEAgBiADKAIEEQEACyABIAAoAgAgByAIa2o2AgAgAiAEIAAoAgBqNgIAIAVBEGokAA8LEEYACyABAX8gASgCABD9A8AhAiAAIAEoAgA2AgQgACACOgAAC6kZAQl/IwBBkARrIgskACALIAo2AogEIAsgATYCjAQCQCAAIAtBjARqEEMEQCAFIAUoAgBBBHI2AgBBACEADAELIAtB9wE2AkwgCyALQegAaiALQfAAaiALQcwAaiIPEFEiESgCACIBNgJkIAsgAUGQA2o2AmAjAEEQayIBJAAgD0IANwIAIA9BADYCCCABQRBqJAAjAEEQayIBJAAgC0FAayIOQgA3AgAgDkEANgIIIAFBEGokACMAQRBrIgEkACALQTRqIg1CADcCACANQQA2AgggAUEQaiQAIwBBEGsiASQAIAtBKGoiDEIANwIAIAxBADYCCCABQRBqJAAjAEEQayIBJAAgC0EcaiIQQgA3AgAgEEEANgIIIAFBEGokACMAQRBrIgokACALAn8gAgRAIApBBGoiAiADEMAD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fHwhFiAOIAhBAEdqIQ4LIAFBAWohBCABLQABIgENAQwICwsgBSEADAELQQEhB0EAIQpBACEADAILIAwhBwwDCyAMIQcLIA4NAQtBfyEOCyAHRQ0AIAoQLyAAEC8LIBEEQCADEIEBCyAGQbACaiQAIANBkAFqJAAgDgtDAAJAIABFDQACQAJAAkACQCABQQJqDgYAAQICBAMECyAAIAI8AAAPCyAAIAI9AQAPCyAAIAI+AgAPCyAAIAI3AwALC7UDAgN/AX4jAEEgayIDJAACQCABQv///////////wCDIgVCgICAgICAwMA/fSAFQoCAgICAgMC/wAB9VARAIAFCGYinIQQgAFAgAUL///8PgyIFQoCAgAhUIAVCgICACFEbRQRAIARBgYCAgARqIQIMAgsgBEGAgICABGohAiAAIAVCgICACIWEQgBSDQEgAiAEQQFxaiECDAELIABQIAVCgICAgICAwP//AFQgBUKAgICAgIDA//8AURtFBEAgAUIZiKdB////AXFBgICA/gdyIQIMAQtBgICA/AchAiAFQv///////7+/wABWDQBBACECIAVCMIinIgRBkf4ASQ0AIANBEGogACABQv///////z+DQoCAgICAgMAAhCIFIARBgf4AaxBmIAMgACAFQYH/ACAEaxCvASADKQMIIgBCGYinIQIgAykDACADKQMQIAMpAxiEQgBSrYQiBVAgAEL///8PgyIAQoCAgAhUIABCgICACFEbRQRAIAJBAWohAgwBCyAFIABCgICACIWEQgBSDQAgAkEBcSACaiECCyADQSBqJAAgAiABQiCIp0GAgICAeHFyvguNBAIFfwF+AkACQAJAAkACQAJ/IAAoAgQiAiAAKAJoRwRAIAAgAkEBajYCBCACLQAADAELIAAQPwsiAkEraw4DAAEAAQsgAkEtRiEFAn8gACgCBCIDIAAoAmhHBEAgACADQQFqNgIEIAMtAAAMAQsgABA/CyIDQTprIQQgAUUNASAEQXVLDQEgACkDcEIAUw0CIAAgACgCBEEBazYCBAwCCyACQTprIQQgAiEDCyAEQXZJDQACQCADQTBrQQpPDQBBACECA0AgAyACQQpsaiEGAn8gACgCBCICIAAoAmhHBEAgACACQQFqNgIEIAItAAAMAQsgABA/CyEDIAZBMGshAiACQcyZs+YASCADQTBr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IwNBHGpBxAA2AgALIARB8AJqIBcgFiAPEO0DIAQpA/gCIRUgBCkD8AILIRYgDiAVNwMoIA4gFjcDICAEQZDGAGokACAOKQMoIRUgDikDICEWDAQLIAEpA3BCAFkEQCABIAEoAgRBAWs2AgQLDAELAkACfyABKAIEIgIgASgCaEcEQCABIAJBAWo2AgQgAi0AAAwBCyABED8LQShGBEBBASEGDAELQoCAgICAgOD//wAhFSABKQNwQgBTDQMgASABKAIEQQFrNgIEDAMLA0ACfyABKAIEIgIgASgCaEcEQCABIAJBAWo2AgQgAi0AAAwBCyABED8LIglBwQBrIQICQAJAIAlBMGtBCkkNACACQRpJDQAgCUHfAEYNACAJQeEAa0EaTw0BCyAGQQFqIQYMAQsLQoCAgICAgOD//wAhFSAJQSlGDQIgASkDcCIYQgBZBEAgASABKAIEQQFrNgIECwJAIAMEQCAGDQEMBAsMAQsDQCAYQgBZBEAgASABKAIEQQFrNgIECyAGQQFrIgYNAAsMAgsjA0EcakEcNgIAIAFCABCAAQtCACEVCyAAIBY3AwAgACAVNwMIIA5BMGokAAvKBgIFfwR+IwBBgAFrIgUkAAJAAkACQCADIARCAEIAEK4BRQ0AAn8gBEL///////8/gyELAn8gBEIwiKdB//8BcSIGQf//AUcEQEEEIAYNARpBAkEDIAMgC4RQGwwCCyADIAuEUAsLIQkgAkIwiKciCEH//wFxIgdB//8BRg0AIAkNAQsgBUEQaiABIAIgAyAEEEsgBSAFKQMQIgIgBSkDGCIBIAIgARDsAyAFKQMIIQIgBSkDACEEDAELIAEgAkL///////////8AgyILIAMgBEL///////////8AgyIKEK4BQQBMBEAgASALIAMgChCuAQRAIAEhBAwCCyAFQfAAaiABIAJCAEIAEEsgBSkDeCECIAUpA3AhBAwBCyAEQjCIp0H//wFxIQYgBwR+IAEFIAVB4ABqIAEgC0IAQoCAgICAgMC7wAAQSyAFKQNoIgtCMIinQfgAayEHIAUpA2ALIQQgBkUEQCAFQdAAaiADIApCAEKAgICAgIDAu8AAEEsgBSkDWCIKQjCIp0H4AGshBiAFKQNQIQMLIApC////////P4NCgICAgICAwACEIQwgC0L///////8/g0KAgICAgIDAAIQhCyAGIAdIBEADQAJ+IAsgDH0gAyAEVq19IgpCAFkEQCAKIAQgA30iBIRQBEAgBUEgaiABIAJCAEIAEEsgBSkDKCECIAUpAyAhBAwFCyAKQgGGIARCP4iEDAELIAtCAYYgBEI/iIQLIQsgBEIBhiEEIAdBAWsiByAGSg0ACyAGIQcLAkAgCyAMfSADIARWrX0iCkIAUwRAIAshCgwBCyAKIAQgA30iBIRCAFINACAFQTBqIAEgAkIAQgAQSyAFKQM4IQIgBSkDMCEEDAELIApC////////P1gEQANAIARCP4ghDSAHQQFrIQcgBEIBhiEEIA0gCkIBhoQiCkKAgICAgIDAAFQNAAsLIAhBgIACcSEGIAdBAEwEQCAFQUBrIAQgCkL///////8/gyAHQfgAaiAGcq1CMIaEQgBCgICAgICAwMM/EEsgBSkDSCECIAUpA0AhBAwBCyAKQv///////z+DIAYgB3KtQjCGhCECCyAAIAQ3AwAgACACNwMIIAVBgAFqJAALqg8CBX8PfiMAQdACayIFJAAgBEL///////8/gyELIAJC////////P4MhCiACIASFQoCAgICAgICAgH+DIQ0gBEIwiKdB//8BcSEIAkACQCACQjCIp0H//wFxIglB//8Ba0GCgH5PBEAgCEH//wFrQYGAfksNAQsgAVAgAkL///////////8AgyIMQoCAgICAgMD//wBUIAxCgICAgICAwP//AFEbRQRAIAJCgICAgICAIIQhDQwCCyADUCAEQv///////////wCDIgJCgICAgICAwP//AFQgAkKAgICAgIDA//8AURtFBEAgBEKAgICAgIAghCENIAMhAQwCCyABIAxCgICAgICAwP//AIWEUARAIAMgAkKAgICAgIDA//8AhYRQBEBCACEBQoCAgICAgOD//wAhDQwDCyANQoCAgICAgMD//wCEIQ1CACEBDAILIAMgAkKAgICAgIDA//8AhYRQBEBCACEBDAILIAEgDIRQBEBCgICAgICA4P//ACANIAIgA4RQGyENQgAhAQwCCyACIAOEUARAIA1CgICAgICAwP//AIQhDUIAIQEMAgsgDEL///////8/WARAIAVBwAJqIAEgCiABIAogClAiBht5IAZBBnStfKciBkEPaxBmQRAgBmshBiAFKQPIAiEKIAUpA8ACIQELIAJC////////P1YNACAFQbACaiADIAsgAyALIAtQIgcbeSAHQQZ0rXynIgdBD2sQZiAGIAdqQRBrIQYgBSkDuAIhCyAFKQOwAiEDCyAFQaACaiALQoCAgICAgMAAhCISQg+GIANCMYiEIgJCAEKAgICAsOa8gvUAIAJ9IgRCABBgIAVBkAJqQgAgBSkDqAJ9QgAgBEIAEGAgBUGAAmogBSkDmAJCAYYgBSkDkAJCP4iEIgRCACACQgAQYCAFQfABaiAEQgBCACAFKQOIAn1CABBgIAVB4AFqIAUpA/gBQgGGIAUpA/ABQj+IhCIEQgAgAkIAEGAgBUHQAWogBEIAQgAgBSkD6AF9QgAQYCAFQcABaiAFKQPYAUIBhiAFKQPQAUI/iIQiBEIAIAJCABBgIAVBsAFqIARCAEIAIAUpA8gBfUIAEGAgBUGgAWogAkIAIAUpA7gBQgGGIAUpA7ABQj+IhEIBfSICQgAQYCAFQZABaiADQg+GQgAgAkIAEGAgBUHwAGogAkIAQgAgBSkDqAEgBSkDoAEiDCAFKQOYAXwiBCAMVK18IARCAVatfH1CABBgIAVBgAFqQgEgBH1CACACQgAQYCAGIAkgCGtqIQYCfyAFKQNwIhNCAYYiDiAFKQOIASIPQgGGIAUpA4ABQj+IhHwiEELn7AB9IhRCIIgiAiAKQoCAgICAgMAAhCIVQgGGIhZCIIgiBH4iESABQgGGIgxCIIgiCyAQIBRWrSAOIBBWrSAFKQN4QgGGIBNCP4iEIA9CP4h8fHxCAX0iE0IgiCIQfnwiDiARVK0gDiAOIBNC/////w+DIhMgAUI/iCIXIApCAYaEQv////8PgyIKfnwiDlatfCAEIBB+fCAEIBN+IhEgCiAQfnwiDyARVK1CIIYgD0IgiIR8IA4gDiAPQiCGfCIOVq18IA4gDiAUQv////8PgyIUIAp+IhEgAiALfnwiDyARVK0gDyAPIBMgDEL+////D4MiEX58Ig9WrXx8Ig5WrXwgDiAEIBR+IhggECARfnwiBCACIAp+fCIKIAsgE358IhBCIIggCiAQVq0gBCAYVK0gBCAKVq18fEIghoR8IgQgDlStfCAEIA8gAiARfiICIAsgFH58IgtCIIggAiALVq1CIIaEfCICIA9UrSACIBBCIIZ8IAJUrXx8IgIgBFStfCIEQv////////8AWARAIBYgF4QhFSAFQdAAaiACIAQgAyASEGAgAUIxhiAFKQNYfSAFKQNQIgFCAFKtfSEKQgAgAX0hCyAGQf7/AGoMAQsgBUHgAGogBEI/hiACQgGIhCICIARCAYgiBCADIBIQYCABQjCGIAUpA2h9IAUpA2AiDEIAUq19IQpCACAMfSELIAEhDCAGQf//AGoLIgZB//8BTgRAIA1CgICAgICAwP//AIQhDUIAIQEMAQsCfiAGQQBKBEAgCkIBhiALQj+IhCEKIARC////////P4MgBq1CMIaEIQwgC0IBhgwBCyAGQY9/TARAQgAhAQwCCyAFQUBrIAIgBEEBIAZrEK8BIAVBMGogDCAVIAZB8ABqEGYgBUEgaiADIBIgBSkDQCICIAUpA0giDBBgIAUpAzggBSkDKEIBhiAFKQMgIgFCP4iEfSAFKQMwIgQgAUIBhiIBVK19IQogBCABfQshBCAFQRBqIAMgEkIDQgAQYCAFIAMgEkIFQgAQYCAMIAIgAiADIAJCAYMiASAEfCIDVCAKIAEgA1atfCIBIBJWIAEgElEbrXwiAlatfCIEIAIgAiAEQoCAgICAgMD//wBUIAMgBSkDEFYgASAFKQMYIgRWIAEgBFEbca18IgJWrXwiBCACIARCgICAgICAwP//AFQgAyAFKQMAViABIAUpAwgiA1YgASADURtxrXwiASACVK18IA2EIQ0LIAAgATcDACAAIA03AwggBUHQAmokAAu/AgEBfyMAQdAAayIEJAACQCADQYCAAU4EQCAEQSBqIAEgAkIAQoCAgICAgID//wAQSyAEKQMoIQIgBCkDICEBIANB//8BSQRAIANB//8AayEDDAILIARBEGogASACQgBCgICAgICAgP//ABBLQf3/AiADIANB/f8CThtB/v8BayEDIAQpAxghAiAEKQMQIQEMAQsgA0GBgH9KDQAgBEFAayABIAJCAEKAgICAgICAORBLIAQpA0ghAiAEKQNAIQEgA0H0gH5LBEAgA0GN/wBqIQMMAQsgBEEwaiABIAJCAEKAgICAgICAORBLQeiBfSADIANB6IF9TBtBmv4BaiEDIAQpAzghAiAEKQMwIQELIAQgASACQgAgA0H//wBqrUIwhhBLIAAgBCkDCDcDCCAAIAQpAwA3AwAgBEHQAGokAAs1ACAAIAE3AwAgACACQv///////z+DIARCMIinQYCAAnEgAkIwiKdB//8BcXKtQjCGhDcDCAvAAQIBfwJ+QX8hAwJAIABCAFIgAUL///////////8AgyIEQoCAgICAgMD//wBWIARCgICAgICAwP//AFEbDQAgAkL///////////8AgyIFQoCAgICAgMD//wBWIAVCgICAgICAwP//AFJxDQAgACAEIAWEhFAEQEEADwsgASACg0IAWQRAIAEgAlIgASACU3ENASAAIAEgAoWEQgBSDwsgAEIAUiABIAJVIAEgAlEbDQAgACABIAKFhEIAUiEDCyADC0sBAn8gACgCACIBBEACfyABKAIMIgIgASgCEEYEQCABIAEoAgAoAiQRAAAMAQsgAigCAAtBf0cEQCAAKAIARQ8LIABBADYCAAtBAQtLAQJ/IAAoAgAiAQRAAn8gASgCDCICIAEoAhBGBEAgASABKAIAKAIkEQAADAELIAItAAALQX9HBEAgACgCAEUPCyAAQQA2AgALQQELpAEBA38gAUEISwRAQQQgASABQQRNGyEBQQEgACAAQQFNGyEAA0ACQCAAIAFqQQFrQQAgAWtxIgIgACAAIAJJGyEDQQAhBCMAQRBrIgIkAAJAIAFBA3ENACADIAFwDQAgAkEMaiABIAMQgwQhA0EAIAIoAgwgAxshBAsgAkEQaiQAIAQiAg0AQayZNf4QAgAiA0UNACADEQkADAELCyACDwsgABAyCwkAIAFBARDyAwsTACABQQhLBEAgABAvDwsgABAvC4YBAQN/IwBBEGsiBCQAIwBBIGsiAyQAIANBGGogACABEL0CIANBEGogA0EMaiADKAIYIAMoAhwgAhC8AiADIAAgAygCECAAa2o2AgwgAyACIAMoAhQgAmtqNgIIIAQgAygCDDYCCCAEIAMoAgg2AgwgA0EgaiQAIAQoAgwhBSAEQRBqJAAgBQsJACAAEL4CEC8LpQEBB38jAEEQayICJAAgACgCQCIBBH8gAkHSATYCBCACQQhqIAEgAkEEahBRIQEgACAAKAIAKAIYEQAAIQQgASgCACEGIAFBADYCACAGEKwEIQUgAEEANgJAIABBAEEAIAAoAgAoAgwRAwAaIAEoAgAhAyABQQA2AgAgAwRAIAMgAUEEaigCABEAABoLQQAgACAEIAVyGwVBAAshByACQRBqJAAgBwsKACAAQaSKNRB5C4YC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KAIANgIADwsgAiACKAIAIgFBBGo2AgAgACABMgEANwMADwsgAiACKAIAIgFBBGo2AgAgACABMwEANwMADwsgAiACKAIAIgFBBGo2AgAgACABMAAANwMADwsgAiACKAIAIgFBBGo2AgAgACABMQAANwMADwsgAiACKAIAQQdqQXhxIgFBCGo2AgAgACABKwMAOQMADwsgACACIAMRAgALDwsgAiACKAIAIgFBBGo2AgAgACABNAIANwMADwsgAiACKAIAIgFBBGo2AgAgACABNQIANwMADwsgAiACKAIAQQdqQXhxIgFBCGo2AgAgACABKQMANwMAC3IBA38gACgCACwAAEEwa0EKTwRAQQAPCwNAIAAoAgAhA0F/IQEgAkHMmbPmAE0EQEF/IAMsAABBMGsiASACQQpsIgJqIAEgAkH/////B3NKGyEBCyAAIANBAWo2AgAgASECIAMsAAFBMGtBCkkNAAsgAguZEwIWfwF+IwBB0ABrIggkACAIIAE2AkwgCEE3aiEYIAhBOGohEgJAAkACQANAQQAhBwNAIAEhDSAHIBFB/////wdzSg0CIAcgEWohEQJAAkACQCABIgctAAAiCQRAA0ACQAJAIAlB/wFxIgFFBEAgByEBDAELIAFBJUcNASAHIQkDQCAJLQABQSVHBEAgCSEBDAILIAdBAWohByAJLQACIRogCUECaiIBIQkgGkElRg0ACwsgByANayIHIBFB/////wdzIhlKDQggAARAIAAgDSAHEGELIAcNBiAIIAE2AkwgAUEBaiEHQX8hDgJAIAEsAAFBMGsiC0EKTw0AIAEtAAJBJEcNACABQQNqIQcgCyEOQQEhEwsgCCAHNgJMQQAhDAJAIAcsAAAiCUEgayIBQR9LBEAgByELDAELIAchC0EBIAF0IgFBidEEcUUNAANAIAggB0EBaiILNgJMIAEgDHIhDCAHLAABIglBIGsiAUEgTw0BIAshB0EBIAF0IgFBidEEcQ0ACwsCQCAJQSpGBEAgC0EBaiEJAn8CQCALLAABQTBrQQpPDQAgCy0AAkEkRw0AIAksAABBMGshASALQQNqIQlBASETAn8gAEUEQCAEIAFBAnRqQQo2AgBBAAwBCyADIAFBA3RqKAIACwwBCyATDQYgAEUEQCAIIAk2AkxBACETQQAhDwwDCyACIAIoAgAiAUEEajYC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BQUAAQIDBAsgAUEQayIBKwMAIAArAwBkRQ0EIAIgAP0AAwD9CwMAIAAgASgCCDYCCCAAIAEpAwA3AwAgASACKAIINgIIIAEgAikDADcDAAwECyAAQRBqIQMgAUEQayIBKwMAIQkgACsDECIKIAArAwBkRQRAIAkgCmRFDQQgAiAD/QADAP0LAwAgAyABKAIINgIIIAMgASkDADcDACABIAIoAgg2AgggASACKQMANwMAIAArAxAgACsDAGRFDQQgAiAA/QADAP0LAwAgACADKAIINgIIIAAgAykDADcDACADIAIoAgg2AgggAyACKQMANwMADAQLIAkgCmQEQCACIAD9AAMA/QsDACAAIAEoAgg2AgggACABKQMANwMAIAEgAigCCDYCCCABIAIpAwA3AwAMBAsgAiAA/QADAP0LAwAgACADKAIINgIIIAAgAykDADcDACADIAIoAgg2AgggAyACKQMANwMAIAErAwAgACsDEGRFDQMgAiAD/QADAP0LAwAgAyABKAIINgIIIAMgASkDADcDACABIAIoAgg2AgggASACKQMANwMADAMLIAAgAEEQaiAAQSBqIAFBEGsQ2AEaDAILIAAgAEEQaiIEIABBIGoiAyAAQTBqIgUQ2AEaIAFBEGsiASsDACAAKwMwZEUNASACIAX9AAMA/QsDACAFIAEoAgg2AgggBSABKQMANwMAIAEgAigCCDYCCCABIAIpAwA3AwAgBSsDACADKwMAZEUNASACIAP9AAMA/QsDACADIAUoAgg2AgggAyAFKQMANwMAIAUgAigCCDYCCCAFIAIpAwA3AwAgAysDACAEKwMAZEUNASACIAT9AAMA/QsDACAEIAMoAgg2AgggBCADKQMANwMAIAMgAigCCDYCCCADIAIpAwA3AwAgACsDECAAKwMAZEUNASACIAD9AAMA/QsDACAAIAQoAgg2AgggACAEKQMANwMAIAQgAigCCDYCCCAEIAIpAwA3AwAMAQsgAEEQaiEEIABBIGohAyAAKwMgIQkCQCAAKwMQIgogACsDACILZEUEQCAJIApkRQ0BIAIgBP0AAwD9CwMAIAQgA0EIaigCADYCCCAEIAMpAwA3AwAgAyACKAIINgIIIAMgAikDADcDACAEKwMAIAtkRQ0BIAIgAP0AAwD9CwMAIAAgBCgCCDYCCCAAIAQpAwA3AwAg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IABBhBlqIABBlBJqKAIANgIAIABB/BhqIAApAowSNwIAIABBADoA/hEgAEHpyAE7AfwRIABBAjoAhxIgAEH4EWoiAkHaGC8AADsBACAAQYAUOwH6ESAAQRA2AuwRIABB0hgpAAAiEzcC8BEgAEGQGWogAEGEEmooAgA2AgAgACAAKQL8ETcDiBkgAEGUGWpBEDYCACAAQaAZaiACKAIANgIAIABBmBlqIBM3AwAgAEEAOgDiESAAQejSATsB4BEgAEECOgDrESAAQdgRakHtGy0AADoAACAAQQU6AN8RIABBETYC0BEgAEEAOgDZESAAQekbKAAANgLUESAAQawZaiAAQegRaigCADYCACAAIAApAuARNwKkGSAAQbAZakERNgIAIABBvBlqIABB3BFqKAIANgIAIABBtBlqIAApAtQRNwIAIABBADoAxhEgAEHm0gE7AcQRIABBAjoAzxEgAEG7EWpBlRwoAAA2AAAgAEEHOgDDESAAQRI2ArQRIABBADoAvxEgAEGSHCgAADYCuBEgAEHIGWogAEHMEWooAgA2AgAgACAAKQLEETcDwBkgAEHMGWpBEjYCACAAQdgZaiAAQcARaigCADYCACAAQdAZaiAAKQK4ETcDACAAQQA6AKoRIABB9tIBOwGoESAAQQI6ALMRIABBpBFqIgJB4CEvAAA7AQAgAEGAFDsBphEgAEETNgKYESAAQdghKQAAIhM3ApwRIABB5BlqIABBsBFqKAIANgIAIAAgACkCqBE3AtwZIABB6BlqQRM2AgAgAEH0GWogAigCADYCACAAQewZaiATNwIAIABBADoAjhEgAEHoygE7AYwRIABBAjoAlxEgAEGEEWpBtwovAAA7AQAgAEEGOgCLESAAQRQ2AvwQIABBADoAhhEgAEGzCigAADYCgBEgAEGAGmogAEGUEWooAgA2AgAgACAAKQKMETcD+BkgAEGEGmpBFDYCACAAQZAaaiAAQYgRaigCADYCACAAQYgaaiAAKQKAETcDACAAQQA6APIQIABB9dYBOwHwECAAQQI6APsQIABB7BBqIgJBjBktAAA6AAAgAEEJOgDvECAAQRU2AuAQIABBADoA7RAgAEGEGSkAACITNwLkECAAQZwaaiAAQfgQaigCADYCACAAIAApAvAQNwKUGiAAQaAaakEVNgIAIABBrBpqIAIoAgA2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IABB4B9qQY4ZKQAAIhM3AwAgAEHYH2ogAEHMC2ooAgA2AgAgAEEJOgDDCyAAQQA6AMELIABB6B9qIAIoAgA2AgAgAEEuNgK0CyAAIBM3ArgLIAAgACkCxAs3A9AfIABBADoAqgsgAEHr3AE7AagLIABBAjoAswsgAEGfC2pBmCkoAAA2AAAgAEEHOgCnCyAAQS82ApgLIABBADoAowsgAEGVKSgAADYCnAsgAEH0H2ogAEGwC2ooAgA2AgAgACAAKQKoCzcC7B8gAEH4H2pBLzYCACAAQYQgaiAAQaQLaigCADYCACAAQfwfaiAAKQKcCzcCACAAQQA6AI4LIABB5egBOwGMCyAAQQI6AJcLIABBiAtqIgJBADoAACAAQQg6AIsLIABBMDYC/AogAELl5tH75q3asO4ANwKACyAAQZAgaiAAQZQLaigCADYCACAAIAApAowLNwOIICAAQZQgakEwNgIAIABBoCBqIAIoAgA2AgAgAEGYIGpC5ebR++at2rDuADcDACAAQQA6APIKIABB7dYBOwHwCiAAQQI6APsKIABB7ApqIgJBgRkvAAA7AQAgAEEKOgDvCiAAQTE2AuAKIABBADoA7gogAEH5GCkAACITNwLkCiAAQawgaiAAQfgKaigCADYCACAAIAApAvAKNwKkICAAQbAgakExNgIAIABBvCBqIAIoAgA2AgAgAEG0IGogEzcCACAAQQA6ANYKIABB4uQBOwHUCiAAQQI6AN8KIABBzApqQbkWLwAAOwEAIABBBjoA0wogAEEyNgLECiAAQQA6AM4KIABBtRYoAAA2AsgKIABByCBqIABB3ApqKAIANgIAIAAgACkC1Ao3A8AgIABBzCBqQTI2AgAgAEHYIGogAEHQCmooAgA2AgAgAEHQIGogACkCyAo3AwAgAEEAOgC6CiAAQeXqATsBuAogAEECOgDDCiAAQbAKakGhHy8AADsBACAAQQY6ALcKIABBMzYCqAogAEEAOgCyCiAAQZ0fKAAANgKsCiAAQeQgaiAAQcAKaigCADYCACAAIAApArgKNwLcICAAQeggakEzNgIAIABB9CBqIABBtApqKAIANgIAIABB7CBqIAApAqwKNwIAIABBADoAngogAEHp5gE7AZwKIABBAjoApwogAEGYCmoiAkHXJi0AADoAACAAQQk6AJsKIABBNDYCjAogAEEAOgCZCiAAQc8mKQAAIhM3ApAKIABBgCFqIABBpApqKAIANgIAIAAgACkCnAo3A/ggIABBhCFqQTQ2AgAgAEGQIWogAigCADYCACAAQYghaiATNwMAIABBADoAggogAEHo8gE7AYAKIABBAjoAiwogAEH8CWoiAkEAOgAAIABBCDoA/wkgAEE1NgLwCSAAQuHktavmrdqw7gA3AvQJIABBnCFqIABBiApqKAIANgIAIAAgACkCgAo3ApQhIABBoCFqQTU2AgAgAEGsIWogAigCADYCACAAQaQhakLh5LWr5q3asO4ANwIAIABBADoA5gkgAEHuygE7AeQJIABBAjoA7wkgAEHcCWpByBsvAAA7AQAgAEEGOgDjCSAAQTY2AtQJIABBADoA3gkgAEHEGygAADYC2AkgAEG4IWogAEHsCWooAgA2AgAgACAAKQLkCTcDsCEgAEG8IWpBNjYCACAAQcghaiAAQeAJaigCADYCACAAQcAhaiAAKQLYCTcDACAAQQA6AMoJIABB7dwBOwHICSAAQQI6ANMJIABBxAlqIgJBqxktAAA6AAAgAEEJOgDHCSAAQTc2ArgJIABBADoAxQkgAEGjGSkAACITNwK8CSAAQdQhaiAAQdAJaigCADYCACAAIAApAsgJNwLMISAAQdghakE3NgIAIABB5CFqIAIoAgA2AgAgAEHcIWogEzcCACAAQQA6AK4JIABB4uYBOwGsCSAAQQI6ALcJIABBowlqQfQYKAAANgAAIABBBzoAqwkgAEE4NgKcCSAAQQA6AKcJIABB8RgoAAA2AqAJIABB8CFqIABBtAlqKAIANgIAIAAgACkCrAk3A+ghIABB9CFqQTg2AgAgAEGAImogAEGoCWooAgA2AgAgAEH4IWogACkCoAk3AwAgAEEAOgCSCSAAQevWATsBkAkgAEECOgCbCSAAQYgJakHyHC8AADsBACAAQQY6AI8JIABBOTYCgAkgAEEAOgCKCSAAQe4cKAAANgKECSAAQYwiaiAAQZgJaigCADYCACAAIAApApAJNwKEIiAAQZAiakE5NgIAIABBnCJqIABBjAlqKAIANgIAIABBlCJqIAApAoQJNwIAIABBADoA9gggAEHz4gE7AfQIIABBAjoA/wggAEHwCGoiAkEAOgAAIABBCDoA8wggAEE6NgLkCCAAQuHYiYvmrdqw7gA3AugIIABBqCJqIABB/AhqKAIANgIAIAAgACkC9Ag3A6AiIABBrCJqQTo2AgAgAEG4ImogAigCADYCACAAQbAiakLh2ImL5q3asO4ANwMAIABBADoA2gggAEHz7gE7AdgIIABBAjoA4wggAEHPCGpBvxsoAAA2AAAgAEEHOgDXCCAAQTs2AsgIIABBADoA0wggAEG8GygAADYCzAggAEHEImogAEHgCGooAgA2AgAgACAAKQLYCDcCvCIgAEHIImpBOzYCACAAQdQiaiAAQdQIaigCADYCACAAQcwiaiAAKQLMCDcCACAAQQA6AL4IIABB59gBOwG8CCAAQQI6AMcIIABBuAhqIgJBADoAACAAQQg6ALsIIABBPDYCrAggAELnwrHLtqzasO4ANwKwCCAAQeAiaiAAQcQIaigCADYCACAAIAApArwINwPYIiAAQeQiakE8NgIAIABB8CJqIAIoAgA2AgAgAEHoImpC58Kxy7as2rDuADcDACAAQQA6AKIIIABB7eQBOwGgCCAAQQI6AKsIIABBlwhqQd0bKAAANgAAIABBBzoAnwggAEE9NgKQCCAAQQA6AJsIIABB2hsoAAA2ApQIIABB/CJqIABBqAhqKAIANgIAIAAgACkCoAg3AvQiIABBgCNqQT02AgAgAEGMI2ogAEGcCGooAgA2AgAgAEGEI2ogACkClAg3AgAgAEEAOgCGCCAAQfDCATsBhAggAEECOgCPCCAAQfIbKAAANgD7ByAAQQc6AIMIIABBPjYC9AcgAEEAOgD/ByAAQe8bKAAANgL4ByAAQZgjaiAAQYwIaigCADYCACAAIAApAoQINwOQIyAAQZwjakE+NgIAIABBqCNqIABBgAhqKAIANgIAIABBoCNqIAApAvgHNwMAIABBADoA6gcgAEHz0gE7AegHIABBAjoA8wcgAEGCKSgAADYA3wcgAEEHOgDnByAAQT82AtgHIABBADoA4wcgAEH/KCgAADYC3AcgAEG0I2ogACgC8Ac2AgAgACAAKQLoBzcCrCMgAEG4I2pBPzYCACAAQcQjaiAAKALkBzYCACAAQbwjaiAAKQLcBzcCACAAQQA6AM4HIABB69oBOwHMByAAQQI6ANcHIABBxxQtAAA6AMQHIABBBToAywcgAEHAADYCvAcgAEEAOgDFByAAQcMUKAAANgLAByAAQdAj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OwGUAiAAQQI6AJ8CIABBADoAkAIgAEEIOgCTAiAAQdoANgKEAiAAQu3CsYv2rNi5+QA3AogCIABBqClqIAAoApwCNgIAIAAgACkClAI3A6ApIABBrClqQdoANgIAIABBuClqIAAoApACNgIAIABBsClqQu3CsYv2rNi5+QA3AwAgAEEAOgD6ASAAQeHmATsB+AEgAEECOgCDAiAAQQA6APQBIABBCDoA9wEgAEHbADYC6AEgAELh5s2L1q3ZueUANwLsASAAQcQpaiAAKAKAAjYCACAAIAApAvgBNwK8KSAAQcgpakHbADYCACAAQdQpaiAAKAL0ATYCACAAQcwpakLh5s2L1q3ZueUANwIAIABBADoA3gEgAEH06AE7AdwBIABBAjoA5wEgAEH4FC0AADoA1AEgAEEFOgDbASAAQdwANgLMASAAQQA6ANUBIABB9BQoAAA2AtABIABB4ClqIAAoAuQBNgIAIAAgACkC3AE3A9gpIABB5ClqQdwANgIAIABB8ClqIAAoAtgBNgIAIABB6ClqIAApAtABNwMAIABBAzoAywEgAEEAOgDDASAAQbwKLQAAOgDCASAAQboKLwAAOwHAASAAQQA6ALwBIABBCDoAvwEgAEHdADYCsAEgAELowt2Llq3asO4ANwK0ASAAQfwpaiAAKALIATYCACAAIAApAsABNwL0KSAAQYAqakHdADYCACAAQYwqaiAAKAK8ATYCACAAQYQqakLowt2Llq3asO4ANwIAIABBADoApgEgAEHs3AE7AaQBIABBAjoArwEgAEGKKSgAADYAmwEgAEEHOgCjASAAQd4ANgKUASAAQQA6AJ8BIABBhykoAAA2ApgBIABBmCpqIAAoAqwBNgIAIAAgACkCpAE3A5AqIABBnCpqQd4ANgIAIABBqCpqIAAoAqABNgIAIABBoCpqIAApApgBNwMAIABBADoAigEgAEHowgE7AYgBIABBAjoAkwEgAEHxKC0AADoAgAEgAEEFOgCHASAAQd8ANgJ4IABBADoAgQEgAEHtKCgAADYCfCAAQbQqaiAAKAKQATYCACAAIAApAogBNwKsKiAAQbgqakHfADYCACAAQcQqaiAAKAKEATYCACAAQbwqaiAAKQJ8NwIAIABBADoAbiAAQeLCATsBbCAAQQI6AHcgAEGyFCgAADYAYyAAQQc6AGsgAEHg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IAFBADYCCEHQ1zQgACgCICAAQSBqIAAtACsiAcBBAEgiBBsgACgCJCABIAQbEF8aIAAsACtBAEgEQCAAKAIgEC8LIAAsABtBAEgEQCAAKAIQEC8LIAAsAA9BAEgEQCAAKAIEEC8LIABBBGoiAUEAEDkgACABQdjhABA4IgEoAgg2AhggACABKQIANwMQIAFCADcCACABQQA2AgggACAAQRBqQfPgABA3IgEoAgg2AiggACABKQIANwMgIAFCADcCACABQQA2AghB0Nc0IAAoAiAgAEEgaiAALQArIgHAQQBIIgQbIAAoAiQgASAEGxBfGiAALAArQQBIBEAgACgCIBAvCyAALAAbQQBIBEAgACgCEBAvCyAALAAPQQBIBEAgACgCBBAvCyAAQQRqIgFBABA5IAAgAUGZ4QAQOCIBKAIINgIYIAAgASkCADcDECABQgA3AgAgAUEANgIIIAAgAEEQakHz4AAQNyIBKAIINgIoIAAgASkCADcDICABQgA3AgAgAUEANgIIQdDXNCAAKAIgIABBIGogAC0AKyIBwEEASCIEGyAAKAIkIAEgBBsQXxogACwAK0EASARAIAAoAiAQLwsgACwAG0EASARAIAAoAhAQLwsgACwAD0EASARAIAAoAgQQLwsgAEEEaiIBQQAQOSAAIAFB1OEAEDgiASgCCDYCGCAAIAEpAgA3AxAgAUIANwIAIAFBADYCCCAAIABBEGpB8+AAEDciASgCCDYCKCAAIAEpAgA3AyAgAUIANwIAIAFBADYCCEHQ1zQgACgCICAAQSBqIAAtACsiAcBBAEgiBBsgACgCJCABIAQbEF8aIAAsACtBAEgEQCAAKAIgEC8LIAAsABtBAEgEQCAAKAIQEC8LIAAsAA9BAEgEQCAAKAIEEC8LIABBBGoiAUEAEDkgACABQavhABA4IgEoAgg2AhggACABKQIANwMQIAFCADcCACABQQA2AgggACAAQRBqQfPgABA3IgEoAgg2AiggACABKQIANwMgIAFCADcCACABQQA2AghB0Nc0IAAoAiAgAEEgaiAALQArIgHAQQBIIgQbIAAoAiQgASAEGxBfGiAALAArQQBIBEAgACgCIBAvCyAALAAbQQBIBEAgACgCEBAvCyAALAAPQQBIBEAgACgCBBAvCyAAQQRqIgFBABA5IAAgAUHB4QAQOCIBKAIINgIYIAAgASkCADcDECABQgA3AgAgAUEANgIIIAAgAEEQakHz4AAQNyIBKAIINgIoIAAgASkCADcDICABQgA3AgAgAUEANgIIQdDXNCAAKAIgIABBIGogAC0AKyIBwEEASCIEGyAAKAIkIAEgBBsQXxogACwAK0EASARAIAAoAiAQLwsgACwAG0EASARAIAAoAhAQLwsgACwAD0EASARAIAAoAgQQLwsgAEEEaiIBQQAQOSAAIAFByeEAEDgiASgCCDYCGCAAIAEpAgA3AxAgAUIANwIAIAFBADYCCCAAIABBEGpB8+AAEDciASgCCDYCKCAAIAEpAgA3AyAgAUIANwIAIAFBADYCCEHQ1zQgACgCICAAQSBqIAAtACsiAcBBAEgiBBsgACgCJCABIAQbEF8aIAAsACtBAEgEQCAAKAIgEC8LIAAsABtBAEgEQCAAKAIQEC8LIAAsAA9BAEgEQCAAKAIEEC8LIABBBGoiAUEBEDkgACABQbThABA4IgEoAgg2AhggACABKQIANwMQIAFCADcCACABQQA2AgggACAAQRBqQfPgABA3IgEoAgg2AiggACABKQIANwMgIAFCADcCACABQQA2AghB0Nc0IAAoAiAgAEEgaiAALQArIgHAQQBIIgQbIAAoAiQgASAEGxBfGiAALAArQQBIBEAgACgCIBAvCyAALAAbQQBIBEAgACgCEBAvCyAALAAPQQBIBEAgACgCBBAvCyAAQQRqIgFBABA5IAAgAUGF4QAQOCIBKAIINgIYIAAgASkCADcDECABQgA3AgAgAUEANgIIIAAgAEEQakHz4AAQNyIBKAIINgIoIAAgASkCADcDICABQgA3AgAgAUEANgIIQdDXNCAAKAIgIABBIGogAC0AKyIBwEEASCIEGyAAKAIkIAEgBBsQXxogACwAK0EASARAIAAoAiAQLwsgACwAG0EASARAIAAoAhAQLwsgACwAD0EASARAIAAoAgQQLwsgAEEEaiIBQQAQOSAAIAFB6OEAEDgiASgCCDYCGCAAIAEpAgA3AxAgAUIANwIAIAFBADYCCCAAIABBEGpB8+AAEDciASgCCDYCKCAAIAEpAgA3AyAgAUIANwIAIAFBADYCCEHQ1zQgACgCICAAQSBqIAAtACsiAcBBAEgiBBsgACgCJCABIAQbEF8aIAAsACtBAEgEQCAAKAIgEC8LIAAsABtBAEgEQCAAKAIQEC8LIAAsAA9BAEgEQCAAKAIEEC8LIABBBGoiAUEAEDkgACABQefhABA4IgEoAgg2AhggACABKQIANwMQIAFCADcCACABQQA2AgggACAAQRBqQfPgABA3IgEoAgg2AiggACABKQIANwMgIAFCADcCACABQQA2AghB0Nc0IAAoAiAgAEEgaiAALQArIgHAQQBIIgQbIAAoAiQgASAEGxBfGiAALAArQQBIBEAgACgCIBAvCyAALAAbQQBIBEAgACgCEBAvCyAALAAPQQBIBEAgACgCBBAvCyAAQQRqIgFBABA5IAAgAUH+4AAQOCIBKAIINgIYIAAgASkCADcDECABQgA3AgAgAUEANgIIIAAgAEEQakHz4AAQNyIBKAIINgIoIAAgASkCADcDICABQgA3AgAgAUEANgIIQdDXNCAAKAIgIABBIGogAC0AKyIBwEEASCIEGyAAKAIkIAEgBBsQXxogACwAK0EASARAIAAoAiAQLwsgACwAG0EASARAIAAoAhAQLwsgACwAD0EASARAIAAoAgQQLwsgAEEEaiIBQQAQOSAAIAFB3+EAEDgiASgCCDYCGCAAIAEpAgA3AxAgAUIANwIAIAFBADYCCCAAIABBEGpB8+AAEDciASgCCDYCKCAAIAEpAgA3AyAgAUIANwIAIAFBADYCCEHQ1zQgACgCICAAQSBqIAAtACsiAcBBAEgiBBsgACgCJCABIAQbEF8aIAAsACtBAEgEQCAAKAIgEC8LIAAsABtBAEgEQCAAKAIQEC8LIAAsAA9BAEgEQCAAKAIEEC8LIABBBGoiAUEAEDkgACABQaHhABA4IgEoAgg2AhggACABKQIANwMQIAFCADcCACABQQA2AgggACAAQRBqQfPgABA3IgEoAgg2AiggACABKQIANwMgIAFCADcCACABQQA2AghB0Nc0IAAoAiAgAEEgaiAALQArIgHAQQBIIgQbIAAoAiQgASAEGxBfGiAALAArQQBIBEAgACgCIBAvCyAALAAbQQBIBEAgACgCEBAvCyAALAAPQQBIBEAgACgCBBAvCyAAQQRqIgFBABA5IAAgAUGN4QAQOCIBKAIINgIYIAAgASkCADcDECABQgA3AgAgAUEANgIIIAAgAEEQakHz4AAQNyIBKAIINgIoIAAgASkCADcDICABQgA3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QTBqIQEgAkEwaiICIA1HDQALCyADIAE2AgQMAgsgASACIBH8CgAAIAMgASAMQTBsajYCBAwBCyABBEAgAyABNgIEIAEQLyADQQA2AgggA0IANwIAQQAhBwsgDEHWqtUqTw0CQdWq1SogB0EwbSIBQQF0IgcgDCAHIAxLGyABQarVqhVPGyIBQdaq1SpPDQIgAyABQTBsIgcQMiIBNgIEIAMgATYCACADIAEgB2o2AgggAyACIA1HBH8gASACIBFBMGsiAiACQTBwa0EwaiIC/AoAACABIAJqBSABCzYCBAsgAyAEKAJENgI0IAMgBCkCPDcCLCADIAT9AAIs/QsCHCADIAT9AAIc/QsCDCAIQYgCaiIBIARByABqRwRAIAEgBCgCSCAEKAJMENQEIAhBlAJqIAQoAlQgBCgCWBDTBAsgCCAEKQJgNwKgAiAEKAIAIRQgBSgCnBoiCEEANgJMIAgoAlAiEUUNACAGQQhqIQ1BACEMA0AgCCgCWCAMQQR0aiIEQQhqIgMhAiADKAIAIgchAQJAIAdFDQADQCACIAEgASgCECAUSCIPGyECIAFBBGogASAPGygCACIBDQALIAIgA0YNACACKAIQIBRKDQAgBCgCAEH+////B0sNAAJ/A0AgDSAHIgEoAhAiAkgEQCABKAIAIgcNASABDAILIAIgDU4NAiABKAIEIgcNAAsgAUEEagshAkEUEDIiAyABNgIIIANCADcCACADIA02AhAgAiADNgIAIAQoAgQoAgAiAQRAIAQgATYCBCACKAIAIQMLIAMgAyAEKAIIIhFGIgE6AAwCQCABDQADQCADKAIIIgItAAwNAQJAIAIgAigCCCIBKAIAIgdGBEACQCABKAIEIgdFDQAgBy0ADA0ADAILAkAgAyACKAIARgRAIAIhAwwBCyACIAIoAgQiAygCACIHNgIEIAMgBwR/IAcgAjYCCCACKAIIBSABCzYCCCACKAIIIgEgASgCACACR0ECdGogAzYCACADIAI2AgAgAiADNgIIIAMoAggiASgCACECCyADQQE6AAwgAUEAOgAMIAEgAigCBCIDNgIAIAMEQCADIAE2AggLIAIgASgCCDYCCCABKAIIIgMgAygCACABR0ECdGogAjYCACACIAE2AgQgASACNgIIDAMLAkAgB0UNACAHLQAMDQAMAQsCQCADIAIoAgBH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JAAgBSABNgIcAkAgAigCBEEBcUUEQCAAIAEgAiADIAQgACgCACgCGBEKACECDAELIAVBEGoiASACKAIcIgA2AgAgAEEEakEB/h4CABogARDBASEAIAEoAgAiAkEEakF//h4CAEUEQCACIAIoAgAoAggRAQALAkAgBARAIAEgACAAKAIAKAIYEQIADAELIAVBEGogACAAKAIAKAIcEQIACyAFIAVBEGoQdTYCDANAIAUgBUEQaiIAEJ0BNgIIIAUoAgwiASAFKAIIRwRAIAVBHGogASwAABD8AyAFIAUoAgxBAWo2AgwMAQUgBSgCHCECIAAQMxoLCwsgBUEgaiQAIAILwQUBBn8jAEHAAmsiACQAIAAgAjYCuAIgACABNgK8AiMAQRBrIgIkACAAQcQBaiIBQgA3AgAgAUEANgIIIAJBEGokACAAQRBqIgYgAygCHCICNgIAIAJBBGpBAf4eAgAaIAYQbCICQdDqAkHq6gIgAEHQAWogAigCACgCMBEIABogBigCACICQQRqQX/+HgIARQRAIAIgAigCACgCCBEBAAsgASEDIwBBEGsiASQAIABBuAFqIgJCADcCACACQQA2AgggAUEQaiQAIAIgAi0AC0EHdgR/IAIoAghB/////wdxQQFrBUEKCxA6IAACfyACLQALQQd2BEAgAigCAAwBCyACCyIBNgK0ASAAIAY2AgwgAEEANgIIA0ACQCAAQbwCaiAAQbgCahBCDQAgACgCtAECfyACLQALQQd2BEAgAigCBAwBCyACLQALQf8AcQsgAWpGBEACfyACLQALQQd2BEAgAigCBAwBCyACLQALQf8AcQshBiACAn8gAi0AC0EHdgRAIAIoAgQMAQsgAi0AC0H/AHELQQF0EDogAiACLQALQQd2BH8gAigCCEH/////B3FBAWsFQQoLEDogACAGAn8gAi0AC0EHdgRAIAIoAgAMAQsgAgsiAWo2ArQBCwJ/IABBvAJqIgcoAgAiBigCDCIIIAYoAhBGBEAgBiAGKAIAKAIkEQAADAELIAgoAgALQRAgASAAQbQBaiAAQQhqQQAgAyAAQRBqIABBDGogAEHQAWoQvQENACAHEFwaDAELCyACIAAoArQBIAFrEDoCfyACLQALQQd2BEAgAigCAAwBCyACCyELEEQhCSAAIAU2AgAgCyAJIAAQ1ANBAUcEQCAE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zwFB/wBxC0UNACAAKAIMIgMgAEEQamtBnwFKDQAgACADQQRqNgIMIAMgACgCCDYCAAsgBSACIAAoArQBIAQgBhDYAzcDACAAQcQBaiAAQRBqIAAoAgwgBBBkIABBzAJqIABByAJqEEIEQCAEIAQoAgBBAnI2AgALIAAoAswCIQogARAzGiAAQcQBahAzGiAAQdACaiQAIAoLmgUBBX8jAEHQAmsiACQAIAAgAjYCyAIgACABNgLMAiADEIwBIQYgAyAAQdABahDQASEHIABBxAFqIAMgAEHEAmoQzwEjAEEQayICJAAgAEG4AWoiAUIANwIAIAFBADYCCCACQRBqJAAgASABLQALQQd2BH8gASgCCEH/////B3FBAWsFQQoLEDogAAJ/IAEtAAtBB3YEQCABKAIADAELIAELIgI2ArQBIAAgAEEQajYCDCAAQQA2AggDQAJAIABBzAJqIABByAJqEEINACAAKAK0AQJ/IAEtAAtBB3YEQCABKAIEDAELIAEtAAtB/wBxCyACakYEQAJ/IAEtAAtBB3YEQCABKAIEDAELIAEtAAtB/wBxCyEDIAECfyABLQALQQd2BEAgASgCBAwBCyABLQALQf8AcQtBAXQQOiABIAEtAAtBB3YEfyABKAIIQf////8HcUEBawVBCgsQOiAAIAMCfyABLQALQQd2BEAgASgCAAwBCyABCyICajYCtAELAn8gAEHMAmoiCCgCACIDKAIMIgkgAygCEEYEQCADIAMoAgAoAiQRAAAMAQsgCSgCAAsgBiACIABBtAFqIABBCGogACgCxAIgAEHEAWogAEEQaiAAQQxqIAcQvQENACAIEFwaDAELCwJAAn8gAC0AzwFBB3YEQCAAKALIAQwBCyAALQDPAUH/AHELRQ0AIAAoAgwiAyAAQRBqa0GfAUoNACAAIANBBGo2AgwgAyAAKAIINgIACyAFIAIgACgCtAEgBCAGENsDOwEAIABBxAFqIABBEGogACgCDCAEEGQgAEHMAmogAEHIAmoQQgRAIAQgBCgCAEECcjYCAAsgACgCzAIhCiABEDMaIABBxAFqEDMaIABB0AJqJAAgCguaBQEFfyMAQdACayIAJAAgACACNgLIAiAAIAE2AswCIAMQjAEhBiADIABB0AFqENABIQcgAEHEAWogAyAAQcQCahDPASMAQRBrIgIkACAA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AUEQaiQAIAIgAi0AC0EHdgR/IAIoAghB/////wdxQQFrBUEKCxA6IAACfyACLQALQQd2BEAgAigCAAwBCyACCyIBNgK0ASAAIAY2AgwgAEEANgIIA0ACQCAAQfwBaiAAQfgBahBDDQAgACgCtAECfyACLQALQQd2BEAgAigCBAwBCyACLQALQf8AcQsgAWpGBEACfyACLQALQQd2BEAgAigCBAwBCyACLQALQf8AcQshBiACAn8gAi0AC0EHdgRAIAIoAgQMAQsgAi0AC0H/AHELQQF0EDogAiACLQALQQd2BH8gAigCCEH/////B3FBAWsFQQoLEDogACAGAn8gAi0AC0EHdgRAIAIoAgAMAQsgAgsiAWo2ArQBCwJ/IABB/AFqIgcoAgAiBigCDCIIIAYoAhBGBEAgBiAGKAIAKAIkEQAADAELIAgtAAALwEEQIAEgAEG0AWogAEEIakEAIAMgAEEQaiAAQQxqIABB0AFqEMABDQAgBxBdGgwBCwsgAiAAKAK0ASABaxA6An8gAi0AC0EHdgRAIAIoAgAMAQsgAgshCxBEIQkgACAFNgIAIAsgCSAAENQDQQFHBEAgBEEENgIACyAAQfwBaiAAQfgBahBDBEAgBCAEKAIAQQJyNgIACyAAKAL8ASEKIAIQMxogAxAzGiAAQYACaiQAIAoLHgEBf0HQuwMoAgAiAARAIAAQ4ARB0LsDQQA2AgALC9EFAgN/AX4jAEGQAmsiACQAIAAgAjYCiAIgACABNgKMAiAAQdABaiADIABB4AFqIABB3wFqIABB3gFqELYCIwBBEGsiAiQAIABBxAFqIgFCADcCACABQQA2AgggAkEQaiQAIAEgAS0AC0EHdgR/IAEoAghB/////wdxQQFrBUEKCxA6IAACfyABLQALQQd2BEAgASgCAAwBCyABCyICNgLAASAAIABBIGo2AhwgAEEANgIYIABBAToAFyAAQcUAOgAWA0ACQCAAQYwCaiAAQYgCahBDDQAgACgCwAECfyABLQALQQd2BEAgASgCBAwBCyABLQALQf8AcQsgAmpGBEACfyABLQALQQd2BEAgASgCBAwBCyABLQALQf8AcQshAyABAn8gAS0AC0EHdgRAIAEoAgQMAQsgAS0AC0H/AHELQQF0EDogASABLQALQQd2BH8gASgCCEH/////B3FBAWsFQQoL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QQNxbGogACwADiAxIAV2QQNxbGogACwADyAwIAV2QQNxbGogBiAHai0AAEEPcWwgCmogACwAESAuIAV2QQNxbCAALAAQIC8gBXZBA3FsaiAALAASIC0gBXZBA3FsaiAALAATICwgBXZBA3FsaiAALAAUICsgBXZBA3FsaiAALAAVICogBXZBA3FsaiAALAAWICkgBXZBA3FsaiAALAAXICggBXZBA3FsaiAALAAYICcgBXZBA3FsaiAALAAZICYgBXZBA3FsaiAALAAaICUgBXZBA3FsaiAALAAbICQgBXZBA3FsaiAALAAcICMgBXZBA3FsaiAALAAdICIgBXZBA3FsaiAALAAeICEgBXZBA3FsaiAALAAfICAgBXZBA3FsaiAHIAZBAXJqLQAAQQ9xbGohCiAAQSBqIQAgBUECaiEFIAZBAmohBiAMQQFqIgxBBEcNAAsgBEEgaiEEIAlBCGohCSALIQZBACELIEAhACAGDQALIEEgQiBElCAKspQgQyBClCAOIA8gECARIBIgEyAUIBUgFiAXIBggGSAaIBsgHCAdIB5sampqampqampqampqampqspSTkiFBIAhBAWoiCCANRw0ACwsgASBBOAIAC6YCAQF/IAAgACgCACgCGBEAABogACABEPgDIgE2AkQgAC0AYiECIAAgASABKAIAKAIcEQAAIgE6AGIgASACRwRAIABBADYCECAAQQA2AgwgAEEANgIIIABBADYCHCAAQQA2AhQgAEEANgIYIAAtAGAhASAALQBiBEACQCABRQ0AIAAoAiAiAUUNACABEC8LIAAgAC0AYToAYCAAIAAoAjw2AjQgACgCOCEBIABCADcCOCAAIAE2AiAgAEEAOgBhDwsCQCABDQAgACgCICIBIABBLGpGDQAgAEEAOgBhIAAgATYCOCAAIAAoAjQiATYCPCABEDIhASAAQQE6AGAgACABNgIgDwsgACAAKAI0IgE2AjwgARAyIQEgAEEBOgBhIAAgATYCOAsL8QMCBX8BfiMAQRBrIgMkAAJAIAAoAkBFDQACQCAAKAJEIgQEQCAAKAJcIgJBEHEEQCAAKAIYIAAoAhRHBEBBfyEBIABBfyAAKAIAKAI0EQQAQX9GDQQLIABByABqIQEDQCAAKAJEIgQgASAAKAIgIgIgAiAAKAI0aiADQQxqIAQoAgAoAhQRCgAh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AgAhAAsgAygC1AEgABCkASADIAMoAtgBQQFqNgLYAQsCQCAEQRRqIgAgAUHwA2pGDQAgAS0A+wMiCMAhAiAELAAfQQBOBEAgAkEATgRAIAAgASkD8AM3AgAgACABKAL4AzYCCAwCCyAAIAEoAvADIAEoAvQDEN4BDAELIAAgASgC8AMgAUHwA2ogAkEASCIAGyABKAL0AyAIIAAbEN8BCyAGQQFqIgYgASgCyAQiAEgNAAsLIAMgAygCICICNgLAASACQZiVA0oEQCADIAMoAtwBQQFqNgLcASADIAMoAuABQQFqNgLgASADIAP9AALkASACQZiVA2siBP0R/a4B/QsC5AEgAyADKAL0ASAEajYC9AEgAyADKAL4ASAEajYC+AEgAyADKAL8ASAEajYC/AELAkAgACACTg0AIAFByCU2AuABIAEgAiAAazYC5AFBBEGT4wAgAUHgAWoQNCABKALIBCIGIAMoAiBODQAgA0HUAWohBSADQcQBaiELIAFB8ANqQQRyIQgDQAJAIAMoAvwBIgIgBkgEQCABQZQEaiIAIAYgAmsQOSABIABBsykQOCIAKAIINgKoBCABIAApAgA3A6AEIABCADcCACAAQQA2AgggAUGgBGpBuyoQNyIAKAIAIQIgASAAKAIENgLkAyABIAAoAAc2AOcDIABCADcCACAALQALIQQgAEEANgIIIAEsAPsDQQBIBEAgASgC8AMQLwsgASACNgLwAyAIIAEoAOcDNgADIAggASgC5AM2AgAgASAEOgD7AyABLACrBEEASARAIAEoAqAEEC8LIAEsAJ8EQQBODQEgASgClAQQLwwBCyADKALcASAGRgRAAn8gASwA+wNBAEgEQCABQQc2AvQDIAEoAvADDAELIAFBBzoA+wMgAUHwA2oLIgBBADoAByAAQdQpKAAANgADIABB0SkoAAA2AAAMAQsgAygC4AEiACAGRgRAAn8gASwA+wNBAEgEQCABQQc2AvQDIAEoAvADDAELIAFBBzoA+wMgAUHwA2oLIgBBADoAByAAQcQpKAAANgADIABBwSkoAAA2AAAMAQsgAygC5AEgBkYEQCABQfADakHhKUENEMsBDAELIAMoAugBIAZGBEAgAUHwA2pB7ylBDhDLAQwBCyADKALsASAGRgRAAn8gASwA+wNBAEgEQCABQQg2AvQDIAEoAvADDAELIAFBCDoA+wMgAUHwA2oLIgBBADoACCAAQtu+zfrEqdOv3QA3AAAMAQsgAygC8AEgBkYEQAJ/IAEsAPsDQQBIBEAgAUEINgL0AyABKALwAwwBCyABQQg6APsDIAFB8ANqCyIAQQA6AAggAELbvsGS1cjVr90ANwAADAELIAMoAvQBIAZGBEACfyABLAD7A0EASARAIAFBCDYC9AMgASgC8AMMAQsgAUEIOgD7AyABQfADagsiAEEAOgAIIABC2765+rSK1K/dADcAAAwBCyADKAL4ASAGRgRAAn8gASwA+wNBAEgEQCABQQc2AvQDIAEoAvADDAELIAFBBzoA+wMgAUHwA2oLIgBBADoAByAAQcwpKAAANgADIABBySkoAAA2AAAMAQsgAiAGRgRAAn8gASwA+wNBAEgEQCABQQc2AvQDIAEoAvADDAELIAFBBzoA+wMgAUHwA2oLIgBBADoAByAAQdwpKAAANgADIABB2SkoAAA2AAAMAQsCQCAAIAZODQAgBiAAIAMoAsABIgJqIAJBmJUDSmtBtZQDa0oNACAGIABBf3NqIQxBACEEIwBBEGsiCiQAAkACQEHE1zQoAgAiAkHI1zRHBEADQCACKAIcIAxGDQICQCACKAIEIgAEQANAIAAiAigCACIADQAMAgsACwNAIAIgAigCCCICKAIARw0ACwsgAkHI1zRHDQALCyAKIAw2AgQgCkHBEzYCAEECQaDrACAKEDQMAQsgAigCECACQRBqIAIsABtBAEgbIQQLIApBEGokACABIAFBlARqIAQQpgEiAkG5KRA4IgAoAgg2AqgEIAEgACkCADcDoAQgAEIANwIAIABBADYCCCABQaAEakG7KhA3IgAoAgAhBCABIAAoAgQ2AuQDIAEgACgABzYA5wMgAEIANwIAIAAtAAshCiAAQQA2AgggASwA+wNBAEgEQCABKALwAxAvCyABIAQ2AvADIAggASgA5wM2AAMgCCABKALkAzYCACABIAo6APsDIAEsAKsEQQBIBEAgASgCoAQQLwsgASwAnwRBAE4NASACKAIAEC8MAQsgAUGUBGoiACAGEDkgASAAQaQpEDgiACgCCDYCqAQgASAAKQIANwOgBCAAQgA3AgAgAEEANgIIIAFBoARqQbsqEDciACgCACECIAEgACgCBDYC5AMgASAAKAAHNgDnAyAAQgA3AgAgAC0ACyEEIABBADYCCCABLAD7A0EASARAIAEoAvADEC8LIAEgAjYC8AMgCCABKADnAzYAAyAIIAEoAuQDNgIAIAEgBDoA+wMgASwAqwRBAEgEQCABKAKgBBAvCyABLACfBEEATg0AIAEoApQEEC8LIAEgAUHwA2oiADYClAQgAUGgBGogCyAAIAFBlARqEOIEIAEoAqAEIAY2AhwCQAJAIAUiAiIAKAIAIgRFDQADQCAEIgAoAhAiAiAGSgRAIAAiAigCACIEDQEMAgsgAiAGTg0CIAAoAgQiBA0ACyAAQQRqIQILQSAQMiIEIAY2AhAgBCAANgIIIARCADcCACAEQgA3AhQgBEEANgIcIAIgBDYCACAEIQAgAygC0AEoAgAiCgRAIAMgCjYC0AEgAigCACEACyADKALUASAAEKQBIAMgAygC2AFBAWo2AtgBCwJAIARBFGoiACABQfADakYNACABLQD7AyIKwCECIAQsAB9BAE4EQCACQQBOBEAgACABKQPwAzcCACAAIAEoAvgDNgIIDAILIAAgASgC8AMgASgC9AMQ3gEMAQsgACABKALwAyABQfADaiACQQBIIgAbIAEoAvQDIAogABsQ3wELIAZBAWoiBiADKAIgSA0ACwsgAygCwAEhACABQcglNgLQASABIAAgAEGYlQNKa0G1lANrNgLUAUEEQaztACABQdABahA0IAEoArAEIgAEQCABIAA2ArQEIAAQLwsgASwA+wNBAEgEQCABKALwAxAvCyADKAIQIQggAygCMCEAIAMoAkAhAiABQQE6APgDIAEgASgC+AM2AsgBIAEgAEEPbCACQRhsakEZakG8Amw2AvADQQAhACABQQA2AvQDIAEgASkC8AM3A8ABIAMgAUHAAWoQvgEiAjYCqAEgAkUEQCABQcglNgIAQQJBo+oAIAEQNAwBCyADNAJEISUgAygCQCEKIAMoAjghEiADNAI0IScgAygCKCETIAM0AiQhKCADNAIgISkCQCADKAIwIgwgAygClAEiACADKAKQASIEa0E8bSICSwRAQQAhBiAMIAJrIgQgAygCmAEiCyAAa0E8bU0EQCADIAQEfyAAQQAgBEE8bEE8ayICIAJBPHBrQTxqIgL8CwAgACACagUgAAs2ApQBDAILAkAgACADKAKQASIFa0E8bSIQIARqIgJBxYiRIkkEQEHEiJEiIAsgBWtBPG0iC0EBdCIRIAIgAiARSRsgC0GixIgRTxsiCwRAIAtBxYiRIk8NAiALQTxsEDIhBgsgEEE8bCAGaiICQQAgBEE8bEE8ayIEIARBPHBrQTxqIgT8CwAgAiAEaiEEIAAgBUcEQANAIAJBPGsiAiAAQTxrIgD9AAIA/QsCACACIAAoAjg2AjggAiAAKQIwNwIwIAIgAP0AAiD9CwIgIAIgAP0AAhD9CwIQIAAgBUcNAAsgAygCkAEhAAsgAyAGIAtBPGxqNgKYASADIAQ2ApQBIAMgAjYCkAEgAARAIAAQLwsMAwsQNgALEEcACyACIAxNDQAgAyAEIAxBPGxqNgKUAQsgCEEARyEQAkAgAygCoAEiACADKAKcASIEa0HgAG0iAiAKSQRAQQAhBSAKIAJrIgQgAygCpAEiCyAAa0HgAG1NBEAgAyAEBH8gAEEAIARB4ABsQeAAayICIAJB4ABwa0HgAGoiAvwLACAAIAJqBSAACzYCoAEMAgsCQCAAIAMoApwBIgZrQeAAbSIRIARqIgJBq9WqFUkEQEGq1aoVIAsgBmtB4ABtIgtBAXQiGCACIAIgGEkbIAtB1arVCk8bIgsEQCALQavVqhVPDQIgC0HgAGwQMiEFCyARQeAAbCAFaiICQQAgBEHgAGxB4ABrIgQgBEHgAHBrQeAAaiIE/AsAIAIgBGohBCAAIAZHBEADQCACQeAAayICIABB4ABrIgBB4AD8CgAAIAAgBkcNAAsgAygCnAEhAAsgAyAFIAtB4ABsajYCpAEgAyAENgKgASADIAI2ApwBIAAEQCAAEC8LDAMLEDYACxBHAAsgAiAKTQ0AIAMgBCAKQeAAbGo2AqABCyADIAMoAqgBQQAgE6wiJCAoEFU2AmQgAyADKAKoASAQQgMgJSAkELwBNgJoIAMgAygCqAFBAEIBICQQVTYCbCADIAMoAqgBIBBCAyAkICQQvAE2AnAgAyADKAKoAUEAQgEgJBBVNgJ0IAMgAygCqAFBACAkEEg2AnggAyADKAKoAUEAICQQSDYCfCADKAJkIQIgAUEgEDIiADYC8AMgAUKcgICAgISAgIB/NwL0AyAAQZAeKAAANgAYIABBiB4pAAA3ABAgAEH4Hf0AAAD9CwAAIABBADoAHCABIAFB8ANqIgA2AqAEIAFBsARqIANBtAFqIgUgACABQaAEahA8IAEoArAEIAI2AhwgASwA+wNBAEgEQCABKALwAxAvCyADKAJoIQIgAUEgEDIiADYC8AMgAUKUgICAgISAgIB/NwL0AyAAQdEOKAAANgAQIABBwQ79AAAA/QsAACAAQQA6ABQgASABQfADaiIANgKgBCABQbAEaiAFIAAgAUGgBGoQPCABKAKwBCACNgIcIAEsAPsDQQBIBEAgASgC8AMQLwsgAygCbCECIAFBIBAyIgA2AvADIAFCkoCAgICEgICAfzcC9AMgAEGNEy8AADsAECAAQf0S/QAAAP0LAAAgAEEAOgASIAEgAUHwA2oiADYCoAQgAUGwBGogBSAAIAFBoARqEDwgASgCsAQgAjYCHCABLAD7A0EASARAIAEoAvADEC8LIAMoAnAhAiABQSAQMiIANgLwAyABQpSAgICAhICAgH83AvQDIABBrg4oAAA2ABAgAEGeDv0AAAD9CwAAIABBADoAFCABIAFB8ANqIgA2AqAEIAFBsARqIAUgACABQaAEahA8IAEoArAEIAI2AhwgASwA+wNBAEgEQCABKALwAxAvCyADKAJ0IQIgAUEgEDIiADYC8AMgAUKSgICAgISAgIB/NwL0AyAAQe4SLwAAOwAQIABB3hL9AAAA/QsAACAAQQA6ABIgASABQfADaiIANgKgBCABQbAEaiAFIAAgAUGgBGoQPCABKAKwBCACNgIcIAEsAPsDQQBIBEAgASgC8AMQLwsgAygCeCECIAFBIBAyIgA2AvADIAFCloCAgICEgICAfzcC9AMgAEGDDSkAADcADiAAQfUM/QAAAP0LAAAgAEEAOgAWIAEgAUHwA2oiADYCoAQgAUGwBGogBSAAIAFBoARqEDwgASgCsAQgAjYCHCABLAD7A0EASARAIAEoAvADEC8LIAMoAnwhAiABQSAQMiIANgLwAyABQpSAgICAhICAgH83AvQDIABB8hEoAAA2ABAgAEHiEf0AAAD9CwAAIABBADoAFCABIAFB8ANqIgA2AqAEIAFBsARqIAUgACABQaAEahA8IAEoArAEIAI2AhwgASwA+wNBAEgEQCABKALwAxAvCyAMQQBKBEAgE0ECdKwhJUEAIQQDQCADKAKQASAEQTxsaiIAIAMoAqgBQQAgJBBINgIkIAAgAygCqAFBACAkEEg2AiggACADKAKoASAIICQgJRBVNgIsIAAgAygCqAFBACAlEEg2AjAgACADKAKoASAIICUgJBBVNgI0IAAgAygCqAFBACAkEEg2AjggACADKAKoAUEAICQQSDYCACAAIAMoAqgBQQAgJBBINgIEIAAgAygCqAEgCCAkICQQVTYCECAAIAMoAqgBQQAgJBBINgIUIAAgAygCqAEgCCAkICQQVTYCGCAAIAMoAqgBIAggJCAkEFU2AhwgACADKAKoAUEAICQQSDYCICAAIAMoAqgBIAggJCAkEFU2AgggACADKAKoAUEAICQQSDYCDCAAKAIkIQYgAUGgBGoiAiAEEDkgASACQc7CABA4IgIoAgg2ArgEIAEgAikCADcDsAQgAkIANwIAIAJBADYCCCABIAFBsARqQYwNEDciAigCCDYC+AMgASACKQIANwPwAyACQgA3AgAgAkEANgIIIAEgAUHwA2oiAjYC5AMgAUGUBGogBSACIAFB5ANqEDwgASgClAQgBjYCHCABLAD7A0EASARAIAEoAvADEC8LIAEsALsEQQBIBEAgASgCsAQQLwsgASwAqwRBAEgEQCABKAKgBBAvCyAAKAIoIQYgAUGgBGoiAiAEEDkgASACQc7CABA4IgIoAgg2ArgEIAEgAikCADcDsAQgAkIANwIAIAJBADYCCCABIAFBsARqQfcREDciAigCCDYC+AMgASACKQIANwPwAyACQgA3AgAgAkEANgIIIAEgAUHwA2oiAjYC5AMgAUGUBGogBSACIAFB5ANqEDwgASgClAQgBjYCHCABLAD7A0EASARAIAEoAvADEC8LIAEsALsEQQBIBEAgASgCsAQQLwsgASwAqwRBAEgEQCABKAKgBBAvCyAAKAIsIQYgAUGgBGoiAiAEEDkgASACQc7CABA4IgIoAgg2ArgEIAEgAikCADcDsAQgAkIANwIAIAJBADYCCCABIAFBsARqQdYOEDciAigCCDYC+AMgASACKQIANwPwAyACQgA3AgAgAkEANgIIIAEgAUHwA2oiAjYC5AMgAUGUBGogBSACIAFB5ANqEDwgASgClAQgBjYCHCABLAD7A0EASARAIAEoAvADEC8LIAEsALsEQQBIBEAgASgCsAQQLwsgASwAqwRBAEgEQCABKAKgBBAvCyAAKAIwIQYgAUGgBGoiAiAEEDkgASACQc7CABA4IgIoAgg2ArgEIAEgAikCADcDsAQgAkIANwIAIAJBADYCCCABIAFBsARqQZATEDciAigCCDYC+AMgASACKQIANwPwAyACQgA3AgAgAkEANgIIIAEgAUHwA2oiAjYC5AMgAUGUBGogBSACIAFB5ANqEDwgASgClAQgBjYCHCABLAD7A0EASARAIAEoAvADEC8LIAEsALsEQQBIBEAgASgCsAQQLwsgASwAqwRBAEgEQCABKAKgBBAvCyAAKAI0IQYgAUGgBGoiAiAEEDkgASACQc7CABA4IgIoAgg2ArgEIAEgAikCADcDsAQgAkIANwIAIAJBADYCCCABIAFBsARqQbMOEDciAigCCDYC+AMgASACKQIANwPwAyACQgA3AgAgAkEANgIIIAEgAUHwA2oiAjYC5AMgAUGUBGogBSACIAFB5ANqEDwgASgClAQgBjYCHCABLAD7A0EASARAIAEoAvADEC8LIAEsALsEQQBIBEAgASgCsAQQLwsgASwAqwRBAEgEQCABKAKgBBAvCyAAKAI4IQYgAUGgBGoiAiAEEDkgASACQc7CABA4IgIoAgg2ArgEIAEgAikCADcDsAQgAkIANwIAIAJBADYCCCABIAFBsARqQfESEDciAigCCDYC+AMgASACKQIANwPwAyACQgA3AgAgAkEANgIIIAEgAUHwA2oiAjYC5AMgAUGUBGogBSACIAFB5ANqEDwgASgClAQgBjYCHCABLAD7A0EASARAIAEoAvADEC8LIAEsALsEQQBIBEAgASgCsAQQLwsgASwAqwRBAEgEQCABKAKgBBAvCyAAKAIAIQYgAUGgBGoiAiAEEDkgASACQc7CABA4IgIoAgg2ArgEIAEgAikCADcDsAQgAkIANwIAIAJBADYCCCABIAFBsARqQbENEDciAigCCDYC+AMgASACKQIANwPwAyACQgA3AgAgAkEANgIIIAEgAUHwA2oiAjYC5AMgAUGUBGogBSACIAFB5ANqEDwgASgClAQgBjYCHCABLAD7A0EASARAIAEoAvADEC8LIAEsALsEQQBIBEAgASgCsAQQLwsgASwAqwRBAEgEQCABKAKgBBAvCyAAKAIEIQYgAUGgBGoiAiAEEDkgASACQc7CABA4IgIoAgg2ArgEIAEgAikCADcDsAQgAkIANwIAIAJBADYCCCABIAFBsARqQZgSEDciAigCCDYC+AMgASACKQIANwPwAyACQgA3AgAgAkEANgIIIAEgAUHwA2oiAjYC5AMgAUGUBGogBSACIAFB5ANqEDwgASgClAQgBjYCHCABLAD7A0EASARAIAEoAvADEC8LIAEsALsEQQBIBEAgASgCsAQQLwsgASwAqwRBAEgEQCABKAKgBBAvCyAAKAIQIQYgAUGgBGoiAiAEEDkgASACQc7CABA4IgIoAgg2ArgEIAEgAikCADcDsAQgAkIANwIAIAJBADYCCCABIAFBsARqQZIMEDciAigCCDYC+AMgASACKQIANwPwAyACQgA3AgAgAkEANgIIIAEgAUHwA2oiAjYC5AMgAUGUBGogBSACIAFB5ANqEDwgASgClAQgBjYCHCABLAD7A0EASARAIAEoAvADEC8LIAEsALsEQQBIBEAgASgCsAQQLwsgASwAqwRBAEgEQCABKAKgBBAvCyAAKAIUIQYgAUGgBGoiAiAEEDkgASACQc7CABA4IgIoAgg2ArgEIAEgAikCADcDsAQgAkIANwIAIAJBADYCCCABIAFBsARqQa0REDciAigCCDYC+AMgASACKQIANwPwAyACQgA3AgAgAkEANgIIIAEgAUHwA2oiAjYC5AMgAUGUBGogBSACIAFB5ANqEDwgASgClAQgBjYCHCABLAD7A0EASARAIAEoAvADEC8LIAEsALsEQQBIBEAgASgCsAQQLwsgASwAqwRBAEgEQCABKAKgBBAvCyAAKAIYIQYgAUGgBGoiAiAEEDkgASACQc7CABA4IgIoAgg2ArgEIAEgAikCADcDsAQgAkIANwIAIAJBADYCCCABIAFBsARqQbwMEDciAigCCDYC+AMgASACKQIANwPwAyACQgA3AgAgAkEANgIIIAEgAUHwA2oiAjYC5AMgAUGUBGogBSACIAFB5ANqEDwgASgClAQgBjYCHCABLAD7A0EASARAIAEoAvADEC8LIAEsALsEQQBIBEAgASgCsAQQLwsgASwAqwRBAEgEQCABKAKgBBAvCyAAKAIcIQYgAUGgBGoiAiAEEDkgASACQc7CABA4IgIoAgg2ArgEIAEgAikCADcDsAQgAkIANwIAIAJBADYCCCABIAFBsARqQYsOEDciAigCCDYC+AMgASACKQIANwPwAyACQgA3AgAgAkEANgIIIAEgAUHwA2oiAjYC5AMgAUGUBGogBSACIAFB5ANqEDwgASgClAQgBjYCHCABLAD7A0EASARAIAEoAvADEC8LIAEsALsEQQBIBEAgASgCsAQQLwsgASwAqwRBAEgEQCABKAKgBBAvCyAAKAIgIQYgAUGgBGoiAiAEEDkgASACQc7CABA4IgIoAgg2ArgEIAEgAikCADcDsAQgAkIANwIAIAJBADYCCCABIAFBsARqQc0SEDciAigCCDYC+AMgASACKQIANwPwAyACQgA3AgAgAkEANgIIIAEgAUHwA2oiAjYC5AMgAUGUBGogBSACIAFB5ANqEDwgASgClAQgBjYCHCABLAD7A0EASARAIAEoAvADEC8LIAEsALsEQQBIBEAgASgCsAQQLwsgASwAqwRBAEgEQCABKAKgBBAvCyAAKAIIIQYgAUGgBGoiAiAEEDkgASACQc7CABA4IgIoAgg2ArgEIAEgAikCADcDsAQgAkIANwIAIAJBADYCCCABIAFBsARqQeQMEDciAigCCDYC+AMgASACKQIANwPwAyACQgA3AgAgAkEANgIIIAEgAUHwA2oiAjYC5AMgAUGUBGogBSACIAFB5ANqEDwgASgClAQgBjYCHCABLAD7A0EASARAIAEoAvADEC8LIAEsALsEQQBIBEAgASgCsAQQLwsgASwAqwRBAEgEQCABKAKgBBAvCyAAKAIMIQIgAUGgBGoiACAEEDkgASAAQc7CABA4IgAoAgg2ArgEIAEgACkCADcDsAQgAEIANwIAIABBADYCCCABIAFBsARqQdMREDciACgCCDYC+AMgASAAKQIANwPwAyAAQgA3AgAgAEEANgIIIAEgAUHwA2oiADYC5AMgAUGUBGogBSAAIAFB5ANqEDwgASgClAQgAjYCHCABLAD7A0EASARAIAEoAvADEC8LIAEsALsEQQBIBEAgASgCsAQQLwsgASwAqwRBAEgEQCABKAKgBBAvCyAEQQFqIgQgDEcNAAsLIAMgAygCqAFBACASrCIkICcQVTYCgAEgAyADKAKoASAIICQgKRBVNgKEASADIAMoAqgBQQAgJBBINgKIASADIAMoAqgBQQAgJBBINgKMASADKAKAASECIAFBIBAyIgA2AvADIAFCnICAgICEgICAfzcC9AMgAEGtHigAADYAGCAAQaUeKQAANwAQIABBlR79AAAA/QsAACAAQQA6ABwgASABQfADaiIANgKgBCABQbAEaiAFIAAgAUGgBGoQPCABKAKwBCACNgIcIAEsAPsDQQBIBEAgASgC8AMQLwsgAygChAEhAiABQSAQMiIANgLwAyABQp6AgICAhICAgH83AvQDIABB6Q0pAAA3ABYgAEHjDSkAADcAECAAQdMN/QAAAP0LAAAgAEEAOgAeIAEgAUHwA2oiADYCoAQgAUGwBGogBSAAIAFBoARqEDwgASgCsAQgAjYCHCABLAD7A0EASARAIAEoAvADEC8LIAMoAogBIQIgAUEgEDIiADYC8AMgAUKRgICAgISAgIB/NwL0AyAAQdENLQAAOgAQIABBwQ39AAAA/QsAACAAQQA6ABEgASABQfADaiIANgKgBCABQbAEaiAFIAAgAUGgBGoQPCABKAKwBCACNgIcIAEsAPsDQQBIBEAgASgC8AMQLwsgAygCjAEhAiABQRAQMiIANgLwAyABQo+AgICAgoCAgH83AvQDIABBrRIpAAA3AAcgAEGmEikAADcAACAAQQA6AA8gASABQfADaiIANgKgBCABQbAEaiAFIAAgAUGgBGoQPCABKAKwBCACNgIcIAEsAPsDQQBIBEAgASgC8AMQLwsgCkEASgRAIBJBAnSsISVBACEEA0AgAygCnAEgBEHgAGxqIgAgAygCqAFBACAkEEg2AkggACADKAKoAUEAICQQSDYCTCAAIAMoAqgBIAggJCAlEFU2AlAgACADKAKoAUEAICUQSDYCVCAAIAMoAqgBIAggJSAkEFU2AlggACADKAKoAUEAICQQSDYCXCAAIAMoAqgBQQAgJBBINgIAIAAgAygCqAFBACAkEEg2AgQgACADKAKoASAIICQgJBBVNgIQIAAgAygCqAFBACAkEEg2AhQgACADKAKoASAIICQgJBBVNgIYIAAgAygCqAEgCCAkICQQVTYCHCAAIAMoAqgBQQAgJBBINgIgIAAgAygCqAEgCCAkICQQVTYCCCAAIAMoAqgBQQAgJBBINgIMIAAgAygCqAFBACAkEEg2AiQgACADKAKoAUEAICQQSDYCKCAAIAMoAqgBIAggJCAkEFU2AjQgACADKAKoAUEAICQQSDYCOCAAIAMoAqgBIAggJCAkEFU2AjwgACADKAKoASAIICQgJBBVNgJAIAAgAygCqAFBACAkEEg2AkQgACADKAKoASAIICQgJBBVNgIsIAAgAygCqAFBACAkEEg2AjAgACgCSCEGIAFBoARqIgIgBBA5IAEgAkHewgAQOCICKAIINgK4BCABIAIpAgA3A7AEIAJCADcCACACQQA2AgggASABQbAEakGMDRA3IgIoAgg2AvgDIAEgAikCADcD8AMgAkIANwIAIAJBADYCCCABIAFB8ANqIgI2AuQDIAFBlARqIAUgAiABQeQDahA8IAEoApQEIAY2AhwgASwA+wNBAEgEQCABKALwAxAvCyABLAC7BEEASARAIAEoArAEEC8LIAEsAKsEQQBIBEAgASgCoAQQLwsgACgCTCEGIAFBoARqIgIgBBA5IAEgAkHewgAQOCICKAIINgK4BCABIAIpAgA3A7AEIAJCADcCACACQQA2AgggASABQbAEakH3ERA3IgIoAgg2AvgDIAEgAikCADcD8AMgAkIANwIAIAJBADYCCCABIAFB8ANqIgI2AuQDIAFBlARqIAUgAiABQeQDahA8IAEoApQEIAY2AhwgASwA+wNBAEgEQCABKALwAxAvCyABLAC7BEEASARAIAEoArAEEC8LIAEsAKsEQQBIBEAgASgCoAQQLwsgACgCUCEGIAFBoARqIgIgBBA5IAEgAkHewgAQOCICKAIINgK4BCABIAIpAgA3A7AEIAJCADcCACACQQA2AgggASABQbAEakHWDhA3IgIoAgg2AvgDIAEgAikCADcD8AMgAkIANwIAIAJBADYCCCABIAFB8ANqIgI2AuQDIAFBlARqIAUgAiABQeQDahA8IAEoApQEIAY2AhwgASwA+wNBAEgEQCABKALwAxAvCyABLAC7BEEASARAIAEoArAEEC8LIAEsAKsEQQBIBEAgASgCoAQQLwsgACgCVCEGIAFBoARqIgIgBBA5IAEgAkHewgAQOCICKAIINgK4BCABIAIpAgA3A7AEIAJCADcCACACQQA2AgggASABQbAEakGQExA3IgIoAgg2AvgDIAEgAikCADcD8AMgAkIANwIAIAJBADYCCCABIAFB8ANqIgI2AuQDIAFBlARqIAUgAiABQeQDahA8IAEoApQEIAY2AhwgASwA+wNBAEgEQCABKALwAxAvCyABLAC7BEEASARAIAEoArAEEC8LIAEsAKsEQQBIBEAgASgCoAQQLwsgACgCWCEGIAFBoARqIgIgBBA5IAEgAkHewgAQOCICKAIINgK4BCABIAIpAgA3A7AEIAJCADcCACACQQA2AgggASABQbAEakGzDhA3IgIoAgg2AvgDIAEgAikCADcD8AMgAkIANwIAIAJBADYCCCABIAFB8ANqIgI2AuQDIAFBlARqIAUgAiABQeQDahA8IAEoApQEIAY2AhwgASwA+wNBAEgEQCABKALwAxAvCyABLAC7BEEASARAIAEoArAEEC8LIAEsAKsEQQBIBEAgASgCoAQQLwsgACgCXCEGIAFBoARqIgIgBBA5IAEgAkHewgAQOCICKAIINgK4BCABIAIpAgA3A7AEIAJCADcCACACQQA2AgggASABQbAEakHxEhA3IgIoAgg2AvgDIAEgAikCADcD8AMgAkIANwIAIAJBADYCCCABIAFB8ANqIgI2AuQDIAFBlARqIAUgAiABQeQDahA8IAEoApQEIAY2AhwgASwA+wNBAEgEQCABKALwAxAvCyABLAC7BEEASARAIAEoArAEEC8LIAEsAKsEQQBIBEAgASgCoAQQLwsgACgCACEGIAFBoARqIgIgBBA5IAEgAkHewgAQOCICKAIINgK4BCABIAIpAgA3A7AEIAJCADcCACACQQA2AgggASABQbAEakGxDRA3IgIoAgg2AvgDIAEgAikCADcD8AMgAkIANwIAIAJBADYCCCABIAFB8ANqIgI2AuQDIAFBlARqIAUgAiABQeQDahA8IAEoApQEIAY2AhwgASwA+wNBAEgEQCABKALwAxAvCyABLAC7BEEASARAIAEoArAEEC8LIAEsAKsEQQBIBEAgASgCoAQQLwsgACgCBCEGIAFBoARqIgIgBBA5IAEgAkHewgAQOCICKAIINgK4BCABIAIpAgA3A7AEIAJCADcCACACQQA2AgggASABQbAEakGYEhA3IgIoAgg2AvgDIAEgAikCADcD8AMgAkIANwIAIAJBADYCCCABIAFB8ANqIgI2AuQDIAFBlARqIAUgAiABQeQDahA8IAEoApQEIAY2AhwgASwA+wNBAEgEQCABKALwAxAvCyABLAC7BEEASARAIAEoArAEEC8LIAEsAKsEQQBIBEAgASgCoAQQLwsgACgCECEGIAFBoARqIgIgBBA5IAEgAkHewgAQOCICKAIINgK4BCABIAIpAgA3A7AEIAJCADcCACACQQA2AgggASABQbAEakGSDBA3IgIoAgg2AvgDIAEgAikCADcD8AMgAkIANwIAIAJBADYCCCABIAFB8ANqIgI2AuQDIAFBlARqIAUgAiABQeQDahA8IAEoApQEIAY2AhwgASwA+wNBAEgEQCABKALwAxAvCyABLAC7BEEASARAIAEoArAEEC8LIAEsAKsEQQBIBEAgASgCoAQQLwsgACgCFCEGIAFBoARqIgIgBBA5IAEgAkHewgAQOCICKAIINgK4BCABIAIpAgA3A7AEIAJCADcCACACQQA2AgggASABQbAEakGtERA3IgIoAgg2AvgDIAEgAikCADcD8AMgAkIANwIAIAJBADYCCCABIAFB8ANqIgI2AuQDIAFBlARqIAUgAiABQeQDahA8IAEoApQEIAY2AhwgASwA+wNBAEgEQCABKALwAxAvCyABLAC7BEEASARAIAEoArAEEC8LIAEsAKsEQQBIBEAgASgCoAQQLwsgACgCGCEGIAFBoARqIgIgBBA5IAEgAkHewgAQOCICKAIINgK4BCABIAIpAgA3A7AEIAJCADcCACACQQA2AgggASABQbAEakG8DBA3IgIoAgg2AvgDIAEgAikCADcD8AMgAkIANwIAIAJBADYCCCABIAFB8ANqIgI2AuQDIAFBlARqIAUgAiABQeQDahA8IAEoApQEIAY2AhwgASwA+wNBAEgEQCABKALwAxAvCyABLAC7BEEASARAIAEoArAEEC8LIAEsAKsEQQBIBEAgASgCoAQQLwsgACgCHCEGIAFBoARqIgIgBBA5IAEgAkHewgAQOCICKAIINgK4BCABIAIpAgA3A7AEIAJCADcCACACQQA2AgggASABQbAEakGLDhA3IgIoAgg2AvgDIAEgAikCADcD8AMgAkIANwIAIAJBADYCCCABIAFB8ANqIgI2AuQDIAFBlARqIAUgAiABQeQDahA8IAEoApQEIAY2AhwgASwA+wNBAEgEQCABKALwAxAvCyABLAC7BEEASARAIAEoArAEEC8LIAEsAKsEQQBIBEAgASgCoAQQLwsgACgCICEGIAFBoARqIgIgBBA5IAEgAkHewgAQOCICKAIINgK4BCABIAIpAgA3A7AEIAJCADcCACACQQA2AgggASABQbAEakHNEhA3IgIoAgg2AvgDIAEgAikCADcD8AMgAkIANwIAIAJBADYCCCABIAFB8ANqIgI2AuQDIAFBlARqIAUgAiABQeQDahA8IAEoApQEIAY2AhwgASwA+wNBAEgEQCABKALwAxAvCyABLAC7BEEASARAIAEoArAEEC8LIAEsAKsEQQBIBEAgASgCoAQQLwsgACgCCCEGIAFBoARqIgIgBBA5IAEgAkHewgAQOCICKAIINgK4BCABIAIpAgA3A7AEIAJCADcCACACQQA2AgggASABQbAEakHkDBA3IgIoAgg2AvgDIAEgAikCADcD8AMgAkIANwIAIAJBADYCCCABIAFB8ANqIgI2AuQDIAFBlARqIAUgAiABQeQDahA8IAEoApQEIAY2AhwgASwA+wNBAEgEQCABKALwAxAvCyABLAC7BEEASARAIAEoArAEEC8LIAEsAKsEQQBIBEAgASgCoAQQLwsgACgCDCEGIAFBoARqIgIgBBA5IAEgAkHewgAQOCICKAIINgK4BCABIAIpAgA3A7AEIAJCADcCACACQQA2AgggASABQbAEakHTERA3IgIoAgg2AvgDIAEgAikCADcD8AMgAkIANwIAIAJBADYCCCABIAFB8ANqIgI2AuQDIAFBlARqIAUgAiABQeQDahA8IAEoApQEIAY2AhwgASwA+wNBAEgEQCABKALwAxAvCyABLAC7BEEASARAIAEoArAEEC8LIAEsAKsEQQBIBEAgASgCoAQQLwsgACgCJCEGIAFBoARqIgIgBBA5IAEgAkHewgAQOCICKAIINgK4BCABIAIpAgA3A7AEIAJCADcCACACQQA2AgggASABQbAEakGbDRA3IgIoAgg2AvgDIAEgAikCADcD8AMgAkIANwIAIAJBADYCCCABIAFB8ANqIgI2AuQDIAFBlARqIAUgAiABQeQDahA8IAEoApQEIAY2AhwgASwA+wNBAEgEQCABKALwAxAvCyABLAC7BEEASARAIAEoArAEEC8LIAEsAKsEQQBIBEAgASgCoAQQLwsgACgCKCEGIAFBoARqIgIgBBA5IAEgAkHewgAQOCICKAIINgK4BCABIAIpAgA3A7AEIAJCADcCACACQQA2AgggASABQbAEakGEEhA3IgIoAgg2AvgDIAEgAikCADcD8AMgAkIANwIAIAJBADYCCCABIAFB8ANqIgI2AuQDIAFBlARqIAUgAiABQeQDahA8IAEoApQEIAY2AhwgASwA+wNBAEgEQCABKALwAxAvCyABLAC7BEEASARAIAEoArAEEC8LIAEsAKsEQQBIBEAgASgCoAQQLwsgACgCNCEGIAFBoARqIgIgBBA5IAEgAkHewgAQOCICKAIINgK4BCABIAIpAgA3A7AEIAJCADcCACACQQA2AgggASABQbAEakH5CxA3IgIoAgg2AvgDIAEgAikCADcD8AMgAkIANwIAIAJBADYCCCABIAFB8ANqIgI2AuQDIAFBlARqIAUgAiABQeQDahA8IAEoApQEIAY2AhwgASwA+wNBAEgEQCABKALwAxAvCyABLAC7BEEASARAIAEoArAEEC8LIAEsAKsEQQBIBEAgASgCoAQQLwsgACgCOCEGIAFBoARqIgIgBBA5IAEgAkHewgAQOCICKAIINgK4BCABIAIpAgA3A7AEIAJCADcCACACQQA2AgggASABQbAEakGWERA3IgIoAgg2AvgDIAEgAikCADcD8AMgAkIANwIAIAJBADYCCCABIAFB8ANqIgI2AuQDIAFBlARqIAUgAiABQeQDahA8IAEoApQEIAY2AhwgASwA+wNBAEgEQCABKALwAxAvCyABLAC7BEEASARAIAEoArAEEC8LIAEsAKsEQQBIBEAgASgCoAQQLwsgACgCPCEGIAFBoARqIgIgBBA5IAEgAkHewgAQOCICKAIINgK4BCABIAIpAgA3A7AEIAJCADcCACACQQA2AgggASABQbAEakGlDBA3IgIoAgg2AvgDIAEgAikCADcD8AMgAkIANwIAIAJBADYCCCABIAFB8ANqIgI2AuQDIAFBlARqIAUgAiABQeQDahA8IAEoApQEIAY2AhwgASwA+wNBAEgEQCABKALwAxAvCyABLAC7BEEASARAIAEoArAEEC8LIAEsAKsEQQBIBEAgASgCoAQQLwsgAEFAaygCACEGIAFBoARqIgIgBBA5IAEgAkHewgAQOCICKAIINgK4BCABIAIpAgA3A7AEIAJCADcCACACQQA2AgggASABQbAEakHyDRA3IgIoAgg2AvgDIAEgAikCADcD8AMgAkIANwIAIAJBADYCCCABIAFB8ANqIgI2AuQDIAFBlARqIAUgAiABQeQDahA8IAEoApQEIAY2AhwgASwA+wNBAEgEQCABKALwAxAvCyABLAC7BEEASARAIAEoArAEEC8LIAEsAKsEQQBIBEAgASgCoAQQLwsgACgCRCEGIAFBoARqIgIgBBA5IAEgAkHewgAQOCICKAIINgK4BCABIAIpAgA3A7AEIAJCADcCACACQQA2AgggASABQbAEakG2EhA3IgIoAgg2AvgDIAEgAikCADcD8AMgAkIANwIAIAJBADYCCCABIAFB8ANqIgI2AuQDIAFBlARqIAUgAiABQeQDahA8IAEoApQEIAY2AhwgASwA+wNBAEgEQCABKALwAxAvCyABLAC7BEEASARAIAEoArAEEC8LIAEsAKsEQQBIBEAgASgCoAQQLwsgACgCLCEGIAFBoARqIgIgBBA5IAEgAkHewgAQOCICKAIINgK4BCABIAIpAgA3A7AEIAJCADcCACACQQA2AgggASABQbAEakHNDBA3IgIoAgg2AvgDIAEgAikCADcD8AMgAkIANwIAIAJBADYCCCABIAFB8ANqIgI2AuQDIAFBlARqIAUgAiABQeQDahA8IAEoApQEIAY2AhwgASwA+wNBAEgEQCABKALwAxAvCyABLAC7BEEASARAIAEoArAEEC8LIAEsAKsEQQBIBEAgASgCoAQQLwsgACgCMCECIAFBoARqIgAgBBA5IAEgAEHewgAQOCIAKAIINgK4BCABIAApAgA3A7AEIABCADcCACAAQQA2AgggASABQbAEakG+ERA3IgAoAgg2AvgDIAEgACkCADcD8AMgAEIANwIAIABBADYCCCABIAFB8ANqIgA2AuQDIAFBlARqIAUgACABQeQDahA8IAEoApQEIAI2AhwgASwA+wNBAEgEQCABKALwAxAvCyABLAC7BEEASARAIAEoArAEEC8LIAEsAKsEQQBIBEAgASgCoAQQLwsgBEEBaiIEIApHDQALCyADEOUEIgA2AoQCQQAhBCADKAK0ASICIANBuAFqIgZHBEADQCACKAIcEFsgBGohHAJAIAIoAgQiBARAA0AgBCIAKAIAIgQNAAwCCwALA0AgAigCCCIAKAIAIAJHIRsgACECIBsNAAsLIBxBvAJqIQQgBiAAIgJHDQALIAMoAoQCIQALIAMgACAAKAIIEQAAIgAgBCAAKAIAEQQANgKsASADKAKEAiIABH8gACAAKAIAEQAABUGwMAshACABIAS4RAAAAACAhC5BozkDuAEgASAANgK0ASABQcglNgKwAUEEQa3vACABQbABahA0IAMoAqwBEMkBIQwgBiADKAK0ASICRwRAA0AgDCACKAIcEJABAkAgAigCBCIEBEADQCAEIgAoAgAiBA0ADAILAAsDQCACKAIIIgAoAgAgAkchHSAAIQIgHQ0ACwsgBiAAIgJHDQALCyADQQA2ArABIAFBADYCuAQgAUIANwKwBEEAIQoDQAJAIAcoAiQgAUGQBGpBBCAHKAIoEQMAGiAHKAIkIAFBjARqQQQgBygCKBEDABogBygCJCABQYgEakEEIAcoAigRAwAaAkACQAJAIAcoAiQgBygCLBEAAARAIAFByCU2AjAgASAKuEQAAAAAgIQuQaM5AzhBBEGp8QAgAUEwahA0IAMoArABIgANASABQcglNgIQQQNBxOYAIAFBEGoQNEEAIQIMBAtBACEAIAFB0KUB/QAEAP0LBPADQQEhBCABKAKQBEEASg0BDAILQQAhAiAAIAMoArwBIgRGDQIgASAANgIoIAEgBDYCJCABQcglNgIgQQJB1+oAIAFBIGoQNEEBIQIMAgsDQCAHKAIkIAFB8ANqIABBAnRqIgJBBCAHKAIoEQMAGiACKAIAIARsIQQgAEEBaiIAIAEoApAESA0ACwtBACECIAFBADYCqAQgAUIANwOgBCABQQA2ApwEIAFCADcClARBACEAIAEoAowEIggEQCAIQQBIDQQgASAIEDIiADYClAQgASAAIAhqIgI2ApwEIABBACAI/AsAIAEgAjYCmAQLIAcoAiQgACACIABrIgggBygCKBEDABogAUGgBGoiAiAAIAgQywECfyAFIAIQ8AIgBkYEQCABQcglNgJAQQAhBCABIAEoAqAEIAIgASwAqwRBAEgbNgJEQQJB9+cAIAFBQGsQNEEBDAELIAEoAqAEIAFBoARqIAEsAKsEQQBIGyIIEGgiAEHw////B08NBQJAIABBCk0EQCABIAA6AO8DIAFB5ANqIQIMAQsgAEEPckEBaiILEDIhAiABIAtBgICAgHhyNgLsAyABIAI2AuQDIAEgADYC6AMLIAIgCCAA/AoAACAAIAJqQQA6AAAgASABQeQDaiIANgLEBCABQcgEaiAFIAAgAUHEBGoQPCABKALIBCgCHCEAIAEsAO8DQQBIBEAgASgC5AMQLwsgBKwgACkDKCAAKQMgIAApAxggACkDEH5+flIEQCABQcglNgKgAUEAIQQgASABKAKgBCABQaAEaiABLACrBEEASBs2AqQBQQJByecAIAFBoAFqEDQgACkDICEkIAApAxghJSABIAApAxA+ApABIAEgJT4ClAEgASAkPgKYASABQcglNgKAASABIAEoAvADNgKEASABIAEpAvQDNwOIAUECQfzuACABQYABahA0QQEMAQsgACkDGCEkIAEoAvQDIQgCQAJAIAApAxAiJSABKALwAyILrFIEQCABKAL4AyECDAELIAEoAvgDIQIgJCAIrFINACAAKQMgIAKsUQ0BCyABIAApAyA+AnAgASALNgJ0IAEgCDYCeCABIAI2AnwgAUHIJTYCYEEAIQQgASABKAKgBCABQaAEaiABLACrBEEASBs2AmQgASAlPgJoIAEgJD4CbEECQaTuACABQeAAahA0QQEMAQsgBCABKAKIBEEkbEGYmwFqKAIAbCICIAAoAgBBJGxBlJsBaigCAG4gABBbRwRAIAEoAqAEIQggASwAqwQhCyAAEFshACABIAI2AlwgASAANgJYIAFByCU2AlBBACEEIAEgCCABQaAEaiALQQBIGzYCVEECQZviACABQdAAahA0QQEMAQsCQCADKAKEAigCAEE8RgRAIAcoAighAiAHKAIkIAAoAtABIAAQWyACEQMAGgwBCwJAIAAQWyICIAFBsARqIgQoAgQgBCgCACILayIISwRAIAQgAiAIaxDxAgwBCyACIAhJBEAgBCACIAtqNgIECwsgBygCJCABKAKwBCICIAEoArQEIAJrIAcoAigRAwAaIAAgASgCsARBACAAEFsQlwELIAAQWyEeQQEhBCADIAMoArABQQFqNgKwASAeIApqIQpBAAshAiABKAKUBCIABEAgABAvCyABLACrBEEASARAIAEoAqAEEC8LIAQNAQsLIAEoArAEIgAEQCABIAA2ArQEIAAQLwtBACEAIAINACAMEJEBIAMQaSAmfTcDAEEBIQALIAFB0ARqJAAgAAwCCxA2AAsQTQALIR8gBygCJCAHKAIwEQEAIB9FBEAgD0HTHzYCAEECQfPlACAPEDQgAywAkwJBAEgEQCADKAKIAhAvCyAXIAMoAtQBEI8CIBYgAygCyAEQjgIgFSADKAK4ARCNAiADKAKcASIABEAgAyAANgKgASAAEC8LIAMoApABIgAEQCADIAA2ApQBIAAQLwsgAygCWCIABEAgAyAANgJcIAAQLwsgAxAvQQAh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5ZKe4AZsIARqIgA2AgAgA0ECaiIEQQJ0IAJqIABBHnYgAHNB5ZKe4AZsIARqIgA2AgAgA0EDaiIEQfAERwRAIARBAnQgAmogAEEediAAc0Hlkp7gBmwgBGoiADYCACADQQRqIQMMAQsLIAFBADYCwBQgBUHkAmogAUGAAWoiAEHEE/wKAAAgCSgChAIhAiABIAFByBRqNgKIASABQeS1ATYCgAEgASAANgKQASABIAFBzBRqNgKEASAFQZSnAWogAiAAEJACAkACfyAAIAEoApABIgNGBEAgACEDIAEoAoABQRBqDAELIANFDQEgAygCAEEUagshACADIAAoAgARAQALIAEoAsgUIgBBmKcBaigCACECIABBnKcBaigCACEEIAAoApSnASgCACgClBAhACABQcAfNgIwIAEgACAEIAJrarhEAAAAAICELkGjOQM4QQRBgfEAIAFBMGoQNCABKALMFCgChAIhAiABKALIFCEhIAEgAUHIFGo2AogBIAFB1LcBNgKAASABIAFBgAFqIgA2ApABIAEgAUHMFGo2AoQBICFBqKcBaiACIAAQkAICQAJ/IAAgASgCkAEiA0YEQCAAIQMgASgCgAFBEGoMAQsgA0UNASADKAIAQRRqCyEAIAMgACgCABEBAAsgASgCyBQiAEGspwFqKAIAIQIgAEGwpwFqKAIAIQQgACgCqKcBKAIAKAKUECEAIAFBwB82AiAgASAAIAQgAmtquEQAAAAAgIQuQaM5AyhBBEHr7wAgAUEgahA0IAEoAswUKAKEAiECIAEoAsgUISIgASABQcgUajYCiAEgAUGMuQE2AoABIAEgAUGAAWoiADYCkAEgASABQcwUajYChAEgIkG8pwFqIAIgABCQAgJAAn8gACABKAKQASIDRgRAIAAhAyABKAKAAUEQagwBCyADRQ0BIAMoAgBBFGoLIQAgAyAAKAIAEQEACyABKALIFCIAQcCnAWooAgAhAiAAQcSnAWooAgAhBCAAKAK8pwEoAgAoApQQIQAgAUHAHzYCECABIAAgBCACa2q4RAAAAACAhC5BozkDGEEEQdnwACABQRBqEDQgASgCzBQoAoQCIQIgASgCyBQhIyABIAFByBRqNgKIASABQcS6ATYCgAEgASABQYABaiIANgKQASABIAFBzBRqNgKEASAjQdCnAWogAiAAEJACAkACfyAAIAEoApABIgNGBEAgACEDIAEoAoABQRBqDAELIANFDQEgAygCAEEUagshACADIAAoAgARAQALIAEoAsgUIgBB1KcBaigCACECIABB2KcBaigCACEEIAAoAtCnASgCACgClBAhACABQcAfNgIAIAEgACAEIAJrarhEAAAAAICELkGjOQMIQQRBk/AAIAEQNCABKALIFCIDKAKUpwEiAgRAIAEoAswUKAKEAiEAIAIoAgAoApQQIQIgAygClKcBEJEBIANBpKcBaiAAIAAoAggRAAAiACACIAAoAgARBAAiADYCACADIAAQyQE2ApSnASABKALIFCEDCyADKAKopwEiAARAIAEoAswUKAKEAiECIAAoAgAoApQQIQQgABCRASADQbinAWogAiACKAIIEQAAIgAgBCAAKAIAEQQAIgA2AgAgAyAAEMkBNgKopwEgASgCyBQhAwsgAygCvKcBIgAEQCABKALMFCgChAIhAiAAKAIAKAKUECEEIAAQkQEgA0HMpwFqIAIgAigCCBEAACIAIAQgACgCABEEACIANgIAIAMgABDJATYCvKcBIAEoAsgUIQMLIAMoAtCnASIARQ0AIAEoAswUKAKEAiECIAAoAgAoApQQIQQgABCRASADQeCnAWogAiACKAIIEQAAIgAgBCAAKAIAEQQAIgA2AgAgAyAAEMkBNgLQpwEgASgCyBQhAwsgAUHQFGokACADDAELEDYACyIANgKAAiAJIAANARogCRDgBAtBAAsiADYCACAAQQBHCwvqBQIEfwJ8IwBBIGsiBCQAIAQgADYCACAEQQA6ABggBEIANwMQIAQgAjYCDCAEIAE2AgggBCMDNgIEAnwgAwRAIwBBEGsiAyQAIAMgBDYCDCADQQA2AgggA0HIATYCBCADQQRqIQUjAEGAAWsiACQAIwBB4ABrIgEkAAJAQYTzNCgCAEECRgRAIwBBEGtBADYCDAwBCyMAQRBrIgIkAANAAkACQAJAAkACQEGE8zRBAEEB/kgCAA4EAAIBAwULIAJBBGoiBkGE8zQ2AgQgAkHKATYCBCACIwMiBygCRDYCDCAHIAY2AkQQmAQjAyACKAIMNgJEQYTzNEEC/kECAEEDRw0AQYTzNBD6AQsgAkEQaiQADAILQYTzNEEBQQP+SAIAGgtBhPM0QQBBAxChAQwBCwsLIAFBAEHQAPwL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QQAhBgNAIAEgICAEIA/9GwFqIgosAABBIGuylP0TIg39DPz////8/////P////z////9DAAAAAAAAAAAAAAAAAAAAAAgFCAH/Q8iDv1O/QwAAAAAAAAAAAAAAAAAAAAA/SP9hwH9pwH9UiAcIA/9GwAiAv2tAf0MAwAAAAMAAAADAAAAAwAAAP1O/VD9+gH95gH9CwIwIAEgDf0M/P////z////8/////P////0MAAAAAAAAAAAAAAAAAAAAACAVIA79Tv0MAAAAAAAAAAAAAAAAAAAAAP0j/YcB/acB/VIgHSAC/a0B/QwDAAAAAwAAAAMAAAADAAAA/U79UP36Af3mAf0LAiAgASAN/Qz8/////P////z////8/////QwAAAAAAAAAAAAAAAAAAAAAIBYgDv1O/QwAAAAAAAAAAAAAAAAAAAAA/SP9hwH9pwH9UiAeIAL9rQH9DAMAAAADAAAAAwAAAAMAAAD9Tv1Q/foB/eYB/QsCECABIA39DPz////8/////P////z////9DAAAAAAAAAAAAAAAAAAAAAAgFyAO/U79DAAAAAAAAAAAAAAAAAAAAAD9I/2HAf2nAf1SIB8gAv2tAf0MAwAAAAMAAAADAAAAAwAAAP1O/VD9+gH95gH9CwIAIAEgICAKLAABQSBrspT9EyIN/Qz8/////P////z////8/////QwAAAAAAAAAAAAAAAAAAAAAIBAgDv1O/QwAAAAAAAAAAAAAAAAAAAAA/SP9hwH9pwH9UiAYIAL9rQH9DAMAAAADAAAAAwAAAAMAAAD9Tv1Q/foB/eYB/QsCcCABIA39DPz////8/////P////z////9DAAAAAAAAAAAAAAAAAAAAAAgESAO/U79DAAAAAAAAAAAAAAAAAAAAAD9I/2HAf2nAf1SIBkgAv2tAf0MAwAAAAMAAAADAAAAAwAAAP1O/VD9+gH95gH9CwJgIAEgDf0M/P////z////8/////P////0MAAAAAAAAAAAAAAAAAAAAACASIA79Tv0MAAAAAAAAAAAAAAAAAAAAAP0j/YcB/acB/VIgGiAC/a0B/QwDAAAAAwAAAAMAAAADAAAA/U79UP36Af3mAf0LAlAgASAN/Qz8/////P////z////8/////QwAAAAAAAAAAAAAAAAAAAAAIBMgDv1O/QwAAAAAAAAAAAAA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JCACIANGIQkgAkEEaiECIAlFDQALDAELIAEgACgCCDYCKAsLjgcCBH8EeyACQR9KBEAgAkEgbSEGA0AgASAFQQd0aiIDQUBrIAAgBUEWbGoiBC8BAEECdEGQ1gRq/QkCACIHIAQoAQIiAkEMdv0RIAJBDXb9HAEgAkEOdv0cAiACQQ92/RwDIgn9DBAAAAAQAAAAEAAAABAAAAD9TiAE/VwABv2JAf2pASIIQQT9rQH9UP0M8P////D////w////8P////2uAf36Af3mAf0LAgAgAyAHIAJBBHT9ESACQQN0/RwBIAJBAnT9HAIgAkEBdP0cA/0MEAAAABAAAAAQAAAAEAAAAP1OIAj9DA8AAAAPAAAADwAAAA8AAAD9Tv1Q/Qzw////8P////D////w/////a4B/foB/eYB/QsCACADIAcgAkEQdv0RIAJBEXb9HAEgAkESdv0cAiACQRN2/RwD/QwQAAAAEAAAABAAAAAQAAAA/U4gBEEKav1cAAD9iQH9qQEiCEEE/a0B/VD9DPD////w////8P////D////9rgH9+gH95gH9CwJQIAMgByACQQR2/REgAkEFdv0cASACQQZ2/RwCIAJBB3b9HAMiCkEE/asB/QwQAAAAEAAAABAAAAAQAAAA/U4gCP0MDwAAAA8AAAAPAAAADwAAAP1O/VD9DPD////w////8P////D////9rgH9+gH95gH9CwIQIAMgByACQRR2/REgAkEVdv0cASACQRZ2/RwCIAJBF3b9HAP9DBAAAAAQAAAAEAAAABAAAAD9TiAEQQ5q/VwAAP2JAf2pASIIQQT9rQH9UP0M8P////D////w////8P////2uAf36Af3mAf0LAmAgAyAHIAr9DBAAAAAQAAAAEAAAABAAAAD9TiAI/QwPAAAADwAAAA8AAAAPAAAA/U79UP0M8P////D////w////8P////2uAf36Af3mAf0LAiAgAyAHIAJBGHb9ESACQRl2/RwBIAJBGnb9HAIgAkEbdv0cA/0MEAAAABAAAAAQAAAAEAAAAP1OIARBEmr9XAAA/YkB/akBIghBBP2tAf1Q/Qzw////8P////D////w/////a4B/foB/eYB/QsCcCADIAcgCUEE/asB/QwQAAAAEAAAABAAAAAQAAAA/U4gCP0MDwAAAA8AAAAPAAAADwAAAP1O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IAAoAgQ2AigPCwsgAUGfeDYCACABQQA2AigPCxD/AQALnQEBBH8CQAJAIAEoAhAgACgCFEEBa0EMbGoiAi0ACEUNASACKAIEIAIoAgAiBWsiAyABKAIMIAEoAggiBGtKDQFBACECIANBAEwNAANAIAIgBWotAAAgAiAEai0AAEcNAiADIAJBAWoiAkcNAAsMAAsgAUGeeDYCACABIAMgBGo2AgggASAAKAIENgIoDwsgAUGfeDYCACABQQA2AigLUgEBfyAAQcyvATYCACAAKAIIIgFBBGpBf/4eAgBFBEAgASABKAIAKAIIEQEACyAAQdypATYCACAAKAIEIgEEQCABIAEoAgAoAgQRAQALIAAQLwtQAQF/IABBzK8BNgIAIAAoAggiAUEEakF//h4CAEUEQCABIAEoAgAoAggRAQALIABB3KkBNgIAIAAoAgQiAQRAIAEgASgCACgCBBEBAAsgAAvPAQEEfwJAAkACQCABKAIQIAAoAhRBDGxqQQxrIgMtAAhFDQIgAygCBCADKAIAayIEIAEoAgwgASgCCCICa0oNAiAEQQBMDQFBACECA0AgACgCDCIFIAMoAgAgAmosAAAgBSgCACgCFBEEACAAKAIMIgUgASgCCCACaiwAACAFKAIAKAIUEQQARw0DIAQgAkEBaiICRw0ACwwACyABKAIIIQILIAFBnng2AgAgASACIARqNgIIIAEgACgCBDYCKA8LIAFBn3g2AgAgAUEANgIoC1IBAX8gAEH0rgE2AgAgACgCCCIBQQRqQX/+HgIARQRAIAEgASgCACgCCBEBAAsgAEHcqQE2AgAgACgCBCIBBEAgASABKAIAKAIEEQEACyAAEC8LUAEBfyAAQfSuATYCACAAKAIIIgFBBGpBf/4eAgBFBEAgASABKAIAKAIIEQEACyAAQdypATYCACAAKAIEIgEEQCABIAEoAgAoAgQRAQALIAALjQQCAnsDfyACQR9KBEAgAkEgbSEHA0AgASAFQQd0aiICQUBrIAAgBUESbGoiBi8BAEECdEGQ1gRq/QkCACIDIAb9XAAC/YkB/akBIgRBBP2tAf0M+P////j////4////+P////2uAf36Af3mAf0LAgAgAiADIAT9DA8AAAAPAAAADwAAAA8AAAD9Tv0M+P////j////4////+P////2uAf36Af3mAf0LAgAgAiADIAZBBmr9XAAA/YkB/akBIgRBBP2tAf0M+P////j////4////+P////2uAf36Af3mAf0LAlAgAiADIAT9DA8AAAAPAAAADwAAAA8AAAD9Tv0M+P////j////4////+P////2uAf36Af3mAf0LAhAgAiADIAZBCmr9XAAA/YkB/akBIgRBBP2tAf0M+P////j////4////+P////2uAf36Af3mAf0LAmAgAiADIAT9DA8AAAAPAAAADwAAAA8AAAD9Tv0M+P////j////4////+P////2uAf36Af3mAf0LAiAgAiADIAZBDmr9XAAA/YkB/akBIgRBBP2tAf0M+P////j////4////+P////2uAf36Af3mAf0LAnAgAiADIAT9DA8AAAAPAAAADwAAAA8AAAD9Tv0M+P////j////4////+P////2uAf36Af3mAf0LAjAgBUEBaiIFIAdHDQALCwtPAQF/AkAgASgCCCICIAEoAgxGDQAgAi0AACAALQAIRw0AIAFBnXg2AgAgASACQQFqNgIIIAEgACgCBDYCKA8LIAFBn3g2AgAgAUEANgIoC08BAX8CQCABKAIIIgIgASgCDEYNACACLQAAIAAtABRHDQAgAUGdeDYCACABIAJBAWo2AgggASAAKAIENgIoDwsgAUGfeDYCACABQQA2AigLUgEBfyAAQditATYCACAAKAIIIgFBBGpBf/4eAgBFBEAgASABKAIAKAIIEQEACyAAQdypATYCACAAKAIEIgEEQCABIAEoAgAoAgQRAQALIAAQLwtQAQF/IABB2K0BNgIAIAAoAggiAUEEakF//h4CAEUEQCABIAEoAgAoAggRAQALIABB3KkBNgIAIAAoAgQiAQRAIAEgASgCACgCBBEBAAsgAAtoAQJ/AkAgASgCCCICIAEoAgxGDQAgACgCDCIDIAIsAAAgAygCACgCFBEEAEH/AXEgAC0AFEcNACABQZ14NgIAIAEgASgCCEEBajYCCCABIAAoAgQ2AigPCyABQZ94NgIAIAFBADYCKAtSAQF/IABB/KwBNgIAIAAoAggiAUEEakF//h4CAEUEQCABIAEoAgAoAggRAQALIABB3KkBNgIAIAAoAgQiAQRAIAEgASgCACgCBBEBAAsgABAvC1ABAX8gAEH8rAE2AgAgACgCCCIBQQRqQX/+HgIARQRAIAEgASgCACgCCBEBAAsgAEHcqQE2AgAgACgCBCIBBEAgASABKAIAKAIEEQEACyAAC8wDAwV9BX8DeyACQSBOBEAjAEGAAWshCCACQSBtIQwDQCAIIAAgCkEHdGpBgAH8CgAAIAj9AAQA/eABIAj9AAQQ/eAB/ekBIAj9AAQg/eABIAj9AAQw/eAB/ekB/ekBIAj9AARA/eABIAj9AARQ/eAB/ekBIAj9AARg/eABIAj9AARw/eAB/ekB/ekB/ekBIg39HwAiAyAN/R8BIgQgAyAEXiICGyEFAkACQCAN/R8CIgYgDf0fAyIHXgRAIAMgBiAFIAZeIgkbIQMgAg0CIAkNAQwCCyADIAcgBSAHXiIJGyEDIAINASAJRQ0BCyAEIQMLIAEgCkEobGoiCyADQwAA/kKVIgM4AgBDAACAPyADlUMAAAAAIANDAAAAAFwb/RMhD/0MAAAAAAAAAAAAAAAAAAAAACENQQAhAgNAIAsgAkECdGoiCUEJaiAPIAggAkEEdGr9AAQA/eYB/fgBIg79WAAABCAJQQhqIA79WAAAACAJQQpqIA79WAAACCAJQQtqIA79WAAADCAOIA39rgEhDSACQQFqIgJBCEcNAAsgCyADIA39GwAgDf0bAWogDf0bAmogDf0bA2qylDgCBCAKQQFqIgogDEcNAAsLC+oQAwJ9BH8JeyACQSBOBEAgAkEgbSEIA0AgASAHQShsaiIGIAAgB0EHdGoiBSoCAIsiBCAFKgIEiyIDIAMgBF0bIgQgBSoCCIsiAyADIARdGyIEIAUqAgyLIgMgAyAEXRsiBCAFKgIQiyIDIAMgBF0bIgQgBSoCFIsiAyADIARdGyIEIAUqAhiLIgMgAyAEXRsiBCAFKgIciyIDIAMgBF0bIgQgBSoCIIsiAyADIARdGyIEIAUqAiSLIgMgAyAEXRsiBCAFKgIoiyIDIAMgBF0bIgQgBSoCLIsiAyADIARdGyIEIAUqAjCLIgMgAyAEXRsiBCAFKgI0iyIDIAMgBF0bIgQgBSoCOIsiAyADIARdGyIEIAUqAjyLIgMgAyAEXRsiBCAFQUBrIgIqAgCLIgMgAyAEXRsiBCAFKgJEiyIDIAMgBF0bIgQgBSoCSIsiAyADIARdGyIEIAUqAkyLIgMgAyAEXRsiBCAFKgJQiyIDIAMgBF0bIgQgBSoCVIsiAyADIARdGyIEIAUqAliLIgMgAyAEXRsiBCAFKgJciyIDIAMgBF0bIgQgBSoCYIsiAyADIARdGyIEIAUqAmSLIgMgAyAEXRsiBCAFKgJoiyIDIAMgBF0bIgQgBSoCbIsiAyADIARdGyIEIAUqAnCLIgMgAyAEXRsiBCAFKgJ0iyIDIAMgBF0bIgQgBSoCeIsiAyADIARdGyIEIAUqAnyLIgMgAyAEXRtDAAD+QpUiBDgCAAJ/QwAAgD8gBJVDAAAAACAEQwAAAABcG/0TIgogAv0AAgD95gEiCf0fARA1IgOLQwAAAE9dBEAgA6gMAQtBgICAgHgLIQICfyAJ/R8AEDUiA4tDAAAAT10EQCADqAwBC0GAgICAeAv9DyAC/RcBAn8gCf0fAhA1IgOLQwAAAE9dBEAgA6gMAQtBgICAgHgL/RcCIQsCfyAJ/R8DEDUiA4tDAAAAT10EQCADqAwBC0GAgICAeAshAiAF/QACACEJIAZBGGogCyAC/RcDIgv9WgAAAAJ/IAogCf3mASIJ/R8BEDUiA4tDAAAAT10EQCADqAwBC0GAgICAeAshAiAGAn8gCf0fABA1IgOLQwAAAE9dBEAgA6gMAQtBgICAgHgL/Q8gAv0XAQJ/IAn9HwIQNSIDi0MAAABPXQRAIAOoDAELQYCAgIB4C/0XAgJ/IAn9HwMQNSIDi0MAAABPXQRAIAOoDAELQYCAgIB4C/0XAyIP/VoACAACfyAKIAX9AAJQ/eYBIgn9HwEQNSIDi0MAAABPXQRAIAOoDAELQYCAgIB4CyECAn8gCf0fABA1IgOLQwAAAE9dBEAgA6gMAQtBgICAgHgL/Q8gAv0XAQJ/IAn9HwIQNSIDi0MAAABPXQRAIAOoDAELQYCAgIB4C/0XAiEMAn8gCf0fAxA1IgOLQwAAAE9dBEAgA6gMAQtBgICAgHgLIQIgBf0AAhAhCSAGQRxqIAwgAv0XAyIM/VoAAAACfyAKIAn95gEiCf0fARA1IgOLQwAAAE9dBEAgA6gMAQtBgICAgHgLIQIgBkEMagJ/IAn9HwAQNSIDi0MAAABPXQRAIAOoDAELQYCAgIB4C/0PIAL9FwECfyAJ/R8CEDUiA4tDAAAAT10EQCADqAwBC0GAgICAeAv9FwICfyAJ/R8DEDUiA4tDAAAAT10EQCADqAwBC0GAgICAeAv9FwMiEP1aAAAAAn8gCiAF/QACYP3mASIJ/R8BEDUiA4tDAAAAT10EQCADqAwBC0GAgICAeAshAgJ/IAn9HwAQNSIDi0MAAABPXQRAIAOoDAELQYCAgIB4C/0PIAL9FwECfyAJ/R8CEDUiA4tDAAAAT10EQCADqAwBC0GAgICAeAv9FwIhDQJ/IAn9HwMQNSIDi0MAAABPXQRAIAOoDAELQYCAgIB4CyECIAX9AAIgIQkgBkEgaiANIAL9FwMiDf1aAAAAAn8gCiAJ/eYBIgn9HwEQNSIDi0MAAABPXQRAIAOoDAELQYCAgIB4CyECIAZBEGoCfyAJ/R8AEDUiA4tDAAAAT10EQCADqAwBC0GAgICAeAv9DyAC/RcBAn8gCf0fAhA1IgOLQwAAAE9dBEAgA6gMAQtBgICAgHgL/RcCAn8gCf0fAxA1IgOLQwAAAE9dBEAgA6gMAQtBgICAgHgL/RcDIhH9WgAAAAJ/IAogBf0AAnD95gEiCf0fARA1IgOLQwAAAE9dBEAgA6gMAQtBgICAgHgLIQICfyAJ/R8AEDUiA4tDAAAAT10EQCADqAwBC0GAgICAeAv9DyAC/RcBAn8gCf0fAhA1IgOLQwAAAE9dBEAgA6gMAQtBgICAgHgL/RcCIQ4CfyAJ/R8DEDUiA4tDAAAAT10EQCADqAwBC0GAgICAeAshAiAF/QACMCEJIAZBJGogDiAC/RcDIg79WgAAAAJ/IAogCf3mASIK/R8BEDUiA4tDAAAAT10EQCADqAwBC0GAgICAeAshAiAGQRRqAn8gCv0fABA1IgOLQwAAAE9dBEAgA6gMAQtBgICAgHgL/Q8gAv0XAQJ/IAr9HwIQNSIDi0MAAABPXQRAIAOoDAELQYCAgIB4C/0XAgJ/IAr9HwMQNSIDi0MAAABPXQRAIAOoDAELQYCAgIB4C/0XAyIK/VoAAAAgBiAEIA/9hwH9pwEgC/2HAf2nAf2uASAQ/YcB/acB/a4BIAz9hwH9pwH9rgEgEf2HAf2nAf2uASAN/YcB/acB/a4BIAr9hwH9pwH9rgEgDv2HAf2nAf2uASIKIAogCv0NCAkKCwwNDg8AAQIDAAECA/2uASIKIAogCv0NBAUGBwABAgMAAQIDAAECA/2uAf0bALKUOAIEIAdBAWoiByAIRw0ACwsLrgQBCn8jAEFAaiICJAAgAkEAOgAoIAJBADoANCACQQA2AhAgAkIANwMgIAJCADcCLCACQQA2AjwgAkEAOgA4IAJCADcDCCAAKAIYIQUgASgCCCEDIAEoAgwhBCACQQA6ABwgAiAENgIYIAIgBDYCFCACQQhqIgQgBUEBaiACQRRqEIICIAJBADoAKCACIAM2AiQgAiACLQAcOgA0IAIgAzYCICACIAM2AjwgAkEBOgA4IAIgAikCFDcCLAJAAkACQCAAQQhqIAEoAggiAyABKAIMIAQgASgCLEG/H3FBwAByIAMgASgCBEYgAS0AMEEAR3EQ3wIgAC0ANEcEQCABQZ54NgIAIAEgACgCBDYCKCACKAIMIAIoAggiA2siBkEMbSIFQQJJDQFBASEEIAVBAWsiB0EBcSELIAEoAhAhBSAAKAIwIQggBkEYRwRAIAdBfnEhCkEAIQYDQCAFIAQgCGpBDGxqIgFBDGsiByADIARBDGxqIgAoAgA2AgAgByAAKAIENgIEIAcgAC0ACDoACCABIAAoAgw2AgAgASAAKAIQNgIEIAEgAC0AFDoACCAEQQJqIQQgBkECaiIGIApHDQALCyALRQ0CIAQgCGpBDGwgBWpBDGsiACADIARBDGxqIgEoAgA2AgAgACABKAIENgIEIAAgAS0ACDoACAwCCyABQQA2AiggAUGfeDYCACACKAIIIQMLIANFDQELIAIgAzYCDCADEC8LIAJBQGskAAulAQEEfyAAQdyrATYCACAAQQhqIQQCQCAAKAIoIgFFDQAgAUF//h4CBA0AIAEgASgCACgCCBEBAAJAIAFBCGoiA/4QAgAEQCADQX/+HgIADQELIAEgASgCACgCEBEBAAsLIAQoAgAiAUEEakF//h4CAEUEQCABIAEoAgAoAggRAQALIABB3KkBNgIAIAAoAgQiAQRAIAEgASgCACgCBBEBAAsgABAvC6MBAQR/IABB3KsBNgIAIABBCGohBAJAIAAoAigiAUUNACABQX/+HgIEDQAgASABKAIAKAIIEQEAAkAgAUEIaiID/hACAARAIANBf/4eAgANAQsgASABKAIAKAIQEQEACwsgBCgCACIBQQRqQX/+HgIARQRAIAEgASgCACgCCBEBAAsgAEHcqQE2AgAgACgCBCIBBEAgASABKAIAKAIEEQEACyAAC+sCAQZ/AkAgASgCBCIDIAEoAgwiBEYNACAEIAEoAggiAkYEQEEAIQIgAS0ALEEIcQ0BQQEhAiAEQQFrLQAAIgNB3wBGDQEgA8BBAE4EQCAAKAIMKAIIIANBAnRqLQAAQeAAcQ0CC0EAIQIMAQsCQCACIANHDQAgASgCLCIEQYABcQ0AQQAhAiAEQQRxDQFBASECIAMtAAAiA0HfAEYNASADwEEATgRAIAAoAgwoAgggA0ECdGotAABB4ABxDQILQQAhAgwBCyACLQAAIgbAIQRBASEFAn9BASACQQFrLQAAIgJB3wBGDQAaIALAQQBOBEBBASAAKAIMKAIIIAJBAnRqLQAAQeAAcQ0BGgtBAAshBwJAIARB3wBGDQAgBEEATgRAIAAoAgwoAgggBkECdGotAABB4ABxDQELQQAhBQsgByAFRyECCyABQQAgACgCBCAALQAUIAJGIgAbNgIoIAFBn3hBnnggABs2AgALUgEBfyAAQYSrATYCACAAKAIIIgFBBGpBf/4eAgBFBEAgASABKAIAKAIIEQEACyAAQdypATYCACAAKAIEIgEEQCABIAEoAgAoAgQRAQALIAAQLwtQAQF/IABBhKsBNgIAIAAoAggiAUEEakF//h4CAEUEQCABIAEoAgAoAggRAQALIABB3KkBNgIAIAAoAgQiAQRAIAEgASgCACgCBBEBAAsgAAtmAQF/AkACQCABKAIIIgIgASgCDEcNACABLQAsQQJxDQAMAQsCQCAALQAIRQ0AAkAgAi0AAEEKaw4EAAEBAAELDAELIAFBn3g2AgAgAUEANgIoDwsgAUGeeDYCACABIAAoAgQ2AigL8wMDBX0GfwJ7IAJBIE4EQCMAQYABayEIIAJBIG0hDANAIAggACAKQQd0akGAAfwKAAAgCP0ABAD94AEgCP0ABBD94AH96QEgCP0ABCD94AEgCP0ABDD94AH96QH96QEgCP0ABED94AEgCP0ABFD94AH96QEgCP0ABGD94AEgCP0ABHD94AH96QH96QH96QEiDv0fACIDIA79HwEiBCADIAReIgIbIQUCQAJAIA79HwIiBiAO/R8DIgdeBEAgAyAGIAUgBl4iCRshAyACDQIgCQ0BDAILIAMgByAFIAdeIgkbIQMgAg0BIAlFDQELIAQhAwsgASAKQSJsaiINQYD8ASADQwAA/kKVIgOLQwAAgHeUQwAAgAiUQYCAgIgHIAO8IgJBAXQiCUGAgIB4cSILIAtBgICAiAdNG0EBdkGAgIA8ar6SvCILQQ12QYD4AXEgC0H/H3FqIAlBgICAeEsbIAJBEHZBgIACcXI7AQBDAACAPyADlUMAAAAAIANDAAAAAFwb/RMhD0EAIQIDQCANIAJBAnRqIglBA2ogDyAIIAJBBHRq/QAEAP3mAf34ASIO/VgAAAQgCUECaiAO/VgAAAAgCUEEaiAO/VgAAAggCUEFaiAO/VgAAAwgAkEBaiICQQhHDQALIApBAWoiCiAMRw0ACwsLbQACQAJAIAEtADAEQCABKAIIIAEoAgRHDQEgAS0ALEEBcQ0BDAILIAAtAAhFDQACQCABKAIIQQFrLQAAQQprDgQAAQEAAQsMAQsgAUGfeDYCACABQQA2AigPCyABQZ54NgIAIAEgACgCBDYCKAukDwMCfQd/AnsgAkEgTgRAIAJBIG0hCQNAIAEgBkEibGoiCEGA/AEgACAGQQd0aiIFKgIAiyIDIAUqAgSLIgQgAyAEXhsiAyAFKgIIiyIEIAMgBF4bIgMgBSoCDIsiBCADIAReGyIDIAUqAhCLIgQgAyAEXhsiAyAFKgIUiyIEIAMgBF4bIgMgBSoCGIsiBCADIAReGyIDIAUqAhyLIgQgAyAEXhsiAyAFKgIgiyIEIAMgBF4bIgMgBSoCJIsiBCADIAReGyIDIAUqAiiLIgQgAyAEXhsiAyAFKgIsiyIEIAMgBF4bIgMgBSoCMIsiBCADIAReGyIDIAUqAjSLIgQgAyAEXhsiAyAFKgI4iyIEIAMgBF4bIgMgBSoCPIsiBCADIAReGyIDIAVBQGsiCioCAIsiBCADIAReGyIDIAUqAkSLIgQgAyAEXhsiAyAFKgJIiyIEIAMgBF4bIgMgBSoCTIsiBCADIAReGyIDIAUqAlCLIgQgAyAEXhsiAyAFKgJUiyIEIAMgBF4bIgMgBSoCWIsiBCADIAReGyIDIAUqAlyLIgQgAyAEXhsiAyAFKgJgiyIEIAMgBF4bIgMgBSoCZIsiBCADIAReGyIDIAUqAmiLIgQgAyAEXhsiAyAFKgJsiyIEIAMgBF4bIgMgBSoCcIsiBCADIAReGyIDIAUqAnSLIgQgAyAEXhsiAyAFKgJ4iyIEIAMgBF4bIgMgBSoCfIsiBCADIAReG0MAAP5ClSIDi0MAAIB3lEMAAIAIlEGAgICIByADvCICQQF0IgtBgICAeHEiByAHQYCAgIgHTRtBAXZBgICAPGq+krwiB0ENdkGA+AFxIAdB/x9xaiALQYCAgHhLGyACQRB2QYCAAnFyOwEAAn9DAACAPyADlUMAAAAAIANDAAAAAFwb/RMiDSAF/QACAP3mASIM/R8BEDUiA4tDAAAAT10EQCADqAwBC0GAgICAeAshAiAIAn8gDP0fABA1IgOLQwAAAE9dBEAgA6gMAQtBgICAgHgL/Q8gAv0XAQJ/IAz9HwIQNSIDi0MAAABPXQRAIAOoDAELQYCAgIB4C/0XAgJ/IAz9HwMQNSIDi0MAAABPXQRAIAOoDAELQYCAgIB4C/0XAwJ/IA0gBf0AAhD95gEiDP0fABA1IgOLQwAAAE9dBEAgA6gMAQtBgICAgHgL/RcEAn8gDP0fARA1IgOLQwAAAE9dBEAgA6gMAQtBgICAgHgL/RcFAn8gDP0fAhA1IgOLQwAAAE9dBEAgA6gMAQtBgICAgHgL/RcGAn8gDP0fAxA1IgOLQwAAAE9dBEAgA6gMAQtBgICAgHgL/RcHAn8gDSAF/QACIP3mASIM/R8AEDUiA4tDAAAAT10EQCADqAwBC0GAgICAeAv9FwgCfyAM/R8BEDUiA4tDAAAAT10EQCADqAwBC0GAgICAeAv9FwkCfyAM/R8CEDUiA4tDAAAAT10EQCADqAwBC0GAgICAeAv9FwoCfyAM/R8DEDUiA4tDAAAAT10EQCADqAwBC0GAgICAeAv9FwsCfyANIAX9AAIw/eYBIgz9HwAQNSIDi0MAAABPXQRAIAOoDAELQYCAgIB4C/0XDAJ/IAz9HwEQNSIDi0MAAABPXQRAIAOoDAELQYCAgIB4C/0XDQJ/IAz9HwIQNSIDi0MAAABPXQRAIAOoDAELQYCAgIB4C/0XDgJ/IAz9HwMQNSIDi0MAAABPXQRAIAOoDAELQYCAgIB4C/0XD/0LAAICfyANIAr9AAIA/eYBIgz9HwEQNSIDi0MAAABPXQRAIAOoDAELQYCAgIB4CyECIAgCfyAM/R8AEDUiA4tDAAAAT10EQCADqAwBC0GAgICAeAv9DyAC/RcBAn8gDP0fAhA1IgOLQwAAAE9dBEAgA6gMAQtBgICAgHgL/RcCAn8gDP0fAxA1IgOLQwAAAE9dBEAgA6gMAQtBgICAgHgL/RcDAn8gDSAF/QACUP3mASIM/R8AEDUiA4tDAAAAT10EQCADqAwBC0GAgICAeAv9FwQCfyAM/R8BEDUiA4tDAAAAT10EQCADqAwBC0GAgICAeAv9FwUCfyAM/R8CEDUiA4tDAAAAT10EQCADqAwBC0GAgICAeAv9FwYCfyAM/R8DEDUiA4tDAAAAT10EQCADqAwBC0GAgICAeAv9FwcCfyANIAX9AAJg/eYBIgz9HwAQNSIDi0MAAABPXQRAIAOoDAELQYCAgIB4C/0XCAJ/IAz9HwEQNSIDi0MAAABPXQRAIAOoDAELQYCAgIB4C/0XCQJ/IAz9HwIQNSIDi0MAAABPXQRAIAOoDAELQYCAgIB4C/0XCgJ/IAz9HwMQNSIDi0MAAABPXQRAIAOoDAELQYCAgIB4C/0XCwJ/IA0gBf0AAnD95gEiDf0fABA1IgOLQwAAAE9dBEAgA6gMAQtBgICAgHgL/RcMAn8gDf0fARA1IgOLQwAAAE9dBEAgA6gMAQtBgICAgHgL/RcNAn8gDf0fAhA1IgOLQwAAAE9dBEAgA6gMAQtBgICAgHgL/RcOAn8gDf0fAxA1IgOLQwAAAE9dBEAgA6gMAQtBgICAgHgL/RcP/QsAEiAGQQFqIgYgCUcNAAsLCxQAIABBDGpBACABKAIEQeynAUYbCxkAIAAoAgwiAARAIAAgACgCACgCBBEBAAsLAgALCgAgAUGYeDYCAAsLACAAIAEgAhDMBAuLAQEDfxCbAiEDIAAoAgAhAiAAQQA2AgAgAygCACACELIBIAAoAgggACgCDCAAQRBqIAAoAiAgACgCKCAAKAIsIAAoAjAgACgCNCAAKAI4IAAoAgQRKQAgACgCECIBBEAgACABNgIUIAEQLwsgACgCACEBIABBADYCACABBEAgARDdARAvCyAAEC9BAAtBAEHtC0ECQeT5AEG0+gBBAUECQQAQBkGzI0EBQbj6AEG8+gBBA0EEQQAQBkHXC0EEQcD6AEHs+gBBBUEGQQAQBgsWACABQbjDAigCACIAEKsEGiAAEDAaCwsAIAAgASACENUEC+UBAgd/AX0jAEHAAWsiAyQAEJsCIQYgACgCACECIABBADYCACAGKAIAIAIQsgEgACgCBEEB/h4CACIBIAAoAggoAgBIBEADQAJAIAAoAgwoAgAiAiABQdgUbGoiAS0AsAINACABLQCxAg0AIAAoAhgqAgAhCCAAKAIQKAIAIQcgA0EMaiIFIAAoAhRBtAH8CgAAIAcgAiABQdABaiAFIAgQigILIAAoAgRBAf4eAgAiASAAKAIIKAIASA0ACwsgACgCACEBIABBADYCACABBEAgARDdARAvCyAAEC8gA0HAAWokAEEAC0kBA38QmwIhAyAAKAIAIQIgAEEANgIAIAMoAgAgAhCyASAAQQRqEIkCIAAoAgAhASAAQQA2AgAgAQRAIAEQ3QEQLwsgABAvQQALXwECf0Hg1zQoAgAiAARAQeTXNCgCACICIAAiAUcEQANAIAJBDGshASACQQFrLAAAQQBIBEAgASgCABAvCyABIgIgAEcNAAtB4Nc0KAIAIQELQeTXNCAANgIAIAEQLwsLGABB29c0LAAAQQBIBEBB0Nc0KAIAEC8LCwsAIAAgASACEOEEC9QBAQN/IwBBEGsiBCQAIABBADYCBCMAQRBrIgUkACAEQQA6AA8gACAAKAIAQQxrKAIAaiEDAkAgAygCEEUEQCADKAJIBEAgAygCSBD+AwsgBCAAIAAoAgBBDGsoAgBqKAIQRToADwwBCyADQQQQ8wELIAVBEGokAEEEIQMgBC0ADwRAIAAgACAAKAIAQQxrKAIAaigCGCIDIAEgAiADKAIAKAIgEQMAIgE2AgRBBkEAIAEgAkcbIQMLIAAgACgCAEEMaygCAGogAxDzASAEQRBqJAAgAgsZACAAIAAoAgBBDGsoAgBqLQAQQQJxQQF2CykAIABBCGoQ9wNFBEAgACAAKAIAQQxrKAIAaiIAIAAoAhBBBHIQuwILCwsAIAAgASACEOQECxAAQcTXNEHI1zQoAgAQ8wILEABBuNc0QbzXNCgCABD0AgsEAEEBC2gBA38jAEEgayICJAAgAkEMaiABIAAoAjQiACgCABD4AiAAKAIEIQMgAigCDCIEIAAoAghLBEAgACADIAQQxAEiAzYCBCAAIAIoAgw2AggLIAIgAzYCECABIAJBDGoQ9wIgAkEgaiQACwwAIAFBGGogARD3AgsNACABKAIEEC8gARAvC4oBAQJ/IwBBIGsiAiQAIAAoAjQhA0HQABA7IQAgAkEMaiABIAMoAgAQ+AIgACACKAIcNgIQIAAgAv0AAgz9CwIAIAAgASkDMDcDSCAAIAH9AAMg/QsDOCAAIAH9AAMQ/QsDKCAAIAH9AAMA/QsDGCAAKAIAIgEEQCAAIAEQOzYCBAsgAkEgaiQAIAALBgBBuLgDCxgBAX8gACgCNCIBKAIEEC8gARAvIAAQLwsTACACIAEoAtABQQAgARBbEJcBCxMAIAEgAigC0AFBACABEFsQ5gQLqgEAIwBBIGsiACQAAkAgARBbIAMgBGpJBEBBvMMCKAIAEDAaIABBit4ANgIIIABBigM2AgQgAEGbJzYCAEG4wwIoAgBBy+QAIAAQMQwBCyABKALQASIBRQRAQbzDAigCABAwGiAAQZzfADYCGCAAQYsDNgIUIABBmyc2AhBBuMMCKAIAQcvkACAAQRBqEDEMAQsgAiABIANqIAT8CgAAIABBIGokAA8LEAAAC6oBACMAQSBrIgAkAAJAIAEQWyADIARqSQRAQbzDAigCABAwGiAAQcXdADYCCCAAQYEDNgIEIABBmyc2AgBBuMMCKAIAQcvkACAAEDEMAQsgASgC0AEiAUUEQEG8wwIoAgAQMBogAEGc3wA2AhggAEGCAzYCFCAAQZsnNgIQQbjDAigCAEHL5AAgAEEQahAxDAELIAEgA2ogAiAE/AoAACAAQSBqJAAPCxAAAAsFAEGoKwsKACABKAIAQTxGCwUAQcAACwkAIAAoAiAQLwsHACAAKAIgC5sBAQN/IwBBEGsiAiQAIAFBQGsiAxA7IgRFBEBBvMMCKAIAEDAaIAJBzt4ANgIIIAJBuQM2AgQgAkGbJzYCAEG4wwIoAgBBy+QAIAIQMRAAAAtBKBA7IgFBOjYCBCABQTs2AgAgASADNgIkIAEgBDYCICABIAA2AhwgAUH8pAH9AAIA/QsCCCABQYylASgCADYCGCACQRBqJAAgAQuoBgACQAJAAkBBsJk1QQBBAf5IAgAOAgABAgtBgAgkAUGACP0MAAAAAAAAAAAAAAAAAAAAAP0LAABBkAhBAEGSlAH8CAAAQaKcAUEAQR78CwBBwJwBQQBBBvwIAQBBxpwBQQBBHvwLAEHknAFBAEH1AvwIAgBB2Z8BQQBBG/wLAEH0nwFBAEEJ/AgDAEH9nwFBAEEb/AsAQZigAUEAQQn8CAQAQaGgAUEAQR/8CwBBwKABQQBBmJMB/AgFAEHYswJBAEEe/AsAQfazAkEAQQL8CAYAQfizAkEAQR38CwBBlbQCQQBBnRD8CAcAQbLEAkEAQRn8CwBBy8QCQQBBIfwICABB7MQCQQBBGfwLAEGFxQJBAEEh/AgJAEGmxQJBAEEZ/AsAQb/FAkEAQSr8CAoAQenFAkEAQRn8CwBBgsYCQQBBDvwICwBBkMYCQQBBI/wLAEGzxgJBAEEh/AgMAEHUxgJBAEEZ/AsAQe3GAkEAQbYK/AgNAEGj0QJBAEEt/AsAQdDRAkEAQQL8CA4AQdLRAkEAQR78CwBB8NECQQBBygD8CA8AQbrSAkEAQYoE/AsAQcTWAkEAQfkD/AgQAEG92gJBAEGDBPwLAEHA3gJBAEEC/AgRAEHC3gJBAEGSBPwLAEHU4gJBAEH5A/wIEgBBzeYCQQBBgwT8CwBB0OoCQQBBvQb8CBMAQY3xAkEAQYcE/AsAQZT1AkEAQec4/AgUAEH7rQNBAEEf/AsAQZquA0EAQfgA/AgVAEGSrwNBAEHkAPwLAEH2rwNBAEG+CPwIFgBBuLgDQQBBFfwIFwBBzbgDQQBBN/wLAEGEuQNBAEHoAPwIGABB7LkDQQBBPPwLAEGougNBAEHZAPwIGQBBgbsDQQBBP/wLAEHAuwNBAEEM/AgaAEHQuwNBAEHg3TH8CwBBsJk1QQL+FwIAQbCZNUF//gACABoMAQtBsJk1QQFCf/4BAgAaC/wJAPwJAfwJAvwJA/wJBPwJBfwJBvwJB/wJCPwJCfwJCvwJC/wJDPwJDfwJDvwJD/wJEPwJEfwJEvwJE/wJFPwJFfwJFvwJF/wJGPwJGfwJGgsL5pkDGwGSlAHima8A4pmuAOKZrQDimawA4pmrAOKZquKZquKZqgDimakA44CPAOOAjgDjgI0A44CMAHoAaW5maW5pdHkAaW5zdWZmaWNpZW50IG1lbW9yeQBGZWJydWFyeQBKYW51YXJ5AEp1bHkAbWFsYXkAVGh1cnNkYXkAVHVlc2RheQBXZWRuZXNkYXkAU2F0dXJkYXkAU3VuZGF5AE1vbmRheQBGcmlkYXkATWF5ACVtLyVkLyV5ACEha3Zfc2VsZi5jdHgAc2l4ACVzIGZhaWxlZCB0byByZWxlYXNlIG11dGV4ACVzIGZhaWxlZCB0byBhY3F1aXJlIG11dGV4AGNpcmN1bWZsZXgALSsgICAwWDB4AC0wWCswWCAwWC0weCsweCAweABoZWJyZXcAaGF3AE5vdgBlbWJkX2NvbnYAVGh1AHRlbHVndQBBdWd1c3QAJXMgZmFpbGVkIHRvIGJyb2FkY2FzdAB1bnNpZ25lZCBzaG9ydABhbGVydAB3aGlzcGVyX2t2X2NhY2hlX2ZpbmRfc2xvdABwcmludAB1bnNpZ25lZCBpbnQAY2lyY3VtZmxleC1hY2NlbnQAZ3JhdmUtYWNjZW50AGZ1bGxfZGVmYXVsdABrdl9jYWNoZV9pbml0AHhkaWdpdAAuY3Jvc3NfYXR0bi5xdWVyeS53ZWlnaHQALmF0dG4ucXVlcnkud2VpZ2h0AC5jcm9zc19hdHRuLmtleS53ZWlnaHQALmF0dG4ua2V5LndlaWdodAAuY3Jvc3NfYXR0bi5vdXQud2VpZ2h0AC5hdHRuLm91dC53ZWlnaHQAZW5jb2Rlci5sbl9wb3N0LndlaWdodAAubWxwX2xuLndlaWdodAAuY3Jvc3NfYXR0bl9sbi53ZWlnaHQALmF0dG5fbG4ud2VpZ2h0AGRlY29kZXIubG4ud2VpZ2h0AGRlY29kZXIudG9rZW5fZW1iZWRkaW5nLndlaWdodAAuY3Jvc3NfYXR0bi52YWx1ZS53ZWlnaHQALmF0dG4udmFsdWUud2VpZ2h0AGVuY29kZXIuY29udjIud2VpZ2h0AC5tbHAuMi53ZWlnaHQAZW5jb2Rlci5jb252MS53ZWlnaHQALm1scC4wLndlaWdodABzZXQAcmlnaHQtY3VybHktYnJhY2tldABsZWZ0LWN1cmx5LWJyYWNrZXQAcmlnaHQtc3F1YXJlLWJyYWNrZXQAbGVmdC1zcXVhcmUtYnJhY2tldABwdW5jdABnZ21sX25ld19vYmplY3QAT2N0AGZsb2F0AFNhdABjb21tZXJjaWFsLWF0AHVpbnQ2NF90AGh5cGhlbi1taW51cwByZXZlcnNlLXNvbGlkdXMAd2hpc3Blcl9leHBfY29tcHV0ZV90b2tlbl9sZXZlbF90aW1lc3RhbXBzAGFmcmlrYWFucwBwYXJhbXMAZ2dtbF9nZXRfbl90YXNrcwByaWdodC1wYXJlbnRoZXNpcwBsZWZ0LXBhcmVudGhlc2lzAHdoaXNwZXJfcHJpbnRfdGltaW5ncwAuY3Jvc3NfYXR0bi5xdWVyeS5iaWFzAC5hdHRuLnF1ZXJ5LmJpYXMALmNyb3NzX2F0dG4ub3V0LmJpYXMALmF0dG4ub3V0LmJpYXMAZW5jb2Rlci5sbl9wb3N0LmJpYXMALm1scF9sbi5iaWFzAC5jcm9zc19hdHRuX2xuLmJpYXMALmF0dG5fbG4uYmlhcwBkZWNvZGVyLmxuLmJpYXMALmNyb3NzX2F0dG4udmFsdWUuYmlhcwAuYXR0bi52YWx1ZS5iaWFzAGVuY29kZXIuY29udjIuYmlhcwAubWxwLjIuYmlhcwBlbmNvZGVyLmNvbnYxLmJpYXMALm1scC4wLmJpYXMAcm93X2lkID49IDAgJiYgcm93X2lkIDwgbl9hcwAlcwBmb3VyAHdoaXNwZXJfbGFuZ19zdHIAQXByAGNvbnN0cnVjdG9yAHZlY3RvcgBjZ3JhcGgtPm5vZGVzW2NncmFwaC0+bl9ub2RlcyAtIDFdID09IHRlbnNvcgB1bmtub3duIGFsbG9jYXRpb24gZXJyb3IAYmFzaGtpcgBsb3dlcgB1cHBlcgBraG1lcgBidWZmZXIAT2N0b2JlcgBOb3ZlbWJlcgBTZXB0ZW1iZXIARGVjZW1iZXIAdGF0YXIAbXlhbm1hcgB1bnNpZ25lZCBjaGFyAGlvc19iYXNlOjpjbGVhcgBNYXIAcQAvVXNlcnMvZ2dlcmdhbm92L2RldmVsb3BtZW50L2dpdGh1Yi93aGlzcGVyLmNwcC93aGlzcGVyLmNwcABmdWxsLXN0b3AAU2VwACVJOiVNOiVTICVwAHR3bwBhdXRvAHBhc2h0bwB6ZXJvAGxhbwB1bmtub3duAFN1bgBKdW4AY2FycmlhZ2UtcmV0dXJuAGJyZXRvbgBzZW1pY29sb24Ac3RkOjpleGNlcHRpb24AX19jeGFfZ3VhcmRfYWNxdWlyZSBkZXRlY3RlZCByZWN1cnNpdmUgaW5pdGlhbGl6YXRpb24ATW9uAGxhdGluAHBlcmNlbnQtc2lnbgBwbHVzLXNpZ24AZXF1YWxzLXNpZ24AbnVtYmVyLXNpZ24AZG9sbGFyLXNpZ24AbGVzcy10aGFuLXNpZ24AZ3JlYXRlci10aGFuLXNpZ24Ac2V2ZW4AdHVya21lbgBoeXBoZW4Ab2NjaXRhbgB0aWJldGFuAG5hbgBnZXJtYW4AY3BsYW4AY2F0YWxhbgBsYXR2aWFuAGJlbGFydXNpYW4AcnVzc2lhbgBwZXJzaWFuAGluZG9uZXNpYW4AaHVuZ2FyaWFuAGJ1bGdhcmlhbgBib3NuaWFuAG1hY2Vkb25pYW4AdWtyYWluaWFuAHNsb3ZlbmlhbgBsaXRodWFuaWFuAG1vbmdvbGlhbgBpdGFsaWFuAG5vcndlZ2lhbgBzZXJiaWFuAGtvcmVhbgBKYW4AYWxudW0AbWVkaXVtAG1hbGF5YWxhbQBKdWwAY250cmwAZ2dtbF9uZXdfdGVuc29yX2ltcGwAYm9vbABzbWFsbABzdGQ6OmJhZF9mdW5jdGlvbl9jYWxsAEFwcmlsAHRhbWlsAGVtc2NyaXB0ZW46OnZhbABueW5vcnNrAGFzdGVyaXNrAHF1ZXN0aW9uLW1hcmsAcXVvdGF0aW9uLW1hcmsAZXhjbGFtYXRpb24tbWFyawBibGFuawB0YWppawBncmVlawB1emJlawBzbG92YWsAagBtYW9yaQBGcmkAYXplcmJhaWphbmkAc3dhaGlsaQBuZXBhbGkAc29tYWxpAGJlbmdhbGkAbWFyYXRoaQBzaW5kaGkAaGluZGkAcHVuamFiaQBiYWRfYXJyYXlfbmV3X2xlbmd0aAB3ZWxzaABmaW5uaXNoAHNwYW5pc2gAZGFuaXNoAHBvbGlzaABlbmdsaXNoAHR1cmtpc2gAbHV4ZW1ib3VyZ2lzaABzd2VkaXNoAHlpZGRpc2gAYmFja3NsYXNoAGdyYXBoAGthemFraABkdXRjaABNYXJjaABmcmVuY2gAY3plY2gAQXVnAHRhZ2Fsb2cAdW5zaWduZWQgbG9uZwB0ZXJtaW5hdGluZwBzdGQ6OndzdHJpbmcAYmFzaWNfc3RyaW5nAHN0ZDo6c3RyaW5nAHN0ZDo6dTE2c3RyaW5nAHN0ZDo6dTMyc3RyaW5nAGVuY29kZXIucG9zaXRpb25hbF9lbWJlZGRpbmcAZGVjb2Rlci5wb3NpdGlvbmFsX2VtYmVkZGluZwBpbmYAJS4wTGYAJUxmAGNncmFwaC0+bl9sZWFmcyA8IGNncmFwaC0+c2l6ZQBjZ3JhcGgtPm5fbm9kZXMgPCBjZ3JhcGgtPnNpemUAd2hpc3Blcl90b2tlbml6ZQBmaXZlAHl1ZQB0cnVlAGJhc3F1ZQBpbnZhbGlkIGFsaWdubWVudCB2YWx1ZQBUdWUAd2hpc3Blcl9pbml0X3N0YXRlAHdoaXNwZXJfaW5pdF93aXRoX3BhcmFtc19ub19zdGF0ZQB3aGlzcGVyX2luaXRfZnJvbV9maWxlX3dpdGhfcGFyYW1zX25vX3N0YXRlAHdoaXNwZXJfbGFuZ19hdXRvX2RldGVjdF93aXRoX3N0YXRlAHdoaXNwZXJfZnVsbF93aXRoX3N0YXRlAHdoaXNwZXJfZW5jb2RlX3dpdGhfc3RhdGUAd2hpc3Blcl9kZWNvZGVfd2l0aF9zdGF0ZQB0cmFuc2xhdGUAZmFsc2UAcG9ydHVndWVzZQBtYWx0ZXNlAGZhcm9lc2UAY2FudG9uZXNlAGNoaW5lc2UAc3VuZGFuZXNlAHZpZXRuYW1lc2UAX19jeGFfZ3VhcmRfcmVsZWFzZQB1bmRlcnNjb3JlAF9fY3hhX2d1YXJkX2FjcXVpcmUAc3JjMC0+dHlwZSA9PSBkc3QtPnR5cGUAZHN0LT50eXBlID09IHNyYzAtPnR5cGUAVW5rbm93biBlcnJvciB0eXBlAEp1bmUAb25lAG5pbmUAbmV3bGluZQBsb3ctbGluZQB2ZXJ0aWNhbC1saW5lAGhhaXRpYW4gY3Jlb2xlAGRvdWJsZQBhcG9zdHJvcGhlAGxhcmdlAHRocmVlAGZyZWUAdGlsZGUAcmlnaHQtYnJhY2UAbGVmdC1icmFjZQBiYWNrc3BhY2UAdHJhbnNjcmliZQBwZXJpb2QAbWFwOjphdDogIGtleSBub3QgZm91bmQAYW1wZXJzYW5kAHZvaWQAd2hpc3Blcl9sYW5nX2lkAHRlcm1pbmF0ZV9oYW5kbGVyIHVuZXhwZWN0ZWRseSByZXR1cm5lZAB0aHJlYWQgY29uc3RydWN0b3IgZmFpbGVkAF9fdGhyZWFkX3NwZWNpZmljX3B0ciBjb25zdHJ1Y3Rpb24gZmFpbGVkAHRocmVhZDo6am9pbiBmYWlsZWQAbXV0ZXggbG9jayBmYWlsZWQAZm9ybS1mZWVkAFdlZAB3aGlzcGVyX21vZGVsX2xvYWQAJTAyZDolMDJkOiUwMmQlcyUwM2QAbGVhZl8lZABub2RlXyVkAGdnbWxfYWxpZ25lZF9tYWxsb2MAZ2dtbF90YWxsb2NyX2FsbG9jAHN0ZDo6YmFkX2FsbG9jAGRzdC0+bmVbMF0gPT0gbmMAYW1oYXJpYwBpY2VsYW5kaWMAYXJhYmljAERlYwAvVXNlcnMvZ2dlcmdhbm92L2RldmVsb3BtZW50L2dpdGh1Yi93aGlzcGVyLmNwcC9nZ21sLmMAL1VzZXJzL2dnZXJnYW5vdi9kZXZlbG9wbWVudC9naXRodWIvd2hpc3Blci5jcHAvZ2dtbC1iYWNrZW5kLmMAL1VzZXJzL2dnZXJnYW5vdi9kZXZlbG9wbWVudC9naXRodWIvd2hpc3Blci5jcHAvZ2dtbC1hbGxvYy5jAHdiAHJiAEZlYgB2ZXJ0aWNhbC10YWIAbl9sb2dpdHMgPT0gY3R4LnZvY2FiLm5fdm9jYWIAdytiAHIrYgBhK2IAcndhAGNwbGFuLT53b3JrX2RhdGEAaGF1c2EAc2hvbmEAY29tbWEAc2luaGFsYQBsaW5nYWxhAGFscGhhAGthbm5hZGEAeW9ydWJhAFtfZXh0cmFfdG9rZW5fAFtfVFRfAFtfTEFOR18AW19TT1RfXQBbX05PVF9dAFtfRU9UX10AW19CRUdfXQBbX1RSQU5TTEFURV9dAFtfVFJBTlNDUklCRV9dAGEtPm5lWzJdID09IGItPm5lWzJdAGEtPm5lWzJdID09IGItPm5lWzFdAGEtPm5lWzFdID09IGItPm5lWzFdAFoAdGVuc29yLT5vcCA9PSBHR01MX09QX1VOQVJZAE1BUF9VTkFSWQBNQVBfQklOQVJZAENQWQAlYSAlYiAlZCAlSDolTTolUyAlWQBQT1NJWABTT0ZUX01BWABBUkdNQVgAVklFVwBESVYAQ1BVAExFQUtZX1JFTFUAU1FSVABBUkdTT1JUAFdJTl9QQVJUAFdJTl9VTlBBUlQAaXNfcG9zaXRpdmVfY2hhciB8fCBwb3MtPnR5cGUgPT0gV0hJU1BFUl9HUkVUWVBFX0NIQVJfTk9UAHd0eXBlICE9IEdHTUxfVFlQRV9DT1VOVABDT05UAFNFVABNVUxfTUFUAFJFUEVBVABDT05DQVQAR0VUX1JPV1MAU1VNX1JPV1MAQ1JPU1NfRU5UUk9QWV9MT1NTAEdFVF9SRUxfUE9TAEFERF9SRUxfUE9TAGF4aXMzID49IDAgJiYgYXhpczMgPCBHR01MX01BWF9ESU1TAGF4aXMyID49IDAgJiYgYXhpczIgPCBHR01MX01BWF9ESU1TAGF4aXMxID49IDAgJiYgYXhpczEgPCBHR01MX01BWF9ESU1TAGF4aXMwID49IDAgJiYgYXhpczAgPCBHR01MX01BWF9ESU1TACVIOiVNOiVTAFNRUgBRAERVUABDTEFNUABESUFHX01BU0tfWkVSTwBGTEFTSF9BVFROAE5BTgBNRUFOAG5lMSA9PSBOAG5lZDEgPT0gTgBTVU0AUk1TX05PUk0AR1JPVVBfTk9STQBQTQBBTQBuZWIxMCA9PSBNAG5lYzAwID09IE0ATlVMAE1VTABJTTJDT0wAdGVuc29yLT5kYXRhID09IE5VTEwAaGFzaF9zZXQua2V5c1tpXSA9PSBOVUxMAHRlbnNvciAhPSBOVUxMAGN0eC0+bWVtX2J1ZmZlciAhPSBOVUxMAHZpZXctPnZpZXdfc3JjICE9IE5VTEwgJiYgdmlldy0+dmlld19zcmMtPmRhdGEgIT0gTlVMTABpICE9IEdHTUxfSEFTSFRBQkxFX0ZVTEwATENfQUxMAHE4X0sAcTZfSwBxNV9LAHE0X0sAcTNfSwBxMl9LAFNPRlRfTUFYX0JBQ0sAU0lMVV9CQUNLAFJFUEVBVF9CQUNLAEdFVF9ST1dTX0JBQ0sAQ1JPU1NfRU5UUk9QWV9MT1NTX0JBQ0sARkxBU0hfQVRUTl9CQUNLAFJNU19OT1JNX0JBQ0sAUk9QRV9CQUNLAEoAQUxJQkkASABMT0cATEFORwBESUFHAERJQUdfTUFTS19JTkYARkxBU0hfRkYAUEVSTVVURQBUUkFOU1BPU0UAUk9QRQBSRVNIQVBFAE5PTkUAVVBTQ0FMRQBPVVRfUFJPRABNVUxfTUFUX0lEAERFUFJFQ0FURUQAQUREAFBBRABQT09MXzJEAENPTlZfVFJBTlNQT1NFXzJEAFBPT0xfMUQAQ09OVl9UUkFOU1BPU0VfMUQAbmV2MSA9PSBEAG5lYzAxID09IEQAbmVrMCA9PSBEAG5lMCA9PSBEAG5lZDAgPT0gRABuZWMxMCA9PSBEAG5lYjAwID09IEQAQUNDAFNVQgBBAGVtc2NyaXB0ZW46Om1lbW9yeV92aWV3PHNob3J0PgBlbXNjcmlwdGVuOjptZW1vcnlfdmlldzx1bnNpZ25lZCBzaG9ydD4AZW1zY3JpcHRlbjo6bWVtb3J5X3ZpZXc8aW50PgBlbXNjcmlwdGVuOjptZW1vcnlfdmlldzx1bnNpZ25lZCBpbnQ+AGVtc2NyaXB0ZW46Om1lbW9yeV92aWV3PGZsb2F0PgBlbXNjcmlwdGVuOjptZW1vcnlfdmlldzx1aW50OF90PgBlbXNjcmlwdGVuOjptZW1vcnlfdmlldzxpbnQ4X3Q+AGVtc2NyaXB0ZW46Om1lbW9yeV92aWV3PHVpbnQxNl90PgBlbXNjcmlwdGVuOjptZW1vcnlfdmlldzxpbnQxNl90PgBlbXNjcmlwdGVuOjptZW1vcnlfdmlldzx1aW50NjRfdD4AZW1zY3JpcHRlbjo6bWVtb3J5X3ZpZXc8aW50NjRfdD4AZW1zY3JpcHRlbjo6bWVtb3J5X3ZpZXc8dWludDMyX3Q+AGVtc2NyaXB0ZW46Om1lbW9yeV92aWV3PGludDMyX3Q+AGVtc2NyaXB0ZW46Om1lbW9yeV92aWV3PGNoYXI+AGVtc2NyaXB0ZW46Om1lbW9yeV92aWV3PHVuc2lnbmVkIGNoYXI+AHN0ZDo6YmFzaWNfc3RyaW5nPHVuc2lnbmVkIGNoYXI+AGVtc2NyaXB0ZW46Om1lbW9yeV92aWV3PHNpZ25lZCBjaGFyPgBlbXNjcmlwdGVuOjptZW1vcnlfdmlldzxsb25nPgBlbXNjcmlwdGVuOjptZW1vcnlfdmlldzx1bnNpZ25lZCBsb25nPgBlbXNjcmlwdGVuOjptZW1vcnlfdmlldzxkb3VibGU+AD4+PgA8PDwAMDEyMzQ1Njc4OQBpOABIRUFQVTgAQy5VVEYtOABpMTYAZjE2AGRzdC0+dHlwZSA9PSBHR01MX1RZUEVfRjE2AHNyYzAtPnR5cGUgPT0gR0dNTF9UWVBFX0YxNgAgdjMAYXhpczIgIT0gYXhpczMAYXhpczEgIT0gYXhpczMAYXhpczAgIT0gYXhpczMAbmUwMyA9PSBuZTMAbmIyIDw9IG5iMwBNQVBfQ1VTVE9NMwBuZTMgPT0gbmUxMwBuZTAzID09IG5lMTMAbmUzID09IG5lMDMAYXhpczEgIT0gYXhpczIAYXhpczAgIT0gYXhpczIAZ2dtbF9uZWxlbWVudHMoYSkgPT0gbmUwKm5lMSpuZTIAbl9oZWFkID09IG5lMgBuZTAyID09IG5lMgBuYjEgPD0gbmIyAG5lMiA9PSBuZWEyAE1BUF9DVVNUT00yAGkzMgBmMzIAYi0+dHlwZSA9PSBHR01MX1RZUEVfSTMyAGRzdC0+dHlwZSA9PSBHR01MX1RZUEVfRjMyAHNyYzEtPnR5cGUgPT0gR0dNTF9UWVBFX0YzMgBNQVBfQ1VTVE9NM19GMzIATUFQX0NVU1RPTTJfRjMyAE1BUF9DVVNUT00xX0YzMgBuZTIgPT0gbmUxMgBuZTAyID09IG5lMTIAbmUyID09IG5lMDIAYXhpczAgIT0gYXhpczEAZ2dtbF9uZWxlbWVudHMoYSkgPT0gbmUwKm5lMQBuZTAwID09IG5lMQBuYjAgPD0gbmIxAG5lMSA9PSBuZWExAHE4XzEAcTVfMQBxNF8xAE1BUF9DVVNUT00xAEFERDEAbmUxID09IG5lMTEAbmUxID09IG5lMDEAbmUwID09IG5lMDEAdCA9PSAwIHx8IHQgPT0gMQBtYXNrLT5uZVszXSA9PSAxAGItPm5lWzNdID09IDEAbm9kZS0+c3JjWzFdLT5uZVszXSA9PSAxAG5vZGUtPnNyY1swXS0+bmVbM10gPT0gMQBtYXNrLT5uZVsyXSA9PSAxAG5vZGUtPnNyY1sxXS0+bmVbMl0gPT0gMQBuZWMxMSA9PSAxAG5lYjExID09IDEAbmUwMSA9PSAxAG5lMCA9PSAxAGdnbWxfYmxja19zaXplKHJlc3VsdF90eXBlKSA9PSAxAGswID09IHMwAG5lMDAgPT0gbmUwAG5fZGltcyA8PSBuZTAAbmUwID09IG5lYTAAcThfMABxNV8wAHE0XzAAbmUxID09IG5lMTAAbmUwID09IG5lMDAAY3BsYW4tPm5fdGhyZWFkcyA+IDAAbl9wYXN0ID49IDAAUCA+PSAwAHBhcmFtcy0+aXRoID09IDAAcmMgPT0gMAAoKHVpbnRwdHJfdCkgKG1lbV9idWZmZXIgKyBvYmpfbmV3LT5vZmZzKSklR0dNTF9NRU1fQUxJR04gPT0gMAAoKHVpbnRwdHJfdCkgKGN0eC0+bWVtX2J1ZmZlcikpJUdHTUxfTUVNX0FMSUdOID09IDAAbl9kaW1zICUgMiA9PSAwAHAwID09IDAAVGhlIGV4cHJlc3Npb24gY29udGFpbmVkIG1pc21hdGNoZWQgeyBhbmQgfS4AZW5jb2Rlci5ibG9ja3MuAGRlY29kZXIuYmxvY2tzLgBUaGUgZXhwcmVzc2lvbiBjb250YWluZWQgYW4gaW52YWxpZCBjaGFyYWN0ZXIgcmFuZ2UsIHN1Y2ggYXMgW2ItYV0gaW4gbW9zdCBlbmNvZGluZ3MuAEFuIGVtcHR5IHJlZ2V4IGlzIG5vdCBhbGxvd2VkIGluIHRoZSBQT1NJWCBncmFtbWFyLgBUaGUgZXhwcmVzc2lvbiBjb250YWluZWQgYW4gaW52YWxpZCByYW5nZSBpbiBhIHt9IGV4cHJlc3Npb24uAFRoZSBwYXJzZXIgZGlkIG5vdCBjb25zdW1lIHRoZSBlbnRpcmUgcmVndWxhciBleHByZXNzaW9uLgBPbmUgb2YgKj8reyB3YXMgbm90IHByZWNlZGVkIGJ5IGEgdmFsaWQgcmVndWxhciBleHByZXNzaW9uLgBUaGUgY29tcGxleGl0eSBvZiBhbiBhdHRlbXB0ZWQgbWF0Y2ggYWdhaW5zdCBhIHJlZ3VsYXIgZXhwcmVzc2lvbiBleGNlZWRlZCBhIHByZS1zZXQgbGV2ZWwuAFRoZSBleHByZXNzaW9uIGNvbnRhaW5lZCBhbiBpbnZhbGlkIGVzY2FwZWQgY2hhcmFjdGVyLCBvciBhIHRyYWlsaW5nIGVzY2FwZS4AVGhlcmUgd2FzIGluc3VmZmljaWVudCBtZW1vcnkgdG8gY29udmVydCB0aGUgZXhwcmVzc2lvbiBpbnRvIGEgZmluaXRlIHN0YXRlIG1hY2hpbmUuAFRoZSBleHByZXNzaW9uIGNvbnRhaW5lZCBhbiBpbnZhbGlkIGNvbGxhdGluZyBlbGVtZW50IG5hbWUuAFRoZSBleHByZXNzaW9uIGNvbnRhaW5lZCBhbiBpbnZhbGlkIGNoYXJhY3RlciBjbGFzcyBuYW1lLgBUaGVyZSB3YXMgaW5zdWZmaWNpZW50IG1lbW9yeSB0byBkZXRlcm1pbmUgd2hldGhlciB0aGUgcmVndWxhciBleHByZXNzaW9uIGNvdWxkIG1hdGNoIHRoZSBzcGVjaWZpZWQgY2hhcmFjdGVyIHNlcXVlbmNlLgBUaGUgZXhwcmVzc2lvbiBjb250YWluZWQgYW4gaW52YWxpZCBiYWNrIHJlZmVyZW5jZS4AQW4gaW52YWxpZCByZWdleCBncmFtbWFyIGhhcyBiZWVuIHJlcXVlc3RlZC4AVGhlIGV4cHJlc3Npb24gY29udGFpbmVkIG1pc21hdGNoZWQgWyBhbmQgXS4AVGhlIGV4cHJlc3Npb24gY29udGFpbmVkIG1pc21hdGNoZWQgKCBhbmQgKS4ALS0tAHcrACdzfCd0fCdyZXwndmV8J218J2xsfCdkfCA/W1s6YWxwaGE6XV0rfCA/W1s6ZGlnaXQ6XV0rfCA/W15cc1s6YWxwaGE6XVs6ZGlnaXQ6XV0rfFxzKyg/IVxTKXxccysAcisAYSsAJXMgKGNvcHkpACVzICh2aWV3KQBnZ21sX2JhY2tlbmRfaXNfY3B1KGJhY2tlbmRfY3B1KQBnZ21sX2lzX2NvbnRpZ3VvdXMoZHN0KQBvZmZzZXQgKyBpbTAqbmIwICsgaW0xKm5iMSArIGltMipuYjIgKyBpbTMqbmIzIDw9IGdnbWxfbmJ5dGVzKGRzdCkAb2Zmc2V0ICsgKG5lMTAgPT0gMCA/IDAgOiBuZTEwLTEpKm5iMCArIChuZTExID09IDAgPyAwIDogbmUxMS0xKSpuYjEgKyAobmUxMiA9PSAwID8gMCA6IG5lMTItMSkqbmIyICsgKG5lMTMgPT0gMCA/IDAgOiBuZTEzLTEpKm5iMyA8IGdnbWxfbmJ5dGVzKGRzdCkAZ2dtbF9pc19zY2FsYXIoZHN0KQBnZ21sX2lzX2NvbnRpZ3VvdXNfZXhjZXB0X2RpbV8xKGRzdCkAZ2dtbF9hcmVfc2FtZV9zaGFwZShzcmMxLCBkc3QpAGdnbWxfY2FuX3JlcGVhdChzcmMwLCBkc3QpAGdnbWxfYXJlX3NhbWVfc2hhcGUoc3JjMCwgc3JjMSkgJiYgZ2dtbF9hcmVfc2FtZV9zaGFwZShzcmMwLCBkc3QpAGdnbWxfY2FuX3JlcGVhdChzcmMxLCBzcmMwKSAmJiBnZ21sX2FyZV9zYW1lX3NoYXBlKHNyYzAsIGRzdCkAJXMgKGNvbnQpAGRzdC0+bmJbMF0gPT0gc2l6ZW9mKGZsb2F0KQBzcmMwLT5uYlswXSA9PSBzaXplb2YoZmxvYXQpAG5idjAgPT0gc2l6ZW9mKGZsb2F0KQBuYnEwID09IHNpemVvZihmbG9hdCkAbmJrMCA9PSBzaXplb2YoZmxvYXQpAG5iMCA9PSBzaXplb2YoZmxvYXQpAG5iYzEwID09IHNpemVvZihmbG9hdCkAbmIxMCA9PSBzaXplb2YoZmxvYXQpAG5iYjEwID09IHNpemVvZihmbG9hdCkAbmIwMCA9PSBzaXplb2YoZmxvYXQpAHNyYzAtPm5iWzBdID09IHNpemVvZihnZ21sX2ZwMTZfdCkAbmJ2MCA9PSBzaXplb2YoZ2dtbF9mcDE2X3QpAG5icTAgPT0gc2l6ZW9mKGdnbWxfZnAxNl90KQBuYmswID09IHNpemVvZihnZ21sX2ZwMTZfdCkAbmIwID09IHNpemVvZihnZ21sX2ZwMTZfdCkAbmJhMCA9PSBzaXplb2YoZ2dtbF9mcDE2X3QpAG5iYzAwID09IHNpemVvZihnZ21sX2ZwMTZfdCkAbmIwMCA9PSBzaXplb2YoZ2dtbF9mcDE2X3QpAG5iYjAwID09IHNpemVvZihnZ21sX2ZwMTZfdCkAJXMgKGNvcHkgb2YgJXMpACFnZ21sX2lzX3ZpZXcodGVuc29yKQAobnVsbCkAZ2dtbF9pc19jb250aWd1b3VzKG1hc2spAG5iMTAgPT0gZ2dtbF90eXBlX3NpemUoc3JjMS0+dHlwZSkAZ2dtbF9pc19xdWFudGl6ZWQoc3JjMC0+dHlwZSkAbmIwMCA9PSBnZ21sX3R5cGVfc2l6ZSh0eXBlKQAlcyAocGVybXV0ZWQpACVzICh0cmFuc3Bvc2VkKQAlcyAocmVzaGFwZWQpAGdnbWxfaXNfY29udGlndW91c19leGNlcHRfZGltXzEoZ3JhZCkAZ2dtbF9hcmVfc2FtZV9zaGFwZShzcmMwLCBncmFkKQB2aWV3X3NyYyA9PSBOVUxMIHx8IGRhdGFfc2l6ZSArIHZpZXdfb2ZmcyA8PSBnZ21sX25ieXRlcyh2aWV3X3NyYykAcGFyYW1zLT53c2l6ZSA+PSBzaXplb2YoZmxvYXQpICogKG50aCArIG50aCAqIG5jKQBnZ21sX25lbGVtZW50cyhhKSA9PSBnZ21sX25lbGVtZW50cyhiKQBnZ21sX2lzX3NjYWxhcihiKQBnZ21sX2Nhbl9tdWxfbWF0KGEsIGIpAGdnbWxfYXJlX3NhbWVfc2hhcGUoYSwgYikAZ2dtbF9pc19jb250aWd1b3VzKGEpACFnZ21sX2lzX3RyYW5zcG9zZWQoYSkAZ2dtbF9pc19wYWRkZWRfMWQoYSkAZ2dtbF9jYW5fcmVwZWF0X3Jvd3MobWFzaywgYSkAZ2dtbF9jYW5fcmVwZWF0KGIsIGEpAGdnbWxfaXNfY29udGlndW91cyhzcmMxKQBnZ21sX2lzX3NjYWxhcihzcmMxKQBnZ21sX2FyZV9zYW1lX3NoYXBlKHNyYzAsIGRzdCkgJiYgZ2dtbF9hcmVfc2FtZV9zaGFwZShzcmMwLCBzcmMxKQBnZ21sX2lzX2NvbnRpZ3VvdXMob3B0MCkAZ2dtbF9pc19jb250aWd1b3VzKGRzdCkgJiYgZ2dtbF9pc19jb250aWd1b3VzKHNyYzApAGdnbWxfbmVsZW1lbnRzKGRzdCkgPT0gZ2dtbF9uZWxlbWVudHMoc3JjMCkAb2Zmc2V0ICsgKG5lMTAgPT0gMCA/IDAgOiBuZTEwLTEpKm5iMDAgKyAobmUxMSA9PSAwID8gMCA6IG5lMTEtMSkqbmIwMSArIChuZTEyID09IDAgPyAwIDogbmUxMi0xKSpuYjAyICsgKG5lMTMgPT0gMCA/IDAgOiBuZTEzLTEpKm5iMDMgPCBnZ21sX25ieXRlcyhzcmMwKQBnZ21sX2lzX2NvbnRpZ3VvdXNfZXhjZXB0X2RpbV8xKHNyYzApAGdnbWxfY2FuX3JlcGVhdChkc3QsIHNyYzApACkpKQBvcGVyYXRvcigpACgoKABzcmMxLT50eXBlID09IEdHTUxfVFlQRV9GMzIgJiYgIm9ubHkgZjMyIHNyYzEgc3VwcG9ydGVkIGZvciBub3ciAHRlbnNvci0+YnVmZmVyICE9IE5VTEwgJiYgInRlbnNvciBidWZmZXIgbm90IHNldCIAYWxsb2MtPm5fZnJlZV9ibG9ja3MgPCBNQVhfRlJFRV9CTE9DS1MgJiYgIm91dCBvZiBmcmVlIGJsb2NrcyIAb2Zmc2V0ICsgc2l6ZSA8PSBnZ21sX25ieXRlcyh0ZW5zb3IpICYmICJ0ZW5zb3Igd3JpdGUgb3V0IG9mIGJvdW5kcyIAb2Zmc2V0ICsgc2l6ZSA8PSBnZ21sX25ieXRlcyh0ZW5zb3IpICYmICJ0ZW5zb3IgcmVhZCBvdXQgb2YgYm91bmRzIgBkYXRhICE9IE5VTEwgJiYgImZhaWxlZCB0byBhbGxvY2F0ZSBidWZmZXIiACEibm90IGVub3VnaCBzcGFjZSBpbiB0aGUgYnVmZmVyIgB0ZW5zb3ItPmRhdGEgIT0gTlVMTCAmJiAidGVuc29yIG5vdCBhbGxvY2F0ZWQiAGJhc2UgIT0gTlVMTCAmJiAiYmFja2VuZCBidWZmZXIgYmFzZSBjYW5ub3QgYmUgTlVMTCIAUHVyZSB2aXJ0dWFsIGZ1bmN0aW9uIGNhbGxlZCEAV0FSTklORzogQmVoYXZpb3IgbWF5IGJlIHVuZXhwZWN0ZWQgd2hlbiBhbGxvY2F0aW5nIDAgYnl0ZXMgZm9yIGdnbWxfYWxpZ25lZF9tYWxsb2MhACB8IABBVlggPSAAVlNYID0gAEJMQVMgPSAAT1BFTlZJTk8gPSAATkVPTiA9IABDT1JFTUwgPSAATUVUQUwgPSAAV0FTTV9TSU1EID0gAEYxNkMgPSAARlAxNl9WQSA9IABBUk1fRk1BID0gAENVREEgPSAAU1NTRTMgPSAAQVZYMiA9IABBVlg1MTIgPSAAJXM6IG9wIG5vdCBpbXBsZW1lbnRlZDogACVzOiB0ZW5zb3IgJyVzJyBoYXMgd3Jvbmcgc2l6ZSBpbiBtb2RlbCBmaWxlOiBnb3QgJXp1LCBleHBlY3RlZCAlenUKACVzOiB1c2luZyBkaXN0aWxsZWQgbW9kZWwgLSBmb3JjaW5nIG5vX3RpbWVzdGFtcHMKACVzOiBhZGRpbmcgJWQgZXh0cmEgdG9rZW5zCgAlczogICAgICBtZWwgdGltZSA9ICU4LjJmIG1zCgAlczogICAgdG90YWwgdGltZSA9ICU4LjJmIG1zCgAlczogICAgIGxvYWQgdGltZSA9ICU4LjJmIG1zCgBzeXN0ZW1faW5mbzogbl90aHJlYWRzID0gJWQgLyAlZCB8ICVzCgBXSElTUEVSX0FTU0VSVDogJXM6JWQ6ICVzCgBHR01MX0FTU0VSVDogJXM6JWQ6ICVzCgBbJXMgLS0+ICVzXSAgJXMKACVzOiBvZmZzZXQgJWRtcyBpcyBiZWZvcmUgdGhlIHN0YXJ0IG9mIHRoZSBhdWRpbwoAdW5rbm93biB0b2tlbgoAJXM6IGZhaWxlZCB0byBjb21wdXRlIGxvZyBtZWwgc3BlY3Ryb2dyYW0KACVzOiBsb2FkaW5nIG1vZGVsCgAlczogZmFpbGVkIHRvIGxvYWQgbW9kZWwKACVzOiBmYWlsZWQgdG8gZXZhbAoAJXM6ICAgICBmYWxsYmFja3MgPSAlM2QgcCAvICUzZCBoCgAlczogV0FSTiBubyB0ZW5zb3JzIGxvYWRlZCBmcm9tIG1vZGVsIGZpbGUgLSBhc3N1bWluZyBlbXB0eSBtb2RlbCBmb3IgdGVzdGluZwoAJXM6IGVuY29kZXJfYmVnaW5fY2FsbGJhY2sgcmV0dXJuZWQgZmFsc2UgLSBhYm9ydGluZwoAJXM6IHRlbnNvciAnJXMnIGhhcyB3cm9uZyBzaXplIGluIG1vZGVsIGZpbGUKACVzOiB1bmtub3duIHRlbnNvciAnJXMnIGluIG1vZGVsIGZpbGUKACVzOiBubyBzaWduYWwgZGF0YSBhdmFpbGFibGUKACVzOiBmYWlsZWQgdG8gYWxsb2NhdGUgbWVtb3J5IGZvciBrdiBjYWNoZQoAJXM6IGt2X2NhY2hlX2luaXQoKSBmYWlsZWQgZm9yIGNyb3NzLWF0dGVudGlvbiBjYWNoZQoAJXM6IGt2X2NhY2hlX2luaXQoKSBmYWlsZWQgZm9yIHNlbGYtYXR0ZW50aW9uIGNhY2hlCgAlczogZmFpbGVkIHRvIGF1dG8tZGV0ZWN0IGxhbmd1YWdlCgAlczogZmFpbGVkIHRvIGVuY29kZQoAJXM6IGZhaWxlZCB0byBkZWNvZGUKACVzOiBnZ21sX2luaXQoKSBmYWlsZWQKACVzOiBuX3Rva2Vucz0lZCA+IG5fY3R4PSVkCgAlczogRVJST1Igbm90IGFsbCB0ZW5zb3JzIGxvYWRlZCBmcm9tIG1vZGVsIGZpbGUgLSBleHBlY3RlZCAlenUsIGdvdCAlZAoAJXM6IHVua25vd24gbGFuZ3VhZ2UgaWQgJWQKACVzOiB0b28gbWFueSBkZWNvZGVycyByZXF1ZXN0ZWQgKCVkKSwgbWF4ID0gJWQKACVzOiBuX2F1ZGlvX2xheWVyID0gJWQKACVzOiBuX2F1ZGlvX3N0YXRlID0gJWQKACVzOiBuX3RleHRfbGF5ZXIgID0gJWQKACVzOiBuX3RleHRfc3RhdGUgID0gJWQKACVzOiBuX2F1ZGlvX2hlYWQgID0gJWQKACVzOiBuX2F1ZGlvX2N0eCAgID0gJWQKACVzOiBuX3RleHRfaGVhZCAgID0gJWQKACVzOiBuX3RleHRfY3R4ICAg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EBAQEBAAEBAQEBAAEAAQEBAQEBAAABAQEBAQAAAAEBAQEBAQEBAAEBAQEAAQEAAQEBAQEAAQABAQEBAAABAAEBAQEBAQAAAQEBAQABAAABAQEBAQAAAAEBAQEAAAAAAQEBAQEBAQEAAQEBAAEBAQABAQEBAAEBAAEBAQAAAQEAAQEBAQEAAQABAQEAAQABAAEBAQEAAAEAAQEBAAAAAQABAQEBAQEAAAEBAQABAQAAAQEBAQABAAABAQEAAAEAAAEBAQEBAAAAAQEBAAEAAAABAQEBAAAAAAEBAQAAAAAAAQEBAQEBAQEAAQEAAQEBAQABAQEAAQEBAAEBAAABAQEAAQEBAQABAQABAQABAAEBAAEBAQAAAQEAAQEAAAABAQABAQEBAQABAAEBAAEBAAEAAQEBAAEAAQABAQAAAQABAAEBAQEAAAEAAQEAAQAAAQABAQEAAAABAAEBAAAAAAEAAQEBAQEBAAABAQABAQEAAAEBAQABAQAAAQEAAAEBAAABAQEBAAEAAAEBAAEAAQAAAQEBAAABAAABAQAAAAEAAAEBAQEBAAAAAQEAAQEAAAABAQEAAQAAAAEBAAABAAAAAQEBAQAAAAABAQABAAAAAAEBAQAAAAAAAQEAAAAAAAABAQEBAQEBAQABAAEBAQEBAAEBAAEBAQEAAQAAAQEBAQABAQEAAQEBAAEAAQABAQEAAQEAAAEBAQABAAAAAQEBAAEBAQEAAQEAAQABAQABAQABAQABAAEBAAEAAAEAAQEAAQEBAAABAQABAAEAAAEBAAEBAAAAAQEAAQAAAAABAQABAQEBAQABAAEAAQEBAAEAAQEAAQEAAQABAAABAQABAAEBAQABAAEAAQABAAEAAQABAQAAAQABAAEAAAABAAEAAQEBAQAAAQABAAEBAAABAAEBAAEAAAEAAQAAAQAAAQABAQEAAAABAAEAAQAAAAEAAQEAAAAAAQABAAAAAAABAAEBAQEBAQAAAQABAQEBAAABAQABAQEAAAEAAAEBAQAAAQEBAAEBAAABAAEAAQEAAAEBAAABAQAAAQAAAAEBAAABAQEBAAEAAAEAAQEAAQAAAQEAAQABAAABAAABAAEAAAEBAQAAAQAAAQABAAABAAABAQAAAAEAAAEAAAAAAQAAAQEBAQEAAAABAAEBAQAAAAEBAAEBAAAAAQAAAQEAAAABAQEAAQAAAAEAAQABAAAAAQEAAAEAAAABAAAAAQAAAAEBAQEAAAAAAQABAQAAAAABAQABAAAAAAEAAAEAAAAAAQEBAAAAAAABAAEAAAAAAAEBAAAAAAAAAQAAAAAAAAABAQEBAQEBAQAAAQEBAQEBAAEAAQEBAQEAAAABAQEBAQABAQABAQEBAAABAAEBAQEAAQAAAQEBAQAAAAABAQEBAAEBAQABAQEAAAEBAAEBAQABAAEAAQEBAAAAAQABAQEAAQEAAAEBAQAAAQAAAQEBAAEAAAABAQEAAAAAAAEBAQABAQEBAAEBAAABAQEAAQEAAQABAQABAQAAAAEBAAEBAAEBAAEAAQEAAAEAAQABAQABAAABAAEBAAAAAAEAAQEAAQEBAAABAQAAAQEAAAEBAAEAAQAAAQEAAAABAAABAQABAQAAAAEBAAABAAAAAQEAAQAAAAABAQAAAAAAAAEBAAEBAQEBAAEAAAEBAQEAAQABAAEBAQABAAAAAQEBAAEAAQEAAQEAAQAAAQABAQABAAEAAAEBAAEAAAAAAQEAAQABAQEAAQABAAABAQABAAEAAQABAAEAAQAAAAEAAQABAAEBAAABAAEAAAEAAAEAAQABAAAAAQABAAAAAAABAAEAAQEBAQAAAQAAAQEBAAABAAEAAQEAAAEAAAABAQAAAQABAQABAAABAAABAAEAAAEAAQAAAQAAAQAAAAABAAABAAEBAQAAAAEAAAEBAAAAAQABAAEAAAABAAAAAQAAAAEAAQEAAAAAAQAAAQAAAAABAAEAAAAAAAEAAAAAAAAAAQABAQEBAQEAAAABAQEBAQAAAQABAQEBAAAAAAEBAQEAAAEBAAEBAQAAAAEAAQEBAAABAAABAQEAAAAAAAEBAQAAAQEBAAEBAAAAAQEAAQEAAAEAAQABAQAAAAABAAEBAAABAQAAAQEAAAABAAABAQAAAQAAAAEBAAAAAAAAAQEAAAEBAQEAAQAAAAEBAQABAAABAAEBAAEAAAAAAQEAAQAAAQEAAQABAAAAAQABAAEAAAEAAAEAAQAAAAAAAQABAAABAQEAAAEAAAABAQAAAQAAAQABAAABAAAAAAEAAAEAAAEBAAAAAQAAAAEAAAABAAABAAAAAAEAAAAAAAAAAQAAAQEBAQEAAAAAAQEBAQAAAAEAAQEBAAAAAAABAQEAAAABAQABAQAAAAABAAEBAAAAAQAAAQEAAAAAAAABAQAAAAEBAQABAAAAAAEBAAEAAAABAAEAAQAAAAAAAQABAAAAAQEAAAEAAAAAAQAAAQAAAAEAAAABAAAAAAAAAAEAAAABAQEBAAAAAAABAQEAAAAAAQABAQAAAAAAAAEBAAAAAAEBAAEAAAAAAAEAAQAAAAABAAABAAAAAAAAAAEAAAAAAQEBAAAAAAAAAQEAAAAAAAEAAQAAAAAAAAABAAAAAAABAQAAAAAAAAABAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAQAAAAAAAAEBAAAAAAAAAAABAAAAAAABAAEAAAAAAAABAQAAAAAAAQEBAAAAAAAAAAABAAAAAAEAAAEAAAAAAAEAAQAAAAABAQABAAAAAAAAAQEAAAAAAQABAQAAAAAAAQEBAAAAAAEBAQEAAAAAAAAAAAEAAAABAAAAAQAAAAABAAABAAAAAQEAAAEAAAAAAAEAAQAAAAEAAQABAAAAAAEBAAEAAAABAQEAAQAAAAAAAAEBAAAAAQAAAQEAAAAAAQABAQAAAAEBAAEBAAAAAAABAQEAAAABAAEBAQAAAAABAQEBAAAAAQEBAQEAAAAAAAAAAAEAAAEAAAAAAQAAAAEAAAABAAABAQAAAAEAAAAAAQAAAQAAAQABAAABAAAAAQEAAAEAAAEBAQAAAQAAAAAAAQABAAABAAABAAEAAAABAAEAAQAAAQEAAQABAAAAAAEBAAEAAAEAAQEAAQAAAAEBAQABAAABAQEBAAEAAAAAAAABAQAAAQAAAAEBAAAAAQAAAQEAAAEBAAABAQAAAAABAAEBAAABAAEAAQEAAAABAQABAQAAAQEBAAEBAAAAAAABAQEAAAEAAAEBAQAAAAEAAQEBAAABAQABAQEAAAAAAQEBAQAAAQABAQEBAAAAAQEBAQEAAAEBAQEBAQAAAAAAAAAAAQABAAAAAAABAAABAAAAAAEAAQEAAAAAAQAAAAEAAAABAAEAAQAAAAEAAAEBAAAAAQABAQEAAAABAAAAAAEAAAEAAQAAAQAAAQAAAQABAAABAAEBAAEAAAEAAAABAQAAAQABAAEBAAABAAABAQEAAAEAAQEBAQAAAQAAAAAAAQABAAEAAAABAAEAAAEAAAEAAQABAQAAAQABAAAAAQABAAEAAQABAAEAAQAAAQEAAQABAAEBAQABAAEAAAAAAQEAAQABAAABAQABAAABAAEBAAEAAQEAAQEAAQAAAAEBAQABAAEAAQEBAAEAAAEBAQEAAQABAQEBAQABAAAAAAAAAQEAAQAAAAABAQAAAQAAAAEBAAEBAAAAAQEAAAABAAABAQABAAEAAAEBAAABAQAAAQEAAQEBAAABAQAAAAABAAEBAAEAAAEAAQEAAAEAAQABAQABAQABAAEBAAAAAQEAAQEAAQABAQABAQAAAQEBAAEBAAEBAQEAAQEAAAAAAAEBAQABAAAAAQEBAAABAAABAQEAAQEAAAEBAQAAAAEAAQEBAAEAAQABAQEAAAEBAAEBAQABAQEAAQEBAAAAAAEBAQEAAQAAAQEBAQAAAQABAQEBAAEBAAEBAQEAAAABAQEBAQABAAEBAQEBAAABAQEBAQEAAQEBAQEBAQAAAAAAAAAAAQEAAAAAAAABAAEAAAAAAAEBAQAAAAAAAQAAAQAAAAABAQABAAAAAAEAAQEAAAAAAQEBAQAAAAABAAAAAQAAAAEBAAABAAAAAQABAAEAAAABAQEAAQAAAAEAAAEBAAAAAQEAAQEAAAABAAEBAQAAAAEBAQEBAAAAAQAAAAABAAABAQAAAAEAAAEAAQAAAQAAAQEBAAABAAABAAABAAEAAAEBAAEAAQAAAQABAQABAAABAQEBAAEAAAEAAAABAQAAAQEAAAEBAAABAAEAAQEAAAEBAQABAQAAAQAAAQEBAAABAQABAQEAAAEAAQEBAQAAAQEBAQEBAAABAAAAAAABAAEBAAAAAAEAAQABAAAAAQABAQEAAAABAAEAAAEAAAEAAQEAAQAAAQABAAEBAAABAAEBAQEAAAEAAQAAAAEAAQABAQAAAQABAAEAAQABAAEAAQEBAAEAAQABAAABAQABAAEBAAEBAAEAAQABAQEAAQABAQEBAQABAAEAAAAAAQEAAQEAAAABAQABAAEAAAEBAAEBAQAAAQEAAQAAAQABAQABAQABAAEBAAEAAQEAAQEAAQEBAQABAQABAAAAAQEBAAEBAAABAQEAAQABAAEBAQABAQEAAQEBAAEAAAEBAQEAAQEAAQEBAQABAAEBAQEBAAEBAQEBAQEAAQAAAAAAAAEBAQAAAAAAAQEAAQAAAAABAQEBAAAAAAEBAAABAAAAAQEBAAEAAA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AAAAXwAAAGAAAABhAAAATlN0M19fMjIwX19zaGFyZWRfcHRyX3BvaW50ZXJJUE5TXzEzX19lbXB0eV9zdGF0ZUljRUVOU18xMHNoYXJlZF9wdHJJUzJfRTI3X19zaGFyZWRfcHRyX2RlZmF1bHRfZGVsZXRlSVMyX1MyX0VFTlNfOWFsbG9jYXRvcklTMl9FRUVFAAAAAAjaAABYUwAAiNAAAE5TdDNfXzIxMHNoYXJlZF9wdHJJTlNfMTNfX2VtcHR5X3N0YXRlSWNFRUUyN19fc2hhcmVkX3B0cl9kZWZhdWx0X2RlbGV0ZUlTMl9TMl9FRQAAAAAAAADIVAAAYgAAAGMAAABkAAAAXAAAAE5TdDNfXzIxM19fZW1wdHlfc3RhdGVJY0VFAE5TdDNfXzIxNl9fb3duc19vbmVfc3RhdGVJY0VFAE5TdDNfXzIxNV9faGFzX29uZV9zdGF0ZUljRUUAAAAI2gAAkVQAAChTAAAI2gAAc1QAALBUAAAI2gAAWFQAALxUAAAAAAAAvFQAAGIAAABlAAAAZgAAAFwAAAAAAAAAKFUAAGIAAABnAAAAaAAAAFwAAABOU3QzX18yMjBfX2xfYW5jaG9yX211bHRpbGluZUljRUUAAAAI2gAABFUAALxUAAAAAAAAcFUAAGIAAABpAAAAagAAAFwAAABOU3QzX18yMjBfX3JfYW5jaG9yX211bHRpbGluZUljRUUAAAAI2gAATFUAALxUAAAAAAAAyFUAAGsAAABsAAAAbQAAAFwAAABOU3QzX18yMTVfX3dvcmRfYm91bmRhcnlJY05TXzEycmVnZXhfdHJhaXRzSWNFRUVFAAAACNoAAJRVAAC8VAAAAAAAABxWAABuAAAAbwAAAHAAAABcAAAATlN0M19fMjExX19sb29rYWhlYWRJY05TXzEycmVnZXhfdHJhaXRzSWNFRUVFAAAACNoAAOxVAAC8VAAAAAAAAGhWAABiAAAAcQAAAHIAAABcAAAATlN0M19fMjIzX19tYXRjaF9hbnlfYnV0X25ld2xpbmVJY0VFAAAAAAjaAABAVgAAvFQAAAAAAADEVgAAcwAAAHQAAAB1AAAAXAAAAE5TdDNfXzIxOF9fbWF0Y2hfY2hhcl9pY2FzZUljTlNfMTJyZWdleF90cmFpdHNJY0VFRUUAAAAACNoAAIxWAAC8VAAAAAAAACBXAAB2AAAAdwAAAHgAAABcAAAATlN0M19fMjIwX19tYXRjaF9jaGFyX2NvbGxhdGVJY05TXzEycmVnZXhfdHJhaXRzSWNFRUVFAAAI2gAA6FYAALxUAAAAAAAAYFcAAGIAAAB5AAAAegAAAFwAAABOU3QzX18yMTJfX21hdGNoX2NoYXJJY0VFAAAACNoAAERXAAC8VAAAAAAAALhXAAB7AAAAfAAAAH0AAABcAAAATlN0M19fMjE2X19iYWNrX3JlZl9pY2FzZUljTlNfMTJyZWdleF90cmFpdHNJY0VFRUUAAAjaAACEVwAAvFQAAAAAAAAUWAAAfgAAAH8AAACAAAAAXAAAAE5TdDNfXzIxOF9fYmFja19yZWZfY29sbGF0ZUljTlNfMTJyZWdleF90cmFpdHNJY0VFRUUAAAAACNoAANxXAAC8VAAAAAAAAFBYAABiAAAAgQAAAIIAAABcAAAATlN0M19fMjEwX19iYWNrX3JlZkljRUUACNoAADhYAAC8VAAAAAAAAKxYAACDAAAAhAAAAIUAAABcAAAATlN0M19fMjIwX19icmFja2V0X2V4cHJlc3Npb25JY05TXzEycmVnZXhfdHJhaXRzSWNFRUVFAAAI2gAAdFgAALxUAAAAAAAA/FgAAGIAAACGAAAAhwAAAFwAAABOU3QzX18yMjhfX2JlZ2luX21hcmtlZF9zdWJleHByZXNzaW9uSWNFRQAAAAjaAADQWAAAvFQAAAAAAABIWQAAYgAAAIgAAACJAAAAXAAAAE5TdDNfXzIyNl9fZW5kX21hcmtlZF9zdWJleHByZXNzaW9uSWNFRQAI2gAAIFkAALxUAAAAAAAArFkAAIoAAACLAAAAjAAAAI0AAABOU3QzX18yNl9fbG9vcEljRUUATlN0M19fMjE3X19vd25zX3R3b19zdGF0ZXNJY0VFAAAACNoAAH9ZAAC8VAAACNoAAGxZAACgWQAAAAAAAKBZAACKAAAAjgAAAGYAAABcAAAAAAAAAAhaAABZAAAAjwAAAJAAAABcAAAATlN0M19fMjE3X19yZXBlYXRfb25lX2xvb3BJY0VFAAAI2gAA6FkAALBUAAAAAAAASFoAAIoAAACRAAAAkgAAAJMAAABOU3QzX18yMTFfX2FsdGVybmF0ZUljRUUAAAAACNoAACxaAACgWQAAAAAAAJBaAABZAAAAlAAAAJUAAABcAAAATlN0M19fMjIxX19lbXB0eV9ub25fb3duX3N0YXRlSWNFRQAACNoAAGxaAACwVAAAAAAAANBaAABiAAAAlgAAAJcAAABcAAAATlN0M19fMjExX19tYXRjaF9hbnlJY0VFAAAAAAjaAAC0WgAAvFQAAAAAAACcWwAAmAAAAJkAAACaAAAAmwAAAJwAAACdAAAAngAAAJ8AAACgAAAATlN0M19fMjEwX19mdW5jdGlvbjZfX2Z1bmNJWjE4d2hpc3Blcl9pbml0X3N0YXRlRTMkXzBOU185YWxsb2NhdG9ySVMyX0VFRlAxMWdnbWxfY2dyYXBodkVFRQBOU3QzX18yMTBfX2Z1bmN0aW9uNl9fYmFzZUlGUDExZ2dtbF9jZ3JhcGh2RUVFAADg2QAAZFsAAAjaAAAIWwAAlFsAAFoxOHdoaXNwZXJfaW5pdF9zdGF0ZUUzJF8wAADg2QAAqFsAAAAAAABUXAAAmAAAAKEAAACiAAAAowAAAKQAAAClAAAApgAAAKcAAACoAAAATlN0M19fMjEwX19mdW5jdGlvbjZfX2Z1bmNJWjE4d2hpc3Blcl9pbml0X3N0YXRlRTMkXzFOU185YWxsb2NhdG9ySVMyX0VFRlAxMWdnbWxfY2dyYXBodkVFRQAI2gAA+FsAAJRbAABaMTh3aGlzcGVyX2luaXRfc3RhdGVFMyRfMQAA4NkAAGBcAAAAAAAADF0AAJgAAACpAAAAqgAAAKsAAACsAAAArQAAAK4AAACvAAAAsAAAAE5TdDNfXzIxMF9fZnVuY3Rpb242X19mdW5jSVoxOHdoaXNwZXJfaW5pdF9zdGF0ZUUzJF8yTlNfOWFsbG9jYXRvcklTMl9FRUZQMTFnZ21sX2NncmFwaHZFRUUACNoAALBcAACUWwAAWjE4d2hpc3Blcl9pbml0X3N0YXRlRTMkXzIAAODZAAAYXQAAAAAAAMRdAACYAAAAsQAAALIAAACzAAAAtAAAALUAAAC2AAAAtwAAALgAAABOU3QzX18yMTBfX2Z1bmN0aW9uNl9fZnVuY0laMTh3aGlzcGVyX2luaXRfc3RhdGVFMyRfM05TXzlhbGxvY2F0b3JJUzJfRUVGUDExZ2dtbF9jZ3JhcGh2RUVFAAjaAABoXQAAlFsAAFoxOHdoaXNwZXJfaW5pdF9zdGF0ZUUzJF8zAADg2QAA0F0AAE5TdDNfXzIxMmJhc2ljX3N0cmluZ0loTlNfMTFjaGFyX3RyYWl0c0loRUVOU185YWxsb2NhdG9ySWhFRUVFAADg2QAA9F0AAE5TdDNfXzIxMmJhc2ljX3N0cmluZ0l3TlNfMTFjaGFyX3RyYWl0c0l3RUVOU185YWxsb2NhdG9ySXdFRUVFAADg2QAAPF4AAE5TdDNfXzIxMmJhc2ljX3N0cmluZ0lEc05TXzExY2hhcl90cmFpdHNJRHNFRU5TXzlhbGxvY2F0b3JJRHNFRUVFAAAA4NkAAIReAABOU3QzX18yMTJiYXNpY19zdHJpbmdJRGlOU18xMWNoYXJfdHJhaXRzSURpRUVOU185YWxsb2NhdG9ySURpRUVFRQAAAODZAADQXgAATjEwZW1zY3JpcHRlbjExbWVtb3J5X3ZpZXdJY0VFAADg2QAAHF8AAE4xMGVtc2NyaXB0ZW4xMW1lbW9yeV92aWV3SWFFRQAA4NkAAERfAABOMTBlbXNjcmlwdGVuMTFtZW1vcnlfdmlld0loRUUAAODZAABsXwAATjEwZW1zY3JpcHRlbjExbWVtb3J5X3ZpZXdJc0VFAADg2QAAlF8AAE4xMGVtc2NyaXB0ZW4xMW1lbW9yeV92aWV3SXRFRQAA4NkAALxfAABOMTBlbXNjcmlwdGVuMTFtZW1vcnlfdmlld0lpRUUAAODZAADkXwAATjEwZW1zY3JpcHRlbjExbWVtb3J5X3ZpZXdJakVFAADg2QAADGAAAE4xMGVtc2NyaXB0ZW4xMW1lbW9yeV92aWV3SWxFRQAA4NkAADRgAABOMTBlbXNjcmlwdGVuMTFtZW1vcnlfdmlld0ltRUUAAODZAABcYAAATjEwZW1zY3JpcHRlbjExbWVtb3J5X3ZpZXdJeEVFAADg2QAAhGAAAE4xMGVtc2NyaXB0ZW4xMW1lbW9yeV92aWV3SXlFRQAA4NkAAKxgAABOMTBlbXNjcmlwdGVuMTFtZW1vcnlfdmlld0lmRUUAAODZAADUYAAATjEwZW1zY3JpcHRlbjExbWVtb3J5X3ZpZXdJZEVFAADg2QAA/GAAAAAAAAAAAAAAAAAAAAMAAAAEAAAABAAAAAYAAACD+aIARE5uAPwpFQDRVycA3TT1AGLbwAA8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SWNFRUVFAAAAAODZAADEpAAATlN0M19fMjEzYmFzaWNfaXN0cmVhbUljTlNfMTFjaGFyX3RyYWl0c0ljRUVFRQAAZNoAAAClAAAAAAAAAQAAALikAAAD9P//bAAAAAAAAADYpQAA7wAAAPAAAACU////lP///9ilAADxAAAA8gAAAFSlAACMpQAAoKUAAGilAABsAAAAAAAAADClAADhAAAA4gAAAJT///+U////MKUAAOMAAADkAAAATlN0M19fMjE0YmFzaWNfaWZzdHJlYW1JY05TXzExY2hhcl90cmFpdHNJY0VFRUUACNoAAKilAAAwpQAATlN0M19fMjEzYmFzaWNfZmlsZWJ1ZkljTlNfMTFjaGFyX3RyYWl0c0ljRUVFRQAACNoAAOSlAAD4pAAAAAAAAESmAADzAAAA9AAAAE5TdDNfXzI4aW9zX2Jhc2VFAAAA4NkAADCmAAAAAAAA0XSeAFedvSqAcFIP//8+JwoAAABkAAAA6AMAABAnAACghgEAQEIPAICWmAAA4fUFGAAAADUAAABxAAAAa////877//+Sv///AAAAAAAAAAD/////////////////////////////////////////////////////////////////AAECAwQFBgcICf////////8KCwwNDg8QERITFBUWFxgZGhscHR4fICEiI////////woLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIj/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wABAgQHAwYFAAAAAAAAAAIAAMADAADABAAAwAUAAMAGAADABwAAwAgAAMAJAADACgAAwAsAAMAMAADADQAAwA4AAMAPAADAEAAAwBEAAMASAADAEwAAwBQAAMAVAADAFgAAwBcAAMAYAADAGQAAwBoAAMAbAADAHAAAwB0AAMAeAADAHwAAwAAAALMBAADDAgAAwwMAAMMEAADDBQAAwwYAAMMHAADDCAAAwwkAAMMKAADDCwAAwwwAAMMNAADTDgAAww8AAMMAAAy7AQAMwwIA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ODY4Nzg4ODk5MDkxOTI5Mzk0OTU5Njk3OTg5OQAAAAAAAAAAAv8ABGQAIAAABP//BgABAAEAAQD//wH/Af//////Af8B/wH/Af8B/wH/Af8B//////8K/yAA//8D/wH/BP8eAAABBf//////YwAACGMA6AMCAAAA//////8AAAAB/wH//////////////wAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAB/wH//////wABIAAEAIAAAAj//wH/Af////////8B/wb/B/8I/wn//////7wCvAIBAP//AQABAP//AAD//////////wAAAAAAAAAAAAAAAAAAAAAUAXj//wEACv///////////wH/Af8AAAAAAAAB/wH/Af8AAAAAAAAAAAAAAAAAAAAAAAAB/wAAAAAAAAH/Af8BAAAAAQAAAAH//////wAAAAAB////AAAAAP////////////8oAAr//////wEACv////8A//////////8BvggB/wH///8BAP//////////////////Cv//////DP8N/04xMF9fY3h4YWJpdjExNl9fc2hpbV90eXBlX2luZm9FAAAI2gAAFtgAACzcAABOMTBfX2N4eGFiaXYxMTdfX2NsYXNzX3R5cGVfaW5mb0UAAAAI2gAARNgAADjYAABOMTBfX2N4eGFiaXYxMTdfX3BiYXNlX3R5cGVfaW5mb0UAAAAI2gAAdNgAADjYAABOMTBfX2N4eGFiaXYxMTlfX3BvaW50ZXJfdHlwZV9pbmZvRQAI2gAApNgAAJjYAAAAAAAAGNkAAP8BAAAAAgAAAQIAAAICAAADAgAATjEwX19jeHhhYml2MTIzX19mdW5kYW1lbnRhbF90eXBlX2luZm9FAAjaAADw2AAAONgAAHYAAADc2AAAJNkAAGIAAADc2AAAMNkAAGMAAADc2AAAPNkAAGgAAADc2AAASNkAAGEAAADc2AAAVNkAAHMAAADc2AAAYNkAAHQAAADc2AAAbNkAAGkAAADc2AAAeNkAAGoAAADc2AAAhNkAAGwAAADc2AAAkNkAAG0AAADc2AAAnNkAAHgAAADc2AAAqNkAAHkAAADc2AAAtNkAAGYAAADc2AAAwNkAAGQAAADc2AAAzNkAAAAAAABo2AAA/wEAAAQCAAABAgAAAgIAAAUCAAAGAgAABwIAAAgCAAAAAAAAUNoAAP8BAAAJAgAAAQIAAAICAAAFAgAACgIAAAsCAAAMAgAATjEwX19jeHhhYml2MTIwX19zaV9jbGFzc190eXBlX2luZm9FAAAAAAjaAAAo2gAAaNgAAAAAAACs2gAA/wEAAA0CAAABAgAAAgIAAAUCAAAOAgAADwIAABACAABOMTBfX2N4eGFiaXYxMjFfX3ZtaV9jbGFzc190eXBlX2luZm9FAAAACNoAAITaAABo2AAAAAAAABzbAAAIAAAAEQIAABICAAAAAAAARNsAAAgAAAATAgAAFAIAAAAAAAAE2wAACAAAABUCAAAWAgAAU3Q5ZXhjZXB0aW9uAAAAAODZAAD02gAAU3Q5YmFkX2FsbG9jAAAAAAjaAAAM2wAABNsAAFN0MjBiYWRfYXJyYXlfbmV3X2xlbmd0aAAAAAAI2gAAKNsAABzbAAAAAAAAiNsAAAcAAAAXAgAAGAIAAAAAAAAQ3AAAGQIAABoCAAD8AQAAU3QxMWxvZ2ljX2Vycm9yAAjaAAB42wAABNsAAAAAAAC82wAABwAAABsCAAAYAgAAU3QxMmxlbmd0aF9lcnJvcgAAAAAI2gAAqNsAAIjbAAAAAAAA8NsAAAcAAAAcAgAAGAIAAFN0MTJvdXRfb2ZfcmFuZ2UAAAAACNoAANzbAACI2wAAU3QxM3J1bnRpbWVfZXJyb3IAAAAI2gAA/NsAAATbAABTdDl0eXBlX2luZm8AAAAA4NkAABzcAAABFT0AAAA+AAAAAAAAAD8AAAAAAAAAWAFoUNwAAFDcAAAAAAEAAAIAAAAAAAAFAAAAAAAAAAAAAAC9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC7AAAAugAAAOA9DQAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAA//////////8BWZjcAAAAAAAABQAAAAAAAAAAAAAAywAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAuwAAAMwAAADoPQ0AAAQAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAP////8KAQww3QAAwEwOAP4BAAA=";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}var binary=tryParseAsDataURI(file);if(binary){return binary}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}function getBinaryPromise(binaryFile){return Promise.resolve().then(()=>getBinarySync(binaryFile))}function instantiateArrayBuffer(binaryFile,imports,receiver){return getBinaryPromise(binaryFile).then(binary=>WebAssembly.instantiate(binary,imports)).then(instance=>instance).then(receiver,reason=>{err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)})}function instantiateAsync(binary,binaryFile,imports,callback){return instantiateArrayBuffer(binaryFile,imports,callback)}function createWasm(){var info={"a":wasmImports};function receiveInstance(instance,module){var exports=instance.exports;wasmExports=exports;registerTLSInit(wasmExports["_"]);wasmTable=wasmExports["X"];addOnInit(wasmExports["W"]);wasmModule=module;removeRunDependency("wasm-instantiate");return exports}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){receiveInstance(result["instance"],result["module"])}if(Module["instantiateWasm"]){try{return Module["instantiateWasm"](info,receiveInstance)}catch(e){err(`Module.instantiateWasm callback failed with error: ${e}`);readyPromiseReject(e)}}instantiateAsync(wasmBinary,wasmBinaryFile,info,receiveInstantiationResult).catch(readyPromiseReject);return{}}var tempDouble;var tempI64;function ExitStatus(status){this.name="ExitStatus";this.message=`Program terminated with exit(${status})`;this.status=status}var terminateWorker=worker=>{worker.terminate();worker.onmessage=e=>{}};var killThread=pthread_ptr=>{var worker=PThread.pthreads[pthread_ptr];delete PThread.pthreads[pthread_ptr];terminateWorker(worker);__emscripten_thread_free_data(pthread_ptr);PThread.runningWorkers.splice(PThread.runningWorkers.indexOf(worker),1);worker.pthread_ptr=0};var cancelThread=pthread_ptr=>{var worker=PThread.pthreads[pthread_ptr];worker.postMessage({"cmd":"cancel"})};var cleanupThread=pthread_ptr=>{var worker=PThread.pthreads[pthread_ptr];assert(worker);PThread.returnWorkerToPool(worker)};var spawnThread=threadParams=>{var worker=PThread.getNewWorker();if(!worker){return 6}PThread.runningWorkers.push(worker);PThread.pthreads[threadParams.pthread_ptr]=worker;worker.pthread_ptr=threadParams.pthread_ptr;var msg={"cmd":"run","start_routine":threadParams.startRoutine,"arg":threadParams.arg,"pthread_ptr":threadParams.pthread_ptr};if(ENVIRONMENT_IS_NODE){worker.unref()}worker.postMessage(msg,threadParams.transferList);return 0};var PATH={isAbs:path=>path.charAt(0)==="/",splitPath:filename=>{var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:(parts,allowAboveRoot)=>{var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:path=>{var isAbsolute=PATH.isAbs(path),trailingSlash=path.substr(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(p=>!!p),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:path=>{var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.substr(0,dir.length-1)}return root+dir},basename:path=>{if(path==="/")return"/";path=PATH.normalize(path);path=path.replace(/\/$/,"");var lastSlash=path.lastIndexOf("/");if(lastSlash===-1)return path;return path.substr(lastSlash+1)},join:function(){var paths=Array.prototype.slice.call(arguments);return PATH.normalize(paths.join("/"))},join2:(l,r)=>PATH.normalize(l+"/"+r)};var initRandomFill=()=>{if(typeof crypto=="object"&&typeof crypto["getRandomValues"]=="function"){return view=>(view.set(crypto.getRandomValues(new Uint8Array(view.byteLength))),view)}else if(ENVIRONMENT_IS_NODE){try{var crypto_module=require("crypto");var randomFillSync=crypto_module["randomFillSync"];if(randomFillSync){return view=>crypto_module["randomFillSync"](view)}var randomBytes=crypto_module["randomBytes"];return view=>(view.set(randomBytes(view.byteLength)),view)}catch(e){}}abort("initRandomDevice")};var randomFill=view=>(randomFill=initRandomFill())(view);var PATH_FS={resolve:function(){var resolvedPath="",resolvedAbsolute=false;for(var i=arguments.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?arguments[i]:FS.cwd();if(typeof path!="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=PATH.isAbs(path)}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(p=>!!p),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:(from,to)=>{from=PATH_FS.resolve(from).substr(1);to=PATH_FS.resolve(to).substr(1);function trim(arr){var start=0;for(;start<arr.length;start++){if(arr[start]!=="")break}var end=arr.length-1;for(;end>=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i<length;i++){if(fromParts[i]!==toParts[i]){samePartsLength=i;break}}var outputParts=[];for(var i=samePartsLength;i<fromParts.length;i++){outputParts.push("..")}outputParts=outputParts.concat(toParts.slice(samePartsLength));return outputParts.join("/")}};var UTF8Decoder=typeof TextDecoder!="undefined"?new TextDecoder("utf8"):undefined;var UTF8ArrayToString=(heapOrArray,idx,maxBytesToRead)=>{var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heapOrArray[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.buffer instanceof SharedArrayBuffer?heapOrArray.slice(idx,endPtr):heapOrArray.subarray(idx,endPtr))}var str="";while(idx<endPtr){var u0=heapOrArray[idx++];if(!(u0&128)){str+=String.fromCharCode(u0);continue}var u1=heapOrArray[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}var u2=heapOrArray[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u0=(u0&7)<<18|u1<<12|u2<<6|heapOrArray[idx++]&63}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}return str};var FS_stdin_getChar_buffer=[];var lengthBytesUTF8=str=>{var len=0;for(var i=0;i<str.length;++i){var c=str.charCodeAt(i);if(c<=127){len++}else if(c<=2047){len+=2}else if(c>=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx};function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}var FS_stdin_getChar=()=>{if(!FS_stdin_getChar_buffer.length){var result=null;if(ENVIRONMENT_IS_NODE){var BUFSIZE=256;var buf=Buffer.alloc(BUFSIZE);var bytesRead=0;var fd=process.stdin.fd;try{bytesRead=fs.readSync(fd,buf)}catch(e){if(e.toString().includes("EOF"))bytesRead=0;else throw e}if(bytesRead>0){result=buf.slice(0,bytesRead).toString("utf-8")}else{result=null}}else if(typeof window!="undefined"&&typeof window.prompt=="function"){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else if(typeof readline=="function"){result=readline();if(result!==null){result+="\n"}}if(!result){return null}FS_stdin_getChar_buffer=intArrayFromString(result,true)}return FS_stdin_getChar_buffer.shift()};var TTY={ttys:[],init(){},shutdown(){},register(dev,ops){TTY.ttys[dev]={input:[],output:[],ops:ops};FS.registerDevice(dev,TTY.stream_ops)},stream_ops:{open(stream){var tty=TTY.ttys[stream.node.rdev];if(!tty){throw new FS.ErrnoError(43)}stream.tty=tty;stream.seekable=false},close(stream){stream.tty.ops.fsync(stream.tty)},fsync(stream){stream.tty.ops.fsync(stream.tty)},read(stream,buffer,offset,length,pos){if(!stream.tty||!stream.tty.ops.get_char){throw new FS.ErrnoError(60)}var bytesRead=0;for(var i=0;i<length;i++){var result;try{result=stream.tty.ops.get_char(stream.tty)}catch(e){throw new FS.ErrnoError(29)}if(result===undefined&&bytesRead===0){throw new FS.ErrnoError(6)}if(result===null||result===undefined)break;bytesRead++;buffer[offset+i]=result}if(bytesRead){stream.node.timestamp=Date.now()}return bytesRead},write(stream,buffer,offset,length,pos){if(!stream.tty||!stream.tty.ops.put_char){throw new FS.ErrnoError(60)}try{for(var i=0;i<length;i++){stream.tty.ops.put_char(stream.tty,buffer[offset+i])}}catch(e){throw new FS.ErrnoError(29)}if(length){stream.node.timestamp=Date.now()}return i}},default_tty_ops:{get_char(tty){return FS_stdin_getChar()},put_char(tty,val){if(val===null||val===10){out(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},fsync(tty){if(tty.output&&tty.output.length>0){out(UTF8ArrayToString(tty.output,0));tty.output=[]}},ioctl_tcgets(tty){return{c_iflag:25856,c_oflag:5,c_cflag:191,c_lflag:35387,c_cc:[3,28,127,21,4,0,1,0,17,19,26,0,18,15,23,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}},ioctl_tcsets(tty,optional_actions,data){return 0},ioctl_tiocgwinsz(tty){return[24,80]}},default_tty1_ops:{put_char(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},fsync(tty){if(tty.output&&tty.output.length>0){err(UTF8ArrayToString(tty.output,0));tty.output=[]}}}};var mmapAlloc=size=>{abort()};var MEMFS={ops_table:null,mount(mount){return MEMFS.createNode(null,"/",16384|511,0)},createNode(parent,name,mode,dev){if(FS.isBlkdev(mode)||FS.isFIFO(mode)){throw new FS.ErrnoError(63)}if(!MEMFS.ops_table){MEMFS.ops_table={dir:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,lookup:MEMFS.node_ops.lookup,mknod:MEMFS.node_ops.mknod,rename:MEMFS.node_ops.rename,unlink:MEMFS.node_ops.unlink,rmdir:MEMFS.node_ops.rmdir,readdir:MEMFS.node_ops.readdir,symlink:MEMFS.node_ops.symlink},stream:{llseek:MEMFS.stream_ops.llseek}},file:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:{llseek:MEMFS.stream_ops.llseek,read:MEMFS.stream_ops.read,write:MEMFS.stream_ops.write,allocate:MEMFS.stream_ops.allocate,mmap:MEMFS.stream_ops.mmap,msync:MEMFS.stream_ops.msync}},link:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,readlink:MEMFS.node_ops.readlink},stream:{}},chrdev:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:FS.chrdev_stream_ops}}}var node=FS.createNode(parent,name,mode,dev);if(FS.isDir(node.mode)){node.node_ops=MEMFS.ops_table.dir.node;node.stream_ops=MEMFS.ops_table.dir.stream;node.contents={}}else if(FS.isFile(node.mode)){node.node_ops=MEMFS.ops_table.file.node;node.stream_ops=MEMFS.ops_table.file.stream;node.usedBytes=0;node.contents=null}else if(FS.isLink(node.mode)){node.node_ops=MEMFS.ops_table.link.node;node.stream_ops=MEMFS.ops_table.link.stream}else if(FS.isChrdev(node.mode)){node.node_ops=MEMFS.ops_table.chrdev.node;node.stream_ops=MEMFS.ops_table.chrdev.stream}node.timestamp=Date.now();if(parent){parent.contents[name]=node;parent.timestamp=node.timestamp}return node},getFileDataAsTypedArray(node){if(!node.contents)return new Uint8Array(0);if(node.contents.subarray)return node.contents.subarray(0,node.usedBytes);return new Uint8Array(node.contents)},expandFileStorage(node,newCapacity){var prevCapacity=node.contents?node.contents.length:0;if(prevCapacity>=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity<CAPACITY_DOUBLING_MAX?2:1.125)>>>0);if(prevCapacity!=0)newCapacity=Math.max(newCapacity,256);var oldContents=node.contents;node.contents=new Uint8Array(newCapacity);if(node.usedBytes>0)node.contents.set(oldContents.subarray(0,node.usedBytes),0)},resizeFileStorage(node,newSize){if(node.usedBytes==newSize)return;if(newSize==0){node.contents=null;node.usedBytes=0}else{var oldContents=node.contents;node.contents=new Uint8Array(newSize);if(oldContents){node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)))}node.usedBytes=newSize}},node_ops:{getattr(node){var attr={};attr.dev=FS.isChrdev(node.mode)?node.id:1;attr.ino=node.id;attr.mode=node.mode;attr.nlink=1;attr.uid=0;attr.gid=0;attr.rdev=node.rdev;if(FS.isDir(node.mode)){attr.size=4096}else if(FS.isFile(node.mode)){attr.size=node.usedBytes}else if(FS.isLink(node.mode)){attr.size=node.link.length}else{attr.size=0}attr.atime=new Date(node.timestamp);attr.mtime=new Date(node.timestamp);attr.ctime=new Date(node.timestamp);attr.blksize=4096;attr.blocks=Math.ceil(attr.size/attr.blksize);return attr},setattr(node,attr){if(attr.mode!==undefined){node.mode=attr.mode}if(attr.timestamp!==undefined){node.timestamp=attr.timestamp}if(attr.size!==undefined){MEMFS.resizeFileStorage(node,attr.size)}},lookup(parent,name){throw FS.genericErrors[44]},mknod(parent,name,mode,dev){return MEMFS.createNode(parent,name,mode,dev)},rename(old_node,new_dir,new_name){if(FS.isDir(old_node.mode)){var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(new_node){for(var i in new_node.contents){throw new FS.ErrnoError(55)}}}delete old_node.parent.contents[old_node.name];old_node.parent.timestamp=Date.now();old_node.name=new_name;new_dir.contents[new_name]=old_node;new_dir.timestamp=old_node.parent.timestamp;old_node.parent=new_dir},unlink(parent,name){delete parent.contents[name];parent.timestamp=Date.now()},rmdir(parent,name){var node=FS.lookupNode(parent,name);for(var i in node.contents){throw new FS.ErrnoError(55)}delete parent.contents[name];parent.timestamp=Date.now()},readdir(node){var entries=[".",".."];for(var key in node.contents){if(!node.contents.hasOwnProperty(key)){continue}entries.push(key)}return entries},symlink(parent,newname,oldpath){var node=MEMFS.createNode(parent,newname,511|40960,0);node.link=oldpath;return node},readlink(node){if(!FS.isLink(node.mode)){throw new FS.ErrnoError(28)}return node.link}},stream_ops:{read(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);if(size>8&&contents.subarray){buffer.set(contents.subarray(position,position+size),offset)}else{for(var i=0;i<size;i++)buffer[offset+i]=contents[position+i]}return size},write(stream,buffer,offset,length,position,canOwn){if(buffer.buffer===GROWABLE_HEAP_I8().buffer){canOwn=false}if(!length)return 0;var node=stream.node;node.timestamp=Date.now();if(buffer.subarray&&(!node.contents||node.contents.subarray)){if(canOwn){node.contents=buffer.subarray(offset,offset+length);node.usedBytes=length;return length}else if(node.usedBytes===0&&position===0){node.contents=buffer.slice(offset,offset+length);node.usedBytes=length;return length}else if(position+length<=node.usedBytes){node.contents.set(buffer.subarray(offset,offset+length),position);return length}}MEMFS.expandFileStorage(node,position+length);if(node.contents.subarray&&buffer.subarray){node.contents.set(buffer.subarray(offset,offset+length),position)}else{for(var i=0;i<length;i++){node.contents[position+i]=buffer[offset+i]}}node.usedBytes=Math.max(node.usedBytes,position+length);return length},llseek(stream,offset,whence){var position=offset;if(whence===1){position+=stream.position}else if(whence===2){if(FS.isFile(stream.node.mode)){position+=stream.node.usedBytes}}if(position<0){throw new FS.ErrnoError(28)}return position},allocate(stream,offset,length){MEMFS.expandFileStorage(stream.node,offset+length);stream.node.usedBytes=Math.max(stream.node.usedBytes,offset+length)},mmap(stream,length,position,prot,flags){if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}var ptr;var allocated;var contents=stream.node.contents;if(!(flags&2)&&contents.buffer===GROWABLE_HEAP_I8().buffer){allocated=false;ptr=contents.byteOffset}else{if(position>0||position+length<contents.length){if(contents.subarray){contents=contents.subarray(position,position+length)}else{contents=Array.prototype.slice.call(contents,position,position+length)}}allocated=true;ptr=mmapAlloc(length);if(!ptr){throw new FS.ErrnoError(48)}GROWABLE_HEAP_I8().set(contents,ptr)}return{ptr:ptr,allocated:allocated}},msync(stream,buffer,offset,length,mmapFlags){MEMFS.stream_ops.write(stream,buffer,0,length,offset,false);return 0}}};var asyncLoad=(url,onload,onerror,noRunDep)=>{var dep=!noRunDep?getUniqueRunDependency(`al ${url}`):"";readAsync(url,arrayBuffer=>{assert(arrayBuffer,`Loading data file "${url}" failed (no arrayBuffer).`);onload(new Uint8Array(arrayBuffer));if(dep)removeRunDependency(dep)},event=>{if(onerror){onerror()}else{throw`Loading data file "${url}" failed.`}});if(dep)addRunDependency(dep)};var preloadPlugins=Module["preloadPlugins"]||[];var FS_handledByPreloadPlugin=(byteArray,fullname,finish,onerror)=>{if(typeof Browser!="undefined")Browser.init();var handled=false;preloadPlugins.forEach(plugin=>{if(handled)return;if(plugin["canHandle"](fullname)){plugin["handle"](byteArray,fullname,finish,onerror);handled=true}});return handled};var FS_createPreloadedFile=(parent,name,url,canRead,canWrite,onload,onerror,dontCreateFile,canOwn,preFinish)=>{var fullname=name?PATH_FS.resolve(PATH.join2(parent,name)):parent;var dep=getUniqueRunDependency(`cp ${fullname}`);function processData(byteArray){function finish(byteArray){if(preFinish)preFinish();if(!dontCreateFile){FS.createDataFile(parent,name,byteArray,canRead,canWrite,canOwn)}if(onload)onload();removeRunDependency(dep)}if(FS_handledByPreloadPlugin(byteArray,fullname,finish,()=>{if(onerror)onerror();removeRunDependency(dep)})){return}finish(byteArray)}addRunDependency(dep);if(typeof url=="string"){asyncLoad(url,byteArray=>processData(byteArray),onerror)}else{processData(url)}};var FS_modeStringToFlags=str=>{var flagModes={"r":0,"r+":2,"w":512|64|1,"w+":512|64|2,"a":1024|64|1,"a+":1024|64|2};var flags=flagModes[str];if(typeof flags=="undefined"){throw new Error(`Unknown file open mode: ${str}`)}return flags};var FS_getMode=(canRead,canWrite)=>{var mode=0;if(canRead)mode|=292|73;if(canWrite)mode|=146;return mode};var FS={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:null,currentPath:"/",initialized:false,ignorePermissions:true,ErrnoError:null,genericErrors:{},filesystems:null,syncFSRequests:0,lookupPath(path,opts={}){path=PATH_FS.resolve(path);if(!path)return{path:"",node:null};var defaults={follow_mount:true,recurse_count:0};opts=Object.assign(defaults,opts);if(opts.recurse_count>8){throw new FS.ErrnoError(32)}var parts=path.split("/").filter(p=>!!p);var current=FS.root;var current_path="/";for(var i=0;i<parts.length;i++){var islast=i===parts.length-1;if(islast&&opts.parent){break}current=FS.lookupNode(current,parts[i]);current_path=PATH.join2(current_path,parts[i]);if(FS.isMountpoint(current)){if(!islast||islast&&opts.follow_mount){current=current.mounted.root}}if(!islast||opts.follow){var count=0;while(FS.isLink(current.mode)){var link=FS.readlink(current_path);current_path=PATH_FS.resolve(PATH.dirname(current_path),link);var lookup=FS.lookupPath(current_path,{recurse_count:opts.recurse_count+1});current=lookup.node;if(count++>40){throw new FS.ErrnoError(32)}}}}return{path:current_path,node:current}},getPath(node){var path;while(true){if(FS.isRoot(node)){var mount=node.mount.mountpoint;if(!path)return mount;return mount[mount.length-1]!=="/"?`${mount}/${path}`:mount+path}path=path?`${node.name}/${path}`:node.name;node=node.parent}},hashName(parentid,name){var hash=0;for(var i=0;i<name.length;i++){hash=(hash<<5)-hash+name.charCodeAt(i)|0}return(parentid+hash>>>0)%FS.nameTable.length},hashAddNode(node){var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode(node){var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode(parent,name){var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode,parent)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode(parent,name,mode,rdev){var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode(node){FS.hashRemoveNode(node)},isRoot(node){return node===node.parent},isMountpoint(node){return!!node.mounted},isFile(mode){return(mode&61440)===32768},isDir(mode){return(mode&61440)===16384},isLink(mode){return(mode&61440)===40960},isChrdev(mode){return(mode&61440)===8192},isBlkdev(mode){return(mode&61440)===24576},isFIFO(mode){return(mode&61440)===4096},isSocket(mode){return(mode&49152)===49152},flagsToPermissionString(flag){var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions(node,perms){if(FS.ignorePermissions){return 0}if(perms.includes("r")&&!(node.mode&292)){return 2}else if(perms.includes("w")&&!(node.mode&146)){return 2}else if(perms.includes("x")&&!(node.mode&73)){return 2}return 0},mayLookup(dir){var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate(dir,name){try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete(dir,name,isdir){var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else{if(FS.isDir(node.mode)){return 31}}return 0},mayOpen(node,flags){if(!node){return 44}if(FS.isLink(node.mode)){return 32}else if(FS.isDir(node.mode)){if(FS.flagsToPermissionString(flags)!=="r"||flags&512){return 31}}return FS.nodePermissions(node,FS.flagsToPermissionString(flags))},MAX_OPEN_FDS:4096,nextfd(){for(var fd=0;fd<=FS.MAX_OPEN_FDS;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStreamChecked(fd){var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}return stream},getStream:fd=>FS.streams[fd],createStream(stream,fd=-1){if(!FS.FSStream){FS.FSStream=function(){this.shared={}};FS.FSStream.prototype={};Object.defineProperties(FS.FSStream.prototype,{object:{get(){return this.node},set(val){this.node=val}},isRead:{get(){return(this.flags&2097155)!==1}},isWrite:{get(){return(this.flags&2097155)!==0}},isAppend:{get(){return this.flags&1024}},flags:{get(){return this.shared.flags},set(val){this.shared.flags=val}},position:{get(){return this.shared.position},set(val){this.shared.position=val}}})}stream=Object.assign(new FS.FSStream,stream);if(fd==-1){fd=FS.nextfd()}stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream(fd){FS.streams[fd]=null},chrdev_stream_ops:{open(stream){var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;if(stream.stream_ops.open){stream.stream_ops.open(stream)}},llseek(){throw new FS.ErrnoError(70)}},major:dev=>dev>>8,minor:dev=>dev&255,makedev:(ma,mi)=>ma<<8|mi,registerDevice(dev,ops){FS.devices[dev]={stream_ops:ops}},getDevice:dev=>FS.devices[dev],getMounts(mount){var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push.apply(check,m.mounts)}return mounts},syncfs(populate,callback){if(typeof populate=="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err(`warning: ${FS.syncFSRequests} FS.syncfs operations in flight at once, probably just doing extra work`)}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}mounts.forEach(mount=>{if(!mount.type.syncfs){return done(null)}mount.type.syncfs(mount,populate,done)})},mount(type,opts,mountpoint){var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type:type,opts:opts,mountpoint:mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount(mountpoint){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);Object.keys(FS.nameTable).forEach(hash=>{var current=FS.nameTable[hash];while(current){var next=current.name_next;if(mounts.includes(current.mount)){FS.destroyNode(current)}current=next}});node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup(parent,name){return parent.node_ops.lookup(parent,name)},mknod(path,mode,dev){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name||name==="."||name===".."){throw new FS.ErrnoError(28)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},create(path,mode){mode=mode!==undefined?mode:438;mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir(path,mode){mode=mode!==undefined?mode:511;mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree(path,mode){var dirs=path.split("/");var d="";for(var i=0;i<dirs.length;++i){if(!dirs[i])continue;d+="/"+dirs[i];try{FS.mkdir(d,mode)}catch(e){if(e.errno!=20)throw e}}},mkdev(path,mode,dev){if(typeof dev=="undefined"){dev=mode;mode=438}mode|=8192;return FS.mknod(path,mode,dev)},symlink(oldpath,newpath){if(!PATH_FS.resolve(oldpath)){throw new FS.ErrnoError(44)}var lookup=FS.lookupPath(newpath,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var newname=PATH.basename(newpath);var errCode=FS.mayCreate(parent,newname);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.symlink){throw new FS.ErrnoError(63)}return parent.node_ops.symlink(parent,newname,oldpath)},rename(old_path,new_path){var old_dirname=PATH.dirname(old_path);var new_dirname=PATH.dirname(new_path);var old_name=PATH.basename(old_path);var new_name=PATH.basename(new_path);var lookup,old_dir,new_dir;lookup=FS.lookupPath(old_path,{parent:true});old_dir=lookup.node;lookup=FS.lookupPath(new_path,{parent:true});new_dir=lookup.node;if(!old_dir||!new_dir)throw new FS.ErrnoError(44);if(old_dir.mount!==new_dir.mount){throw new FS.ErrnoError(75)}var old_node=FS.lookupNode(old_dir,old_name);var relative=PATH_FS.relative(old_path,new_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(28)}relative=PATH_FS.relative(new_path,old_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(55)}var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(old_node===new_node){return}var isdir=FS.isDir(old_node.mode);var errCode=FS.mayDelete(old_dir,old_name,isdir);if(errCode){throw new FS.ErrnoError(errCode)}errCode=new_node?FS.mayDelete(new_dir,new_name,isdir):FS.mayCreate(new_dir,new_name);if(errCode){throw new FS.ErrnoError(errCode)}if(!old_dir.node_ops.rename){throw new FS.ErrnoError(63)}if(FS.isMountpoint(old_node)||new_node&&FS.isMountpoint(new_node)){throw new FS.ErrnoError(10)}if(new_dir!==old_dir){errCode=FS.nodePermissions(old_dir,"w");if(errCode){throw new FS.ErrnoError(errCode)}}FS.hashRemoveNode(old_node);try{old_dir.node_ops.rename(old_node,new_dir,new_name)}catch(e){throw e}finally{FS.hashAddNode(old_node)}},rmdir(path){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,true);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.rmdir){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.rmdir(parent,name);FS.destroyNode(node)},readdir(path){var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;if(!node.node_ops.readdir){throw new FS.ErrnoError(54)}return node.node_ops.readdir(node)},unlink(path){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,false);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.unlink){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.unlink(parent,name);FS.destroyNode(node)},readlink(path){var lookup=FS.lookupPath(path);var link=lookup.node;if(!link){throw new FS.ErrnoError(44)}if(!link.node_ops.readlink){throw new FS.ErrnoError(28)}return PATH_FS.resolve(FS.getPath(link.parent),link.node_ops.readlink(link))},stat(path,dontFollow){var lookup=FS.lookupPath(path,{follow:!dontFollow});var node=lookup.node;if(!node){throw new FS.ErrnoError(44)}if(!node.node_ops.getattr){throw new FS.ErrnoError(63)}return node.node_ops.getattr(node)},lstat(path){return FS.stat(path,true)},chmod(path,mode,dontFollow){var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}if(!node.node_ops.setattr){throw new FS.ErrnoError(63)}node.node_ops.setattr(node,{mode:mode&4095|node.mode&~4095,timestamp:Date.now()})},lchmod(path,mode){FS.chmod(path,mode,true)},fchmod(fd,mode){var stream=FS.getStreamChecked(fd);FS.chmod(stream.node,mode)},chown(path,uid,gid,dontFollow){var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}if(!node.node_ops.setattr){throw new FS.ErrnoError(63)}node.node_ops.setattr(node,{timestamp:Date.now()})},lchown(path,uid,gid){FS.chown(path,uid,gid,true)},fchown(fd,uid,gid){var stream=FS.getStreamChecked(fd);FS.chown(stream.node,uid,gid)},truncate(path,len){if(len<0){throw new FS.ErrnoError(28)}var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:true});node=lookup.node}else{node=path}if(!node.node_ops.setattr){throw new FS.ErrnoError(63)}if(FS.isDir(node.mode)){throw new FS.ErrnoError(31)}if(!FS.isFile(node.mode)){throw new FS.ErrnoError(28)}var errCode=FS.nodePermissions(node,"w");if(errCode){throw new FS.ErrnoError(errCode)}node.node_ops.setattr(node,{size:len,timestamp:Date.now()})},ftruncate(fd,len){var stream=FS.getStreamChecked(fd);if((stream.flags&2097155)===0){throw new FS.ErrnoError(28)}FS.truncate(stream.node,len)},utime(path,atime,mtime){var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;node.node_ops.setattr(node,{timestamp:Math.max(atime,mtime)})},open(path,flags,mode){if(path===""){throw new FS.ErrnoError(44)}flags=typeof flags=="string"?FS_modeStringToFlags(flags):flags;mode=typeof mode=="undefined"?438:mode;if(flags&64){mode=mode&4095|32768}else{mode=0}var node;if(typeof path=="object"){node=path}else{path=PATH.normalize(path);try{var lookup=FS.lookupPath(path,{follow:!(flags&131072)});node=lookup.node}catch(e){}}var created=false;if(flags&64){if(node){if(flags&128){throw new FS.ErrnoError(20)}}else{node=FS.mknod(path,mode,0);created=true}}if(!node){throw new FS.ErrnoError(44)}if(FS.isChrdev(node.mode)){flags&=~512}if(flags&65536&&!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}if(!created){var errCode=FS.mayOpen(node,flags);if(errCode){throw new FS.ErrnoError(errCode)}}if(flags&512&&!created){FS.truncate(node,0)}flags&=~(128|512|131072);var stream=FS.createStream({node:node,path:FS.getPath(node),flags:flags,seekable:true,position:0,stream_ops:node.stream_ops,ungotten:[],error:false});if(stream.stream_ops.open){stream.stream_ops.open(stream)}if(Module["logReadFiles"]&&!(flags&1)){if(!FS.readFiles)FS.readFiles={};if(!(path in FS.readFiles)){FS.readFiles[path]=1}}return stream},close(stream){if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(stream.getdents)stream.getdents=null;try{if(stream.stream_ops.close){stream.stream_ops.close(stream)}}catch(e){throw e}finally{FS.closeStream(stream.fd)}stream.fd=null},isClosed(stream){return stream.fd===null},llseek(stream,offset,whence){if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(!stream.seekable||!stream.stream_ops.llseek){throw new FS.ErrnoError(70)}if(whence!=0&&whence!=1&&whence!=2){throw new FS.ErrnoError(28)}stream.position=stream.stream_ops.llseek(stream,offset,whence);stream.ungotten=[];return stream.position},read(stream,buffer,offset,length,position){if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.read){throw new FS.ErrnoError(28)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesRead=stream.stream_ops.read(stream,buffer,offset,length,position);if(!seeking)stream.position+=bytesRead;return bytesRead},write(stream,buffer,offset,length,position,canOwn){if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.write){throw new FS.ErrnoError(28)}if(stream.seekable&&stream.flags&1024){FS.llseek(stream,0,2)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesWritten=stream.stream_ops.write(stream,buffer,offset,length,position,canOwn);if(!seeking)stream.position+=bytesWritten;return bytesWritten},allocate(stream,offset,length){if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(offset<0||length<=0){throw new FS.ErrnoError(28)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(8)}if(!FS.isFile(stream.node.mode)&&!FS.isDir(stream.node.mode)){throw new FS.ErrnoError(43)}if(!stream.stream_ops.allocate){throw new FS.ErrnoError(138)}stream.stream_ops.allocate(stream,offset,length)},mmap(stream,length,position,prot,flags){if((prot&2)!==0&&(flags&2)===0&&(stream.flags&2097155)!==2){throw new FS.ErrnoError(2)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(2)}if(!stream.stream_ops.mmap){throw new FS.ErrnoError(43)}return stream.stream_ops.mmap(stream,length,position,prot,flags)},msync(stream,buffer,offset,length,mmapFlags){if(!stream.stream_ops.msync){return 0}return stream.stream_ops.msync(stream,buffer,offset,length,mmapFlags)},munmap:stream=>0,ioctl(stream,cmd,arg){if(!stream.stream_ops.ioctl){throw new FS.ErrnoError(59)}return stream.stream_ops.ioctl(stream,cmd,arg)},readFile(path,opts={}){opts.flags=opts.flags||0;opts.encoding=opts.encoding||"binary";if(opts.encoding!=="utf8"&&opts.encoding!=="binary"){throw new Error(`Invalid encoding type "${opts.encoding}"`)}var ret;var stream=FS.open(path,opts.flags);var stat=FS.stat(path);var length=stat.size;var buf=new Uint8Array(length);FS.read(stream,buf,0,length,0);if(opts.encoding==="utf8"){ret=UTF8ArrayToString(buf,0)}else if(opts.encoding==="binary"){ret=buf}FS.close(stream);return ret},writeFile(path,data,opts={}){opts.flags=opts.flags||577;var stream=FS.open(path,opts.flags,opts.mode);if(typeof data=="string"){var buf=new Uint8Array(lengthBytesUTF8(data)+1);var actualNumBytes=stringToUTF8Array(data,buf,0,buf.length);FS.write(stream,buf,0,actualNumBytes,undefined,opts.canOwn)}else if(ArrayBuffer.isView(data)){FS.write(stream,data,0,data.byteLength,undefined,opts.canOwn)}else{throw new Error("Unsupported data type")}FS.close(stream)},cwd:()=>FS.currentPath,chdir(path){var lookup=FS.lookupPath(path,{follow:true});if(lookup.node===null){throw new FS.ErrnoError(44)}if(!FS.isDir(lookup.node.mode)){throw new FS.ErrnoError(54)}var errCode=FS.nodePermissions(lookup.node,"x");if(errCode){throw new FS.ErrnoError(errCode)}FS.currentPath=lookup.path},createDefaultDirectories(){FS.mkdir("/tmp");FS.mkdir("/home");FS.mkdir("/home/web_user")},createDefaultDevices(){FS.mkdir("/dev");FS.registerDevice(FS.makedev(1,3),{read:()=>0,write:(stream,buffer,offset,length,pos)=>length});FS.mkdev("/dev/null",FS.makedev(1,3));TTY.register(FS.makedev(5,0),TTY.default_tty_ops);TTY.register(FS.makedev(6,0),TTY.default_tty1_ops);FS.mkdev("/dev/tty",FS.makedev(5,0));FS.mkdev("/dev/tty1",FS.makedev(6,0));var randomBuffer=new Uint8Array(1024),randomLeft=0;var randomByte=()=>{if(randomLeft===0){randomLeft=randomFill(randomBuffer).byteLength}return randomBuffer[--randomLeft]};FS.createDevice("/dev","random",randomByte);FS.createDevice("/dev","urandom",randomByte);FS.mkdir("/dev/shm");FS.mkdir("/dev/shm/tmp")},createSpecialDirectories(){FS.mkdir("/proc");var proc_self=FS.mkdir("/proc/self");FS.mkdir("/proc/self/fd");FS.mount({mount(){var node=FS.createNode(proc_self,"fd",16384|511,73);node.node_ops={lookup(parent,name){var fd=+name;var stream=FS.getStreamChecked(fd);var ret={parent:null,mount:{mountpoint:"fake"},node_ops:{readlink:()=>stream.path}};ret.parent=ret;return ret}};return node}},{},"/proc/self/fd")},createStandardStreams(){if(Module["stdin"]){FS.createDevice("/dev","stdin",Module["stdin"])}else{FS.symlink("/dev/tty","/dev/stdin")}if(Module["stdout"]){FS.createDevice("/dev","stdout",null,Module["stdout"])}else{FS.symlink("/dev/tty","/dev/stdout")}if(Module["stderr"]){FS.createDevice("/dev","stderr",null,Module["stderr"])}else{FS.symlink("/dev/tty1","/dev/stderr")}var stdin=FS.open("/dev/stdin",0);var stdout=FS.open("/dev/stdout",1);var stderr=FS.open("/dev/stderr",1)},ensureErrnoError(){if(FS.ErrnoError)return;FS.ErrnoError=function ErrnoError(errno,node){this.name="ErrnoError";this.node=node;this.setErrno=function(errno){this.errno=errno};this.setErrno(errno);this.message="FS error"};FS.ErrnoError.prototype=new Error;FS.ErrnoError.prototype.constructor=FS.ErrnoError;[44].forEach(code=>{FS.genericErrors[code]=new FS.ErrnoError(code);FS.genericErrors[code].stack="<generic error, no stack>"})},staticInit(){FS.ensureErrnoError();FS.nameTable=new Array(4096);FS.mount(MEMFS,{},"/");FS.createDefaultDirectories();FS.createDefaultDevices();FS.createSpecialDirectories();FS.filesystems={"MEMFS":MEMFS}},init(input,output,error){FS.init.initialized=true;FS.ensureErrnoError();Module["stdin"]=input||Module["stdin"];Module["stdout"]=output||Module["stdout"];Module["stderr"]=error||Module["stderr"];FS.createStandardStreams()},quit(){FS.init.initialized=false;for(var i=0;i<FS.streams.length;i++){var stream=FS.streams[i];if(!stream){continue}FS.close(stream)}},findObject(path,dontResolveLastLink){var ret=FS.analyzePath(path,dontResolveLastLink);if(!ret.exists){return null}return ret.object},analyzePath(path,dontResolveLastLink){try{var lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});path=lookup.path}catch(e){}var ret={isRoot:false,exists:false,error:0,name:null,path:null,object:null,parentExists:false,parentPath:null,parentObject:null};try{var lookup=FS.lookupPath(path,{parent:true});ret.parentExists=true;ret.parentPath=lookup.path;ret.parentObject=lookup.node;ret.name=PATH.basename(path);lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});ret.exists=true;ret.path=lookup.path;ret.object=lookup.node;ret.name=lookup.node.name;ret.isRoot=lookup.path==="/"}catch(e){ret.error=e.errno}return ret},createPath(parent,path,canRead,canWrite){parent=typeof parent=="string"?parent:FS.getPath(parent);var parts=path.split("/").reverse();while(parts.length){var part=parts.pop();if(!part)continue;var current=PATH.join2(parent,part);try{FS.mkdir(current)}catch(e){}parent=current}return current},createFile(parent,name,properties,canRead,canWrite){var path=PATH.join2(typeof parent=="string"?parent:FS.getPath(parent),name);var mode=FS_getMode(canRead,canWrite);return FS.create(path,mode)},createDataFile(parent,name,data,canRead,canWrite,canOwn){var path=name;if(parent){parent=typeof parent=="string"?parent:FS.getPath(parent);path=name?PATH.join2(parent,name):parent}var mode=FS_getMode(canRead,canWrite);var node=FS.create(path,mode);if(data){if(typeof data=="string"){var arr=new Array(data.length);for(var i=0,len=data.length;i<len;++i)arr[i]=data.charCodeAt(i);data=arr}FS.chmod(node,mode|146);var stream=FS.open(node,577);FS.write(stream,data,0,data.length,0,canOwn);FS.close(stream);FS.chmod(node,mode)}return node},createDevice(parent,name,input,output){var path=PATH.join2(typeof parent=="string"?parent:FS.getPath(parent),name);var mode=FS_getMode(!!input,!!output);if(!FS.createDevice.major)FS.createDevice.major=64;var dev=FS.makedev(FS.createDevice.major++,0);FS.registerDevice(dev,{open(stream){stream.seekable=false},close(stream){if(output&&output.buffer&&output.buffer.length){output(10)}},read(stream,buffer,offset,length,pos){var bytesRead=0;for(var i=0;i<length;i++){var result;try{result=input()}catch(e){throw new FS.ErrnoError(29)}if(result===undefined&&bytesRead===0){throw new FS.ErrnoError(6)}if(result===null||result===undefined)break;bytesRead++;buffer[offset+i]=result}if(bytesRead){stream.node.timestamp=Date.now()}return bytesRead},write(stream,buffer,offset,length,pos){for(var i=0;i<length;i++){try{output(buffer[offset+i])}catch(e){throw new FS.ErrnoError(29)}}if(length){stream.node.timestamp=Date.now()}return i}});return FS.mkdev(path,mode,dev)},forceLoadFile(obj){if(obj.isDevice||obj.isFolder||obj.link||obj.contents)return true;if(typeof XMLHttpRequest!="undefined"){throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.")}else if(read_){try{obj.contents=intArrayFromString(read_(obj.url),true);obj.usedBytes=obj.contents.length}catch(e){throw new FS.ErrnoError(29)}}else{throw new Error("Cannot load without read() or XMLHttpRequest.")}},createLazyFile(parent,name,url,canRead,canWrite){function LazyUint8Array(){this.lengthKnown=false;this.chunks=[]}LazyUint8Array.prototype.get=function LazyUint8Array_get(idx){if(idx>this.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]};LazyUint8Array.prototype.setDataGetter=function LazyUint8Array_setDataGetter(getter){this.getter=getter};LazyUint8Array.prototype.cacheLength=function LazyUint8Array_cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=(from,to)=>{if(from>to)throw new Error("invalid range ("+from+", "+to+") or no bytes requested!");if(to>datalength-1)throw new Error("only "+datalength+" bytes available! programmer error!");var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}return intArrayFromString(xhr.responseText||"",true)};var lazyArray=this;lazyArray.setDataGetter(chunkNum=>{var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]=="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]=="undefined")throw new Error("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true};if(typeof XMLHttpRequest!="undefined"){if(!ENVIRONMENT_IS_WORKER)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var lazyArray=new LazyUint8Array;Object.defineProperties(lazyArray,{length:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._length}},chunkSize:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}});var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url:url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};var keys=Object.keys(node.stream_ops);keys.forEach(key=>{var fn=node.stream_ops[key];stream_ops[key]=function forceLoadLazyFile(){FS.forceLoadFile(node);return fn.apply(null,arguments)}});function writeChunks(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i<size;i++){buffer[offset+i]=contents[position+i]}}else{for(var i=0;i<size;i++){buffer[offset+i]=contents.get(position+i)}}return size}stream_ops.read=(stream,buffer,offset,length,position)=>{FS.forceLoadFile(node);return writeChunks(stream,buffer,offset,length,position)};stream_ops.mmap=(stream,length,position,prot,flags)=>{FS.forceLoadFile(node);var ptr=mmapAlloc(length);if(!ptr){throw new FS.ErrnoError(48)}writeChunks(stream,GROWABLE_HEAP_I8(),ptr,length,position);return{ptr:ptr,allocated:true}};node.stream_ops=stream_ops;return node}};var UTF8ToString=(ptr,maxBytesToRead)=>ptr?UTF8ArrayToString(GROWABLE_HEAP_U8(),ptr,maxBytesToRead):"";var SYSCALLS={DEFAULT_POLLMASK:5,calculateAt(dirfd,path,allowEmpty){if(PATH.isAbs(path)){return path}var dir;if(dirfd===-100){dir=FS.cwd()}else{var dirstream=SYSCALLS.getStreamFromFD(dirfd);dir=dirstream.path}if(path.length==0){if(!allowEmpty){throw new FS.ErrnoError(44)}return dir}return PATH.join2(dir,path)},doStat(func,path,buf){try{var stat=func(path)}catch(e){if(e&&e.node&&PATH.normalize(path)!==PATH.normalize(FS.getPath(e.node))){return-54}throw e}GROWABLE_HEAP_I32()[buf>>2]=stat.dev;GROWABLE_HEAP_I32()[buf+4>>2]=stat.mode;GROWABLE_HEAP_U32()[buf+8>>2]=stat.nlink;GROWABLE_HEAP_I32()[buf+12>>2]=stat.uid;GROWABLE_HEAP_I32()[buf+16>>2]=stat.gid;GROWABLE_HEAP_I32()[buf+20>>2]=stat.rdev;tempI64=[stat.size>>>0,(tempDouble=stat.size,+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],GROWABLE_HEAP_I32()[buf+24>>2]=tempI64[0],GROWABLE_HEAP_I32()[buf+28>>2]=tempI64[1];GROWABLE_HEAP_I32()[buf+32>>2]=4096;GROWABLE_HEAP_I32()[buf+36>>2]=stat.blocks;var atime=stat.atime.getTime();var mtime=stat.mtime.getTime();var ctime=stat.ctime.getTime();tempI64=[Math.floor(atime/1e3)>>>0,(tempDouble=Math.floor(atime/1e3),+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],GROWABLE_HEAP_I32()[buf+40>>2]=tempI64[0],GROWABLE_HEAP_I32()[buf+44>>2]=tempI64[1];GROWABLE_HEAP_U32()[buf+48>>2]=atime%1e3*1e3;tempI64=[Math.floor(mtime/1e3)>>>0,(tempDouble=Math.floor(mtime/1e3),+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],GROWABLE_HEAP_I32()[buf+56>>2]=tempI64[0],GROWABLE_HEAP_I32()[buf+60>>2]=tempI64[1];GROWABLE_HEAP_U32()[buf+64>>2]=mtime%1e3*1e3;tempI64=[Math.floor(ctime/1e3)>>>0,(tempDouble=Math.floor(ctime/1e3),+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],GROWABLE_HEAP_I32()[buf+72>>2]=tempI64[0],GROWABLE_HEAP_I32()[buf+76>>2]=tempI64[1];GROWABLE_HEAP_U32()[buf+80>>2]=ctime%1e3*1e3;tempI64=[stat.ino>>>0,(tempDouble=stat.ino,+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],GROWABLE_HEAP_I32()[buf+88>>2]=tempI64[0],GROWABLE_HEAP_I32()[buf+92>>2]=tempI64[1];return 0},doMsync(addr,stream,len,flags,offset){if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}if(flags&2){return 0}var buffer=GROWABLE_HEAP_U8().slice(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},varargs:undefined,get(){var ret=GROWABLE_HEAP_I32()[SYSCALLS.varargs>>2];SYSCALLS.varargs+=4;return ret},getp(){return SYSCALLS.get()},getStr(ptr){var ret=UTF8ToString(ptr);return ret},getStreamFromFD(fd){var stream=FS.getStreamChecked(fd);return stream}};function _proc_exit(code){if(ENVIRONMENT_IS_PTHREAD)return proxyToMainThread(0,1,code);EXITSTATUS=code;if(!keepRuntimeAlive()){PThread.terminateAllThreads();if(Module["onExit"])Module["onExit"](code);ABORT=true}quit_(code,new ExitStatus(code))}var exitJS=(status,implicit)=>{EXITSTATUS=status;if(ENVIRONMENT_IS_PTHREAD){exitOnMainThread(status);throw"unwind"}_proc_exit(status)};var _exit=exitJS;var handleException=e=>{if(e instanceof ExitStatus||e=="unwind"){return EXITSTATUS}quit_(1,e)};var PThread={unusedWorkers:[],runningWorkers:[],tlsInitFunctions:[],pthreads:{},init(){if(ENVIRONMENT_IS_PTHREAD){PThread.initWorker()}else{PThread.initMainThread()}},initMainThread(){var pthreadPoolSize=8;while(pthreadPoolSize--){PThread.allocateUnusedWorker()}addOnPreRun(()=>{addRunDependency("loading-workers");PThread.loadWasmModuleToAllWorkers(()=>removeRunDependency("loading-workers"))})},initWorker(){noExitRuntime=false},setExitStatus:status=>{EXITSTATUS=status},terminateAllThreads__deps:["$terminateWorker"],terminateAllThreads:()=>{for(var worker of PThread.runningWorkers){terminateWorker(worker)}for(var worker of PThread.unusedWorkers){terminateWorker(worker)}PThread.unusedWorkers=[];PThread.runningWorkers=[];PThread.pthreads=[]},returnWorkerToPool:worker=>{var pthread_ptr=worker.pthread_ptr;delete PThread.pthreads[pthread_ptr];PThread.unusedWorkers.push(worker);PThread.runningWorkers.splice(PThread.runningWorkers.indexOf(worker),1);worker.pthread_ptr=0;__emscripten_thread_free_data(pthread_ptr)},receiveObjectTransfer(data){},threadInitTLS(){PThread.tlsInitFunctions.forEach(f=>f())},loadWasmModuleToWorker:worker=>new Promise(onFinishedLoading=>{worker.onmessage=e=>{var d=e["data"];var cmd=d["cmd"];if(d["targetThread"]&&d["targetThread"]!=_pthread_self()){var targetWorker=PThread.pthreads[d["targetThread"]];if(targetWorker){targetWorker.postMessage(d,d["transferList"])}else{err(`Internal error! Worker sent a message "${cmd}" to target pthread ${d["targetThread"]}, but that thread no longer exists!`)}return}if(cmd==="checkMailbox"){checkMailbox()}else if(cmd==="spawnThread"){spawnThread(d)}else if(cmd==="cleanupThread"){cleanupThread(d["thread"])}else if(cmd==="killThread"){killThread(d["thread"])}else if(cmd==="cancelThread"){cancelThread(d["thread"])}else if(cmd==="loaded"){worker.loaded=true;if(ENVIRONMENT_IS_NODE&&!worker.pthread_ptr){worker.unref()}onFinishedLoading(worker)}else if(cmd==="alert"){alert(`Thread ${d["threadId"]}: ${d["text"]}`)}else if(d.target==="setimmediate"){worker.postMessage(d)}else if(cmd==="callHandler"){Module[d["handler"]](...d["args"])}else if(cmd){err(`worker sent an unknown command ${cmd}`)}};worker.onerror=e=>{var message="worker sent an error!";err(`${message} ${e.filename}:${e.lineno}: ${e.message}`);throw e};if(ENVIRONMENT_IS_NODE){worker.on("message",data=>worker.onmessage({data:data}));worker.on("error",e=>worker.onerror(e))}var handlers=[];var knownHandlers=["onExit","onAbort","print","printErr"];for(var handler of knownHandlers){if(Module.hasOwnProperty(handler)){handlers.push(handler)}}worker.postMessage({"cmd":"load","handlers":handlers,"urlOrBlob":Module["mainScriptUrlOrBlob"]||_scriptDir,"wasmMemory":wasmMemory,"wasmModule":wasmModule})}),loadWasmModuleToAllWorkers(onMaybeReady){if(ENVIRONMENT_IS_PTHREAD){return onMaybeReady()}let pthreadPoolReady=Promise.all(PThread.unusedWorkers.map(PThread.loadWasmModuleToWorker));pthreadPoolReady.then(onMaybeReady)},allocateUnusedWorker(){var worker;var pthreadMainJs=locateFile("libwhisper.worker.js");worker=new Worker(pthreadMainJs);PThread.unusedWorkers.push(worker)},getNewWorker(){if(PThread.unusedWorkers.length==0){PThread.allocateUnusedWorker();PThread.loadWasmModuleToWorker(PThread.unusedWorkers[0])}return PThread.unusedWorkers.pop()}};Module["PThread"]=PThread;var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var establishStackSpace=()=>{var pthread_ptr=_pthread_self();var stackHigh=GROWABLE_HEAP_I32()[pthread_ptr+52>>2];var stackSize=GROWABLE_HEAP_I32()[pthread_ptr+56>>2];var stackLow=stackHigh-stackSize;_emscripten_stack_set_limits(stackHigh,stackLow);stackRestore(stackHigh)};Module["establishStackSpace"]=establishStackSpace;function exitOnMainThread(returnCode){if(ENVIRONMENT_IS_PTHREAD)return proxyToMainThread(1,0,returnCode);_exit(returnCode)}var wasmTableMirror=[];var getWasmTableEntry=funcPtr=>{var func=wasmTableMirror[funcPtr];if(!func){if(funcPtr>=wasmTableMirror.length)wasmTableMirror.length=funcPtr+1;wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func};var invokeEntryPoint=(ptr,arg)=>{var result=getWasmTableEntry(ptr)(arg);function finish(result){if(keepRuntimeAlive()){PThread.setExitStatus(result)}else{__emscripten_thread_exit(result)}}finish(result)};Module["invokeEntryPoint"]=invokeEntryPoint;var registerTLSInit=tlsInitFunc=>{PThread.tlsInitFunctions.push(tlsInitFunc)};function ExceptionInfo(excPtr){this.excPtr=excPtr;this.ptr=excPtr-24;this.set_type=function(type){GROWABLE_HEAP_U32()[this.ptr+4>>2]=type};this.get_type=function(){return GROWABLE_HEAP_U32()[this.ptr+4>>2]};this.set_destructor=function(destructor){GROWABLE_HEAP_U32()[this.ptr+8>>2]=destructor};this.get_destructor=function(){return GROWABLE_HEAP_U32()[this.ptr+8>>2]};this.set_caught=function(caught){caught=caught?1:0;GROWABLE_HEAP_I8()[this.ptr+12>>0]=caught};this.get_caught=function(){return GROWABLE_HEAP_I8()[this.ptr+12>>0]!=0};this.set_rethrown=function(rethrown){rethrown=rethrown?1:0;GROWABLE_HEAP_I8()[this.ptr+13>>0]=rethrown};this.get_rethrown=function(){return GROWABLE_HEAP_I8()[this.ptr+13>>0]!=0};this.init=function(type,destructor){this.set_adjusted_ptr(0);this.set_type(type);this.set_destructor(destructor)};this.set_adjusted_ptr=function(adjustedPtr){GROWABLE_HEAP_U32()[this.ptr+16>>2]=adjustedPtr};this.get_adjusted_ptr=function(){return GROWABLE_HEAP_U32()[this.ptr+16>>2]};this.get_exception_ptr=function(){var isPointer=___cxa_is_pointer_type(this.get_type());if(isPointer){return GROWABLE_HEAP_U32()[this.excPtr>>2]}var adjusted=this.get_adjusted_ptr();if(adjusted!==0)return adjusted;return this.excPtr}}var exceptionLast=0;var uncaughtExceptionCount=0;var ___cxa_throw=(ptr,type,destructor)=>{var info=new ExceptionInfo(ptr);info.init(type,destructor);exceptionLast=ptr;uncaughtExceptionCount++;throw exceptionLast};var ___emscripten_init_main_thread_js=tb=>{__emscripten_thread_init(tb,!ENVIRONMENT_IS_WORKER,1,!ENVIRONMENT_IS_WEB,65536,false);PThread.threadInitTLS()};var ___emscripten_thread_cleanup=thread=>{if(!ENVIRONMENT_IS_PTHREAD)cleanupThread(thread);else postMessage({"cmd":"cleanupThread","thread":thread})};function pthreadCreateProxied(pthread_ptr,attr,startRoutine,arg){if(ENVIRONMENT_IS_PTHREAD)return proxyToMainThread(2,1,pthread_ptr,attr,startRoutine,arg);return ___pthread_create_js(pthread_ptr,attr,startRoutine,arg)}var ___pthread_create_js=(pthread_ptr,attr,startRoutine,arg)=>{if(typeof SharedArrayBuffer=="undefined"){err("Current environment does not support SharedArrayBuffer, pthreads are not available!");return 6}var transferList=[];var error=0;if(ENVIRONMENT_IS_PTHREAD&&(transferList.length===0||error)){return pthreadCreateProxied(pthread_ptr,attr,startRoutine,arg)}if(error)return error;var threadParams={startRoutine:startRoutine,pthread_ptr:pthread_ptr,arg:arg,transferList:transferList};if(ENVIRONMENT_IS_PTHREAD){threadParams.cmd="spawnThread";postMessage(threadParams,transferList);return 0}return spawnThread(threadParams)};var setErrNo=value=>{GROWABLE_HEAP_I32()[___errno_location()>>2]=value;return value};function ___syscall_fcntl64(fd,cmd,varargs){if(ENVIRONMENT_IS_PTHREAD)return proxyToMainThread(3,1,fd,cmd,varargs);SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=SYSCALLS.get();if(arg<0){return-28}while(FS.streams[arg]){arg++}var newStream;newStream=FS.createStream(stream,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=SYSCALLS.get();stream.flags|=arg;return 0}case 5:{var arg=SYSCALLS.getp();var offset=0;GROWABLE_HEAP_I16()[arg+offset>>1]=2;return 0}case 6:case 7:return 0;case 16:case 8:return-28;case 9:setErrNo(28);return-1;default:{return-28}}}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_ioctl(fd,op,varargs){if(ENVIRONMENT_IS_PTHREAD)return proxyToMainThread(4,1,fd,op,varargs);SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(op){case 21509:{if(!stream.tty)return-59;return 0}case 21505:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tcgets){var termios=stream.tty.ops.ioctl_tcgets(stream);var argp=SYSCALLS.getp();GROWABLE_HEAP_I32()[argp>>2]=termios.c_iflag||0;GROWABLE_HEAP_I32()[argp+4>>2]=termios.c_oflag||0;GROWABLE_HEAP_I32()[argp+8>>2]=termios.c_cflag||0;GROWABLE_HEAP_I32()[argp+12>>2]=termios.c_lflag||0;for(var i=0;i<32;i++){GROWABLE_HEAP_I8()[argp+i+17>>0]=termios.c_cc[i]||0}return 0}return 0}case 21510:case 21511:case 21512:{if(!stream.tty)return-59;return 0}case 21506:case 21507:case 21508:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tcsets){var argp=SYSCALLS.getp();var c_iflag=GROWABLE_HEAP_I32()[argp>>2];var c_oflag=GROWABLE_HEAP_I32()[argp+4>>2];var c_cflag=GROWABLE_HEAP_I32()[argp+8>>2];var c_lflag=GROWABLE_HEAP_I32()[argp+12>>2];var c_cc=[];for(var i=0;i<32;i++){c_cc.push(GROWABLE_HEAP_I8()[argp+i+17>>0])}return stream.tty.ops.ioctl_tcsets(stream.tty,op,{c_iflag:c_iflag,c_oflag:c_oflag,c_cflag:c_cflag,c_lflag:c_lflag,c_cc:c_cc})}return 0}case 21519:{if(!stream.tty)return-59;var argp=SYSCALLS.getp();GROWABLE_HEAP_I32()[argp>>2]=0;return 0}case 21520:{if(!stream.tty)return-59;return-28}case 21531:{var argp=SYSCALLS.getp();return FS.ioctl(stream,op,argp)}case 21523:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tiocgwinsz){var winsize=stream.tty.ops.ioctl_tiocgwinsz(stream.tty);var argp=SYSCALLS.getp();GROWABLE_HEAP_I16()[argp>>1]=winsize[0];GROWABLE_HEAP_I16()[argp+2>>1]=winsize[1]}return 0}case 21524:{if(!stream.tty)return-59;return 0}case 21515:{if(!stream.tty)return-59;return 0}default:return-28}}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_openat(dirfd,path,flags,varargs){if(ENVIRONMENT_IS_PTHREAD)return proxyToMainThread(5,1,dirfd,path,flags,varargs);SYSCALLS.varargs=varargs;try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);var mode=varargs?SYSCALLS.get():0;return FS.open(path,flags,mode).fd}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var __embind_register_bigint=(primitiveType,name,size,minRange,maxRange)=>{};var embind_init_charCodes=()=>{var codes=new Array(256);for(var i=0;i<256;++i){codes[i]=String.fromCharCode(i)}embind_charCodes=codes};var embind_charCodes=undefined;var readLatin1String=ptr=>{var ret="";var c=ptr;while(GROWABLE_HEAP_U8()[c]){ret+=embind_charCodes[GROWABLE_HEAP_U8()[c++]]}return ret};var awaitingDependencies={};var registeredTypes={};var typeDependencies={};var BindingError=undefined;var throwBindingError=message=>{throw new BindingError(message)};var InternalError=undefined;var throwInternalError=message=>{throw new InternalError(message)};var whenDependentTypesAreResolved=(myTypes,dependentTypes,getTypeConverters)=>{myTypes.forEach(function(type){typeDependencies[type]=dependentTypes});function onComplete(typeConverters){var myTypeConverters=getTypeConverters(typeConverters);if(myTypeConverters.length!==myTypes.length){throwInternalError("Mismatched type converter count")}for(var i=0;i<myTypes.length;++i){registerType(myTypes[i],myTypeConverters[i])}}var typeConverters=new Array(dependentTypes.length);var unregisteredTypes=[];var registered=0;dependentTypes.forEach((dt,i)=>{if(registeredTypes.hasOwnProperty(dt)){typeConverters[i]=registeredTypes[dt]}else{unregisteredTypes.push(dt);if(!awaitingDependencies.hasOwnProperty(dt)){awaitingDependencies[dt]=[]}awaitingDependencies[dt].push(()=>{typeConverters[i]=registeredTypes[dt];++registered;if(registered===unregisteredTypes.length){onComplete(typeConverters)}})}});if(0===unregisteredTypes.length){onComplete(typeConverters)}};function sharedRegisterType(rawType,registeredInstance,options={}){var name=registeredInstance.name;if(!rawType){throwBindingError(`type "${name}" must have a positive integer typeid pointer`)}if(registeredTypes.hasOwnProperty(rawType)){if(options.ignoreDuplicateRegistrations){return}else{throwBindingError(`Cannot register type '${name}' twice`)}}registeredTypes[rawType]=registeredInstance;delete typeDependencies[rawType];if(awaitingDependencies.hasOwnProperty(rawType)){var callbacks=awaitingDependencies[rawType];delete awaitingDependencies[rawType];callbacks.forEach(cb=>cb())}}function registerType(rawType,registeredInstance,options={}){if(!("argPackAdvance"in registeredInstance)){throw new TypeError("registerType registeredInstance requires argPackAdvance")}return sharedRegisterType(rawType,registeredInstance,options)}var GenericWireTypeSize=8;var __embind_register_bool=(rawType,name,trueValue,falseValue)=>{name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(wt){return!!wt},"toWireType":function(destructors,o){return o?trueValue:falseValue},"argPackAdvance":GenericWireTypeSize,"readValueFromPointer":function(pointer){return this["fromWireType"](GROWABLE_HEAP_U8()[pointer])},destructorFunction:null})};function handleAllocatorInit(){Object.assign(HandleAllocator.prototype,{get(id){return this.allocated[id]},has(id){return this.allocated[id]!==undefined},allocate(handle){var id=this.freelist.pop()||this.allocated.length;this.allocated[id]=handle;return id},free(id){this.allocated[id]=undefined;this.freelist.push(id)}})}function HandleAllocator(){this.allocated=[undefined];this.freelist=[]}var emval_handles=new HandleAllocator;var __emval_decref=handle=>{if(handle>=emval_handles.reserved&&0===--emval_handles.get(handle).refcount){emval_handles.free(handle)}};var count_emval_handles=()=>{var count=0;for(var i=emval_handles.reserved;i<emval_handles.allocated.length;++i){if(emval_handles.allocated[i]!==undefined){++count}}return count};var init_emval=()=>{emval_handles.allocated.push({value:undefined},{value:null},{value:true},{value:false});emval_handles.reserved=emval_handles.allocated.length;Module["count_emval_handles"]=count_emval_handles};var Emval={toValue:handle=>{if(!handle){throwBindingError("Cannot use deleted val. handle = "+handle)}return emval_handles.get(handle).value},toHandle:value=>{switch(value){case undefined:return 1;case null:return 2;case true:return 3;case false:return 4;default:{return emval_handles.allocate({refcount:1,value:value})}}}};function simpleReadValueFromPointer(pointer){return this["fromWireType"](GROWABLE_HEAP_I32()[pointer>>2])}var __embind_register_emval=(rawType,name)=>{name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":handle=>{var rv=Emval.toValue(handle);__emval_decref(handle);return rv},"toWireType":(destructors,value)=>Emval.toHandle(value),"argPackAdvance":GenericWireTypeSize,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:null})};var floatReadValueFromPointer=(name,width)=>{switch(width){case 4:return function(pointer){return this["fromWireType"](GROWABLE_HEAP_F32()[pointer>>2])};case 8:return function(pointer){return this["fromWireType"](GROWABLE_HEAP_F64()[pointer>>3])};default:throw new TypeError(`invalid float width (${width}): ${name}`)}};var __embind_register_float=(rawType,name,size)=>{name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":value=>value,"toWireType":(destructors,value)=>value,"argPackAdvance":GenericWireTypeSize,"readValueFromPointer":floatReadValueFromPointer(name,size),destructorFunction:null})};var char_0=48;var char_9=57;var makeLegalFunctionName=name=>{if(undefined===name){return"_unknown"}name=name.replace(/[^a-zA-Z0-9_]/g,"$");var f=name.charCodeAt(0);if(f>=char_0&&f<=char_9){return`_${name}`}return name};var runDestructors=destructors=>{while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}};function createNamedFunction(name,body){name=makeLegalFunctionName(name);return{[name]:function(){return body.apply(this,arguments)}}[name]}function newFunc(constructor,argumentList){if(!(constructor instanceof Function)){throw new TypeError(`new_ called with constructor type ${typeof constructor} which is not a function`)}var dummy=createNamedFunction(constructor.name||"unknownFunctionName",function(){});dummy.prototype=constructor.prototype;var obj=new dummy;var r=constructor.apply(obj,argumentList);return r instanceof Object?r:obj}function craftInvokerFunction(humanName,argTypes,classType,cppInvokerFunc,cppTargetFunc,isAsync){var argCount=argTypes.length;if(argCount<2){throwBindingError("argTypes array size mismatch! Must at least get return value and 'this' types!")}var isClassMethodFunc=argTypes[1]!==null&&classType!==null;var needsDestructorStack=false;for(var i=1;i<argTypes.length;++i){if(argTypes[i]!==null&&argTypes[i].destructorFunction===undefined){needsDestructorStack=true;break}}var returns=argTypes[0].name!=="void";var argsList="";var argsListWired="";for(var i=0;i<argCount-2;++i){argsList+=(i!==0?", ":"")+"arg"+i;argsListWired+=(i!==0?", ":"")+"arg"+i+"Wired"}var invokerFnBody=`\n return function ${makeLegalFunctionName(humanName)}(${argsList}) {\n if (arguments.length !== ${argCount-2}) {\n throwBindingError('function ${humanName} called with ' + arguments.length + ' arguments, expected ${argCount-2}');\n }`;if(needsDestructorStack){invokerFnBody+="var destructors = [];\n"}var dtorStack=needsDestructorStack?"destructors":"null";var args1=["throwBindingError","invoker","fn","runDestructors","retType","classParam"];var args2=[throwBindingError,cppInvokerFunc,cppTargetFunc,runDestructors,argTypes[0],argTypes[1]];if(isClassMethodFunc){invokerFnBody+="var thisWired = classParam.toWireType("+dtorStack+", this);\n"}for(var i=0;i<argCount-2;++i){invokerFnBody+="var arg"+i+"Wired = argType"+i+".toWireType("+dtorStack+", arg"+i+"); // "+argTypes[i+2].name+"\n";args1.push("argType"+i);args2.push(argTypes[i+2])}if(isClassMethodFunc){argsListWired="thisWired"+(argsListWired.length>0?", ":"")+argsListWired}invokerFnBody+=(returns||isAsync?"var rv = ":"")+"invoker(fn"+(argsListWired.length>0?", ":"")+argsListWired+");\n";if(needsDestructorStack){invokerFnBody+="runDestructors(destructors);\n"}else{for(var i=isClassMethodFunc?1:2;i<argTypes.length;++i){var paramName=i===1?"thisWired":"arg"+(i-2)+"Wired";if(argTypes[i].destructorFunction!==null){invokerFnBody+=paramName+"_dtor("+paramName+"); // "+argTypes[i].name+"\n";args1.push(paramName+"_dtor");args2.push(argTypes[i].destructorFunction)}}}if(returns){invokerFnBody+="var ret = retType.fromWireType(rv);\n"+"return ret;\n"}else{}invokerFnBody+="}\n";args1.push(invokerFnBody);return newFunc(Function,args1).apply(null,args2)}var ensureOverloadTable=(proto,methodName,humanName)=>{if(undefined===proto[methodName].overloadTable){var prevFunc=proto[methodName];proto[methodName]=function(){if(!proto[methodName].overloadTable.hasOwnProperty(arguments.length)){throwBindingError(`Function '${humanName}' called with an invalid number of arguments (${arguments.length}) - expects one of (${proto[methodName].overloadTable})!`)}return proto[methodName].overloadTable[arguments.length].apply(this,arguments)};proto[methodName].overloadTable=[];proto[methodName].overloadTable[prevFunc.argCount]=prevFunc}};var exposePublicSymbol=(name,value,numArguments)=>{if(Module.hasOwnProperty(name)){if(undefined===numArguments||undefined!==Module[name].overloadTable&&undefined!==Module[name].overloadTable[numArguments]){throwBindingError(`Cannot register public name '${name}' twice`)}ensureOverloadTable(Module,name,name);if(Module.hasOwnProperty(numArguments)){throwBindingError(`Cannot register multiple overloads of a function with the same number of arguments (${numArguments})!`)}Module[name].overloadTable[numArguments]=value}else{Module[name]=value;if(undefined!==numArguments){Module[name].numArguments=numArguments}}};var heap32VectorToArray=(count,firstElement)=>{var array=[];for(var i=0;i<count;i++){array.push(GROWABLE_HEAP_U32()[firstElement+i*4>>2])}return array};var replacePublicSymbol=(name,value,numArguments)=>{if(!Module.hasOwnProperty(name)){throwInternalError("Replacing nonexistant public symbol")}if(undefined!==Module[name].overloadTable&&undefined!==numArguments){Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}};var dynCallLegacy=(sig,ptr,args)=>{var f=Module["dynCall_"+sig];return args&&args.length?f.apply(null,[ptr].concat(args)):f.call(null,ptr)};var dynCall=(sig,ptr,args)=>{if(sig.includes("j")){return dynCallLegacy(sig,ptr,args)}var rtn=getWasmTableEntry(ptr).apply(null,args);return rtn};var getDynCaller=(sig,ptr)=>{var argCache=[];return function(){argCache.length=0;Object.assign(argCache,arguments);return dynCall(sig,ptr,argCache)}};var embind__requireFunction=(signature,rawFunction)=>{signature=readLatin1String(signature);function makeDynCaller(){if(signature.includes("j")){return getDynCaller(signature,rawFunction)}return getWasmTableEntry(rawFunction)}var fp=makeDynCaller();if(typeof fp!="function"){throwBindingError(`unknown function pointer with signature ${signature}: ${rawFunction}`)}return fp};var extendError=(baseErrorType,errorName)=>{var errorClass=createNamedFunction(errorName,function(message){this.name=errorName;this.message=message;var stack=new Error(message).stack;if(stack!==undefined){this.stack=this.toString()+"\n"+stack.replace(/^Error(:[^\n]*)?\n/,"")}});errorClass.prototype=Object.create(baseErrorType.prototype);errorClass.prototype.constructor=errorClass;errorClass.prototype.toString=function(){if(this.message===undefined){return this.name}else{return`${this.name}: ${this.message}`}};return errorClass};var UnboundTypeError=undefined;var getTypeName=type=>{var ptr=___getTypeName(type);var rv=readLatin1String(ptr);_free(ptr);return rv};var throwUnboundTypeError=(message,types)=>{var unboundTypes=[];var seen={};function visit(type){if(seen[type]){return}if(registeredTypes[type]){return}if(typeDependencies[type]){typeDependencies[type].forEach(visit);return}unboundTypes.push(type);seen[type]=true}types.forEach(visit);throw new UnboundTypeError(`${message}: `+unboundTypes.map(getTypeName).join([", "]))};var __embind_register_function=(name,argCount,rawArgTypesAddr,signature,rawInvoker,fn,isAsync)=>{var argTypes=heap32VectorToArray(argCount,rawArgTypesAddr);name=readLatin1String(name);rawInvoker=embind__requireFunction(signature,rawInvoker);exposePublicSymbol(name,function(){throwUnboundTypeError(`Cannot call ${name} due to unbound types`,argTypes)},argCount-1);whenDependentTypesAreResolved([],argTypes,function(argTypes){var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));replacePublicSymbol(name,craftInvokerFunction(name,invokerArgsArray,null,rawInvoker,fn,isAsync),argCount-1);return[]})};var integerReadValueFromPointer=(name,width,signed)=>{switch(width){case 1:return signed?pointer=>GROWABLE_HEAP_I8()[pointer>>0]:pointer=>GROWABLE_HEAP_U8()[pointer>>0];case 2:return signed?pointer=>GROWABLE_HEAP_I16()[pointer>>1]:pointer=>GROWABLE_HEAP_U16()[pointer>>1];case 4:return signed?pointer=>GROWABLE_HEAP_I32()[pointer>>2]:pointer=>GROWABLE_HEAP_U32()[pointer>>2];default:throw new TypeError(`invalid integer width (${width}): ${name}`)}};var __embind_register_integer=(primitiveType,name,size,minRange,maxRange)=>{name=readLatin1String(name);if(maxRange===-1){maxRange=4294967295}var fromWireType=value=>value;if(minRange===0){var bitshift=32-8*size;fromWireType=value=>value<<bitshift>>>bitshift}var isUnsignedType=name.includes("unsigned");var checkAssertions=(value,toTypeName)=>{};var toWireType;if(isUnsignedType){toWireType=function(destructors,value){checkAssertions(value,this.name);return value>>>0}}else{toWireType=function(destructors,value){checkAssertions(value,this.name);return value}}registerType(primitiveType,{name:name,"fromWireType":fromWireType,"toWireType":toWireType,"argPackAdvance":GenericWireTypeSize,"readValueFromPointer":integerReadValueFromPointer(name,size,minRange!==0),destructorFunction:null})};var __embind_register_memory_view=(rawType,dataTypeIndex,name)=>{var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){var size=GROWABLE_HEAP_U32()[handle>>2];var data=GROWABLE_HEAP_U32()[handle+4>>2];return new TA(GROWABLE_HEAP_I8().buffer,data,size)}name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":decodeMemoryView,"argPackAdvance":GenericWireTypeSize,"readValueFromPointer":decodeMemoryView},{ignoreDuplicateRegistrations:true})};function readPointer(pointer){return this["fromWireType"](GROWABLE_HEAP_U32()[pointer>>2])}var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,GROWABLE_HEAP_U8(),outPtr,maxBytesToWrite);var __embind_register_std_string=(rawType,name)=>{name=readLatin1String(name);var stdStringIsUTF8=name==="std::string";registerType(rawType,{name:name,"fromWireType":value=>{var length=GROWABLE_HEAP_U32()[value>>2];var payload=value+4;var str;if(stdStringIsUTF8){var decodeStartPtr=payload;for(var i=0;i<=length;++i){var currentBytePtr=payload+i;if(i==length||GROWABLE_HEAP_U8()[currentBytePtr]==0){var maxRead=currentBytePtr-decodeStartPtr;var stringSegment=UTF8ToString(decodeStartPtr,maxRead);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+1}}}else{var a=new Array(length);for(var i=0;i<length;++i){a[i]=String.fromCharCode(GROWABLE_HEAP_U8()[payload+i])}str=a.join("")}_free(value);return str},"toWireType":(destructors,value)=>{if(value instanceof ArrayBuffer){value=new Uint8Array(value)}var length;var valueIsOfTypeString=typeof value=="string";if(!(valueIsOfTypeString||value instanceof Uint8Array||value instanceof Uint8ClampedArray||value instanceof Int8Array)){throwBindingError("Cannot pass non-string to std::string")}if(stdStringIsUTF8&&valueIsOfTypeString){length=lengthBytesUTF8(value)}else{length=value.length}var base=_malloc(4+length+1);var ptr=base+4;GROWABLE_HEAP_U32()[base>>2]=length;if(stdStringIsUTF8&&valueIsOfTypeString){stringToUTF8(value,ptr,length+1)}else{if(valueIsOfTypeString){for(var i=0;i<length;++i){var charCode=value.charCodeAt(i);if(charCode>255){_free(ptr);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}GROWABLE_HEAP_U8()[ptr+i]=charCode}}else{for(var i=0;i<length;++i){GROWABLE_HEAP_U8()[ptr+i]=value[i]}}}if(destructors!==null){destructors.push(_free,base)}return base},"argPackAdvance":GenericWireTypeSize,"readValueFromPointer":readPointer,destructorFunction:ptr=>_free(ptr)})};var UTF16Decoder=typeof TextDecoder!="undefined"?new TextDecoder("utf-16le"):undefined;var UTF16ToString=(ptr,maxBytesToRead)=>{var endPtr=ptr;var idx=endPtr>>1;var maxIdx=idx+maxBytesToRead/2;while(!(idx>=maxIdx)&&GROWABLE_HEAP_U16()[idx])++idx;endPtr=idx<<1;if(endPtr-ptr>32&&UTF16Decoder)return UTF16Decoder.decode(GROWABLE_HEAP_U8().slice(ptr,endPtr));var str="";for(var i=0;!(i>=maxBytesToRead/2);++i){var codeUnit=GROWABLE_HEAP_I16()[ptr+i*2>>1];if(codeUnit==0)break;str+=String.fromCharCode(codeUnit)}return str};var stringToUTF16=(str,outPtr,maxBytesToWrite)=>{if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite<str.length*2?maxBytesToWrite/2:str.length;for(var i=0;i<numCharsToWrite;++i){var codeUnit=str.charCodeAt(i);GROWABLE_HEAP_I16()[outPtr>>1]=codeUnit;outPtr+=2}GROWABLE_HEAP_I16()[outPtr>>1]=0;return outPtr-startPtr};var lengthBytesUTF16=str=>str.length*2;var UTF32ToString=(ptr,maxBytesToRead)=>{var i=0;var str="";while(!(i>=maxBytesToRead/4)){var utf32=GROWABLE_HEAP_I32()[ptr+i*4>>2];if(utf32==0)break;++i;if(utf32>=65536){var ch=utf32-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}else{str+=String.fromCharCode(utf32)}}return str};var stringToUTF32=(str,outPtr,maxBytesToWrite)=>{if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i<str.length;++i){var codeUnit=str.charCodeAt(i);if(codeUnit>=55296&&codeUnit<=57343){var trailSurrogate=str.charCodeAt(++i);codeUnit=65536+((codeUnit&1023)<<10)|trailSurrogate&1023}GROWABLE_HEAP_I32()[outPtr>>2]=codeUnit;outPtr+=4;if(outPtr+4>endPtr)break}GROWABLE_HEAP_I32()[outPtr>>2]=0;return outPtr-startPtr};var lengthBytesUTF32=str=>{var len=0;for(var i=0;i<str.length;++i){var codeUnit=str.charCodeAt(i);if(codeUnit>=55296&&codeUnit<=57343)++i;len+=4}return len};var __embind_register_std_wstring=(rawType,charSize,name)=>{name=readLatin1String(name);var decodeString,encodeString,getHeap,lengthBytesUTF,shift;if(charSize===2){decodeString=UTF16ToString;encodeString=stringToUTF16;lengthBytesUTF=lengthBytesUTF16;getHeap=()=>GROWABLE_HEAP_U16();shift=1}else if(charSize===4){decodeString=UTF32ToString;encodeString=stringToUTF32;lengthBytesUTF=lengthBytesUTF32;getHeap=()=>GROWABLE_HEAP_U32();shift=2}registerType(rawType,{name:name,"fromWireType":value=>{var length=GROWABLE_HEAP_U32()[value>>2];var HEAP=getHeap();var str;var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i*charSize;if(i==length||HEAP[currentBytePtr>>shift]==0){var maxReadBytes=currentBytePtr-decodeStartPtr;var stringSegment=decodeString(decodeStartPtr,maxReadBytes);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+charSize}}_free(value);return str},"toWireType":(destructors,value)=>{if(!(typeof value=="string")){throwBindingError(`Cannot pass non-string to C++ string type ${name}`)}var length=lengthBytesUTF(value);var ptr=_malloc(4+length+charSize);GROWABLE_HEAP_U32()[ptr>>2]=length>>shift;encodeString(value,ptr+4,length+charSize);if(destructors!==null){destructors.push(_free,ptr)}return ptr},"argPackAdvance":GenericWireTypeSize,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:ptr=>_free(ptr)})};var __embind_register_void=(rawType,name)=>{name=readLatin1String(name);registerType(rawType,{isVoid:true,name:name,"argPackAdvance":0,"fromWireType":()=>undefined,"toWireType":(destructors,o)=>undefined})};var nowIsMonotonic=true;var __emscripten_get_now_is_monotonic=()=>nowIsMonotonic;var maybeExit=()=>{if(!keepRuntimeAlive()){try{if(ENVIRONMENT_IS_PTHREAD)__emscripten_thread_exit(EXITSTATUS);else _exit(EXITSTATUS)}catch(e){handleException(e)}}};var callUserCallback=func=>{if(ABORT){return}try{func();maybeExit()}catch(e){handleException(e)}};var __emscripten_thread_mailbox_await=pthread_ptr=>{if(typeof Atomics.waitAsync==="function"){var wait=Atomics.waitAsync(GROWABLE_HEAP_I32(),pthread_ptr>>2,pthread_ptr);wait.value.then(checkMailbox);var waitingAsync=pthread_ptr+128;Atomics.store(GROWABLE_HEAP_I32(),waitingAsync>>2,1)}};Module["__emscripten_thread_mailbox_await"]=__emscripten_thread_mailbox_await;var checkMailbox=()=>{var pthread_ptr=_pthread_self();if(pthread_ptr){__emscripten_thread_mailbox_await(pthread_ptr);callUserCallback(()=>__emscripten_check_mailbox())}};Module["checkMailbox"]=checkMailbox;var __emscripten_notify_mailbox_postmessage=(targetThreadId,currThreadId,mainThreadId)=>{if(targetThreadId==currThreadId){setTimeout(()=>checkMailbox())}else if(ENVIRONMENT_IS_PTHREAD){postMessage({"targetThread":targetThreadId,"cmd":"checkMailbox"})}else{var worker=PThread.pthreads[targetThreadId];if(!worker){return}worker.postMessage({"cmd":"checkMailbox"})}};var withStackSave=f=>{var stack=stackSave();var ret=f();stackRestore(stack);return ret};var proxyToMainThread=function(index,sync){var numCallArgs=arguments.length-2;var outerArgs=arguments;return withStackSave(()=>{var serializedNumCallArgs=numCallArgs;var args=stackAlloc(serializedNumCallArgs*8);var b=args>>3;for(var i=0;i<numCallArgs;i++){var arg=outerArgs[2+i];GROWABLE_HEAP_F64()[b+i]=arg}return __emscripten_run_on_main_thread_js(index,serializedNumCallArgs,args,sync)})};var proxiedJSCallArgs=[];var __emscripten_receive_on_main_thread_js=(index,callingThread,numCallArgs,args)=>{proxiedJSCallArgs.length=numCallArgs;var b=args>>3;for(var i=0;i<numCallArgs;i++){proxiedJSCallArgs[i]=GROWABLE_HEAP_F64()[b+i]}var func=proxiedFunctionTable[index];PThread.currentProxiedOperationCallerThread=callingThread;var rtn=func.apply(null,proxiedJSCallArgs);PThread.currentProxiedOperationCallerThread=0;return rtn};var __emscripten_thread_set_strongref=thread=>{if(ENVIRONMENT_IS_NODE){PThread.pthreads[thread].ref()}};var requireRegisteredType=(rawType,humanName)=>{var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(humanName+" has unknown type "+getTypeName(rawType))}return impl};var __emval_as=(handle,returnType,destructorsRef)=>{handle=Emval.toValue(handle);returnType=requireRegisteredType(returnType,"emval::as");var destructors=[];var rd=Emval.toHandle(destructors);GROWABLE_HEAP_U32()[destructorsRef>>2]=rd;return returnType["toWireType"](destructors,handle)};var emval_symbols={};var getStringOrSymbol=address=>{var symbol=emval_symbols[address];if(symbol===undefined){return readLatin1String(address)}return symbol};var emval_methodCallers=[];var __emval_call_void_method=(caller,handle,methodName,args)=>{caller=emval_methodCallers[caller];handle=Emval.toValue(handle);methodName=getStringOrSymbol(methodName);caller(handle,methodName,null,args)};var emval_addMethodCaller=caller=>{var id=emval_methodCallers.length;emval_methodCallers.push(caller);return id};var emval_lookupTypes=(argCount,argTypes)=>{var a=new Array(argCount);for(var i=0;i<argCount;++i){a[i]=requireRegisteredType(GROWABLE_HEAP_U32()[argTypes+i*4>>2],"parameter "+i)}return a};var emval_registeredMethods=[];var __emval_get_method_caller=(argCount,argTypes)=>{var types=emval_lookupTypes(argCount,argTypes);var retType=types[0];var signatureName=retType.name+"_$"+types.slice(1).map(function(t){return t.name}).join("_")+"$";var returnId=emval_registeredMethods[signatureName];if(returnId!==undefined){return returnId}var params=["retType"];var args=[retType];var argsList="";for(var i=0;i<argCount-1;++i){argsList+=(i!==0?", ":"")+"arg"+i;params.push("argType"+i);args.push(types[1+i])}var functionName=makeLegalFunctionName("methodCaller_"+signatureName);var functionBody="return function "+functionName+"(handle, name, destructors, args) {\n";var offset=0;for(var i=0;i<argCount-1;++i){functionBody+=" var arg"+i+" = argType"+i+".readValueFromPointer(args"+(offset?"+"+offset:"")+");\n";offset+=types[i+1]["argPackAdvance"]}functionBody+=" var rv = handle[name]("+argsList+");\n";for(var i=0;i<argCount-1;++i){if(types[i+1]["deleteObject"]){functionBody+=" argType"+i+".deleteObject(arg"+i+");\n"}}if(!retType.isVoid){functionBody+=" return retType.toWireType(destructors, rv);\n"}functionBody+="};\n";params.push(functionBody);var invokerFunction=newFunc(Function,params).apply(null,args);returnId=emval_addMethodCaller(invokerFunction);emval_registeredMethods[signatureName]=returnId;return returnId};var __emval_get_module_property=name=>{name=getStringOrSymbol(name);return Emval.toHandle(Module[name])};var __emval_get_property=(handle,key)=>{handle=Emval.toValue(handle);key=Emval.toValue(key);return Emval.toHandle(handle[key])};var __emval_incref=handle=>{if(handle>4){emval_handles.get(handle).refcount+=1}};var craftEmvalAllocator=argCount=>{var argsList="";for(var i=0;i<argCount;++i){argsList+=(i!==0?", ":"")+"arg"+i}var getMemory=()=>GROWABLE_HEAP_U32();var functionBody="return function emval_allocator_"+argCount+"(constructor, argTypes, args) {\n"+" var HEAPU32 = getMemory();\n";for(var i=0;i<argCount;++i){functionBody+="var argType"+i+" = requireRegisteredType(HEAPU32[((argTypes)>>2)], 'parameter "+i+"');\n"+"var arg"+i+" = argType"+i+".readValueFromPointer(args);\n"+"args += argType"+i+"['argPackAdvance'];\n"+"argTypes += 4;\n"}functionBody+="var obj = new constructor("+argsList+");\n"+"return valueToHandle(obj);\n"+"}\n";return new Function("requireRegisteredType","Module","valueToHandle","getMemory",functionBody)(requireRegisteredType,Module,Emval.toHandle,getMemory)};var emval_newers={};var __emval_new=(handle,argCount,argTypes,args)=>{handle=Emval.toValue(handle);var newer=emval_newers[argCount];if(!newer){newer=craftEmvalAllocator(argCount);emval_newers[argCount]=newer}return newer(handle,argTypes,args)};var __emval_new_cstring=v=>Emval.toHandle(getStringOrSymbol(v));var __emval_run_destructors=handle=>{var destructors=Emval.toValue(handle);runDestructors(destructors);__emval_decref(handle)};var _abort=()=>{abort("")};var warnOnce=text=>{if(!warnOnce.shown)warnOnce.shown={};if(!warnOnce.shown[text]){warnOnce.shown[text]=1;if(ENVIRONMENT_IS_NODE)text="warning: "+text;err(text)}};var _emscripten_check_blocking_allowed=()=>{};var runtimeKeepalivePush=()=>{runtimeKeepaliveCounter+=1};var _emscripten_exit_with_live_runtime=()=>{runtimeKeepalivePush();throw"unwind"};var getHeapMax=()=>2147483648;var _emscripten_get_heap_max=()=>getHeapMax();var _emscripten_get_now;_emscripten_get_now=()=>performance.timeOrigin+performance.now();var _emscripten_num_logical_cores=()=>{if(ENVIRONMENT_IS_NODE)return require("os").cpus().length;return navigator["hardwareConcurrency"]};var growMemory=size=>{var b=wasmMemory.buffer;var pages=(size-b.byteLength+65535)/65536;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=GROWABLE_HEAP_U8().length;requestedSize>>>=0;if(requestedSize<=oldSize){return false}var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}var alignUp=(x,multiple)=>x+(multiple-x%multiple)%multiple;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var ENV={};var getExecutableName=()=>thisProgram||"./this.program";var getEnvStrings=()=>{if(!getEnvStrings.strings){var lang=(typeof navigator=="object"&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8";var env={"USER":"web_user","LOGNAME":"web_user","PATH":"/","PWD":"/","HOME":"/home/web_user","LANG":lang,"_":getExecutableName()};for(var x in ENV){if(ENV[x]===undefined)delete env[x];else env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(`${x}=${env[x]}`)}getEnvStrings.strings=strings}return getEnvStrings.strings};var stringToAscii=(str,buffer)=>{for(var i=0;i<str.length;++i){GROWABLE_HEAP_I8()[buffer++>>0]=str.charCodeAt(i)}GROWABLE_HEAP_I8()[buffer>>0]=0};var _environ_get=function(__environ,environ_buf){if(ENVIRONMENT_IS_PTHREAD)return proxyToMainThread(6,1,__environ,environ_buf);var bufSize=0;getEnvStrings().forEach((string,i)=>{var ptr=environ_buf+bufSize;GROWABLE_HEAP_U32()[__environ+i*4>>2]=ptr;stringToAscii(string,ptr);bufSize+=string.length+1});return 0};var _environ_sizes_get=function(penviron_count,penviron_buf_size){if(ENVIRONMENT_IS_PTHREAD)return proxyToMainThread(7,1,penviron_count,penviron_buf_size);var strings=getEnvStrings();GROWABLE_HEAP_U32()[penviron_count>>2]=strings.length;var bufSize=0;strings.forEach(string=>bufSize+=string.length+1);GROWABLE_HEAP_U32()[penviron_buf_size>>2]=bufSize;return 0};function _fd_close(fd){if(ENVIRONMENT_IS_PTHREAD)return proxyToMainThread(8,1,fd);try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var doReadv=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i<iovcnt;i++){var ptr=GROWABLE_HEAP_U32()[iov>>2];var len=GROWABLE_HEAP_U32()[iov+4>>2];iov+=8;var curr=FS.read(stream,GROWABLE_HEAP_I8(),ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr<len)break;if(typeof offset!=="undefined"){offset+=curr}}return ret};function _fd_read(fd,iov,iovcnt,pnum){if(ENVIRONMENT_IS_PTHREAD)return proxyToMainThread(9,1,fd,iov,iovcnt,pnum);try{var stream=SYSCALLS.getStreamFromFD(fd);var num=doReadv(stream,iov,iovcnt);GROWABLE_HEAP_U32()[pnum>>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var convertI32PairToI53Checked=(lo,hi)=>hi+2097152>>>0<4194305-!!lo?(lo>>>0)+hi*4294967296:NaN;function _fd_seek(fd,offset_low,offset_high,whence,newOffset){if(ENVIRONMENT_IS_PTHREAD)return proxyToMainThread(10,1,fd,offset_low,offset_high,whence,newOffset);var offset=convertI32PairToI53Checked(offset_low,offset_high);try{if(isNaN(offset))return 61;var stream=SYSCALLS.getStreamFromFD(fd);FS.llseek(stream,offset,whence);tempI64=[stream.position>>>0,(tempDouble=stream.position,+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],GROWABLE_HEAP_I32()[newOffset>>2]=tempI64[0],GROWABLE_HEAP_I32()[newOffset+4>>2]=tempI64[1];if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var doWritev=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i<iovcnt;i++){var ptr=GROWABLE_HEAP_U32()[iov>>2];var len=GROWABLE_HEAP_U32()[iov+4>>2];iov+=8;var curr=FS.write(stream,GROWABLE_HEAP_I8(),ptr,len,offset);if(curr<0)return-1;ret+=curr;if(typeof offset!=="undefined"){offset+=curr}}return ret};function _fd_write(fd,iov,iovcnt,pnum){if(ENVIRONMENT_IS_PTHREAD)return proxyToMainThread(11,1,fd,iov,iovcnt,pnum);try{var stream=SYSCALLS.getStreamFromFD(fd);var num=doWritev(stream,iov,iovcnt);GROWABLE_HEAP_U32()[pnum>>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var isLeapYear=year=>year%4===0&&(year%100!==0||year%400===0);var arraySum=(array,index)=>{var sum=0;for(var i=0;i<=index;sum+=array[i++]){}return sum};var MONTH_DAYS_LEAP=[31,29,31,30,31,30,31,31,30,31,30,31];var MONTH_DAYS_REGULAR=[31,28,31,30,31,30,31,31,30,31,30,31];var addDays=(date,days)=>{var newDate=new Date(date.getTime());while(days>0){var leap=isLeapYear(newDate.getFullYear());var currentMonth=newDate.getMonth();var daysInCurrentMonth=(leap?MONTH_DAYS_LEAP:MONTH_DAYS_REGULAR)[currentMonth];if(days>daysInCurrentMonth-newDate.getDate()){days-=daysInCurrentMonth-newDate.getDate()+1;newDate.setDate(1);if(currentMonth<11){newDate.setMonth(currentMonth+1)}else{newDate.setMonth(0);newDate.setFullYear(newDate.getFullYear()+1)}}else{newDate.setDate(newDate.getDate()+days);return newDate}}return newDate};var writeArrayToMemory=(array,buffer)=>{GROWABLE_HEAP_I8().set(array,buffer)};var _strftime=(s,maxsize,format,tm)=>{var tm_zone=GROWABLE_HEAP_U32()[tm+40>>2];var date={tm_sec:GROWABLE_HEAP_I32()[tm>>2],tm_min:GROWABLE_HEAP_I32()[tm+4>>2],tm_hour:GROWABLE_HEAP_I32()[tm+8>>2],tm_mday:GROWABLE_HEAP_I32()[tm+12>>2],tm_mon:GROWABLE_HEAP_I32()[tm+16>>2],tm_year:GROWABLE_HEAP_I32()[tm+20>>2],tm_wday:GROWABLE_HEAP_I32()[tm+24>>2],tm_yday:GROWABLE_HEAP_I32()[tm+28>>2],tm_isdst:GROWABLE_HEAP_I32()[tm+32>>2],tm_gmtoff:GROWABLE_HEAP_I32()[tm+36>>2],tm_zone:tm_zone?UTF8ToString(tm_zone):""};var pattern=UTF8ToString(format);var EXPANSION_RULES_1={"%c":"%a %b %d %H:%M:%S %Y","%D":"%m/%d/%y","%F":"%Y-%m-%d","%h":"%b","%r":"%I:%M:%S %p","%R":"%H:%M","%T":"%H:%M:%S","%x":"%m/%d/%y","%X":"%H:%M:%S","%Ec":"%c","%EC":"%C","%Ex":"%m/%d/%y","%EX":"%H:%M:%S","%Ey":"%y","%EY":"%Y","%Od":"%d","%Oe":"%e","%OH":"%H","%OI":"%I","%Om":"%m","%OM":"%M","%OS":"%S","%Ou":"%u","%OU":"%U","%OV":"%V","%Ow":"%w","%OW":"%W","%Oy":"%y"};for(var rule in EXPANSION_RULES_1){pattern=pattern.replace(new RegExp(rule,"g"),EXPANSION_RULES_1[rule])}var WEEKDAYS=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];var MONTHS=["January","February","March","April","May","June","July","August","September","October","November","December"];function leadingSomething(value,digits,character){var str=typeof value=="number"?value.toString():value||"";while(str.length<digits){str=character[0]+str}return str}function leadingNulls(value,digits){return leadingSomething(value,digits,"0")}function compareByDay(date1,date2){function sgn(value){return value<0?-1:value>0?1:0}var compare;if((compare=sgn(date1.getFullYear()-date2.getFullYear()))===0){if((compare=sgn(date1.getMonth()-date2.getMonth()))===0){compare=sgn(date1.getDate()-date2.getDate())}}return compare}function getFirstWeekStartDate(janFourth){switch(janFourth.getDay()){case 0:return new Date(janFourth.getFullYear()-1,11,29);case 1:return janFourth;case 2:return new Date(janFourth.getFullYear(),0,3);case 3:return new Date(janFourth.getFullYear(),0,2);case 4:return new Date(janFourth.getFullYear(),0,1);case 5:return new Date(janFourth.getFullYear()-1,11,31);case 6:return new Date(janFourth.getFullYear()-1,11,30)}}function getWeekBasedYear(date){var thisDate=addDays(new Date(date.tm_year+1900,0,1),date.tm_yday);var janFourthThisYear=new Date(thisDate.getFullYear(),0,4);var janFourthNextYear=new Date(thisDate.getFullYear()+1,0,4);var firstWeekStartThisYear=getFirstWeekStartDate(janFourthThisYear);var firstWeekStartNextYear=getFirstWeekStartDate(janFourthNextYear);if(compareByDay(firstWeekStartThisYear,thisDate)<=0){if(compareByDay(firstWeekStartNextYear,thisDate)<=0){return thisDate.getFullYear()+1}return thisDate.getFullYear()}return thisDate.getFullYear()-1}var EXPANSION_RULES_2={"%a":date=>WEEKDAYS[date.tm_wday].substring(0,3),"%A":date=>WEEKDAYS[date.tm_wday],"%b":date=>MONTHS[date.tm_mon].substring(0,3),"%B":date=>MONTHS[date.tm_mon],"%C":date=>{var year=date.tm_year+1900;return leadingNulls(year/100|0,2)},"%d":date=>leadingNulls(date.tm_mday,2),"%e":date=>leadingSomething(date.tm_mday,2," "),"%g":date=>getWeekBasedYear(date).toString().substring(2),"%G":date=>getWeekBasedYear(date),"%H":date=>leadingNulls(date.tm_hour,2),"%I":date=>{var twelveHour=date.tm_hour;if(twelveHour==0)twelveHour=12;else if(twelveHour>12)twelveHour-=12;return leadingNulls(twelveHour,2)},"%j":date=>leadingNulls(date.tm_mday+arraySum(isLeapYear(date.tm_year+1900)?MONTH_DAYS_LEAP:MONTH_DAYS_REGULAR,date.tm_mon-1),3),"%m":date=>leadingNulls(date.tm_mon+1,2),"%M":date=>leadingNulls(date.tm_min,2),"%n":()=>"\n","%p":date=>{if(date.tm_hour>=0&&date.tm_hour<12){return"AM"}return"PM"},"%S":date=>leadingNulls(date.tm_sec,2),"%t":()=>"\t","%u":date=>date.tm_wday||7,"%U":date=>{var days=date.tm_yday+7-date.tm_wday;return leadingNulls(Math.floor(days/7),2)},"%V":date=>{var val=Math.floor((date.tm_yday+7-(date.tm_wday+6)%7)/7);if((date.tm_wday+371-date.tm_yday-2)%7<=2){val++}if(!val){val=52;var dec31=(date.tm_wday+7-date.tm_yday-1)%7;if(dec31==4||dec31==5&&isLeapYear(date.tm_year%400-1)){val++}}else if(val==53){var jan1=(date.tm_wday+371-date.tm_yday)%7;if(jan1!=4&&(jan1!=3||!isLeapYear(date.tm_year)))val=1}return leadingNulls(val,2)},"%w":date=>date.tm_wday,"%W":date=>{var days=date.tm_yday+7-(date.tm_wday+6)%7;return leadingNulls(Math.floor(days/7),2)},"%y":date=>(date.tm_year+1900).toString().substring(2),"%Y":date=>date.tm_year+1900,"%z":date=>{var off=date.tm_gmtoff;var ahead=off>=0;off=Math.abs(off)/60;off=off/60*100+off%60;return(ahead?"+":"-")+String("0000"+off).slice(-4)},"%Z":date=>date.tm_zone,"%%":()=>"%"};pattern=pattern.replace(/%%/g,"\0\0");for(var rule in EXPANSION_RULES_2){if(pattern.includes(rule)){pattern=pattern.replace(new RegExp(rule,"g"),EXPANSION_RULES_2[rule](date))}}pattern=pattern.replace(/\0\0/g,"%");var bytes=intArrayFromString(pattern,false);if(bytes.length>maxsize){return 0}writeArrayToMemory(bytes,s);return bytes.length-1};var _strftime_l=(s,maxsize,format,tm,loc)=>_strftime(s,maxsize,format,tm);PThread.init();var FSNode=function(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.mounted=null;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.node_ops={};this.stream_ops={};this.rdev=rdev};var readMode=292|73;var writeMode=146;Object.defineProperties(FSNode.prototype,{read:{get:function(){return(this.mode&readMode)===readMode},set:function(val){val?this.mode|=readMode:this.mode&=~readMode}},write:{get:function(){return(this.mode&writeMode)===writeMode},set:function(val){val?this.mode|=writeMode:this.mode&=~writeMode}},isFolder:{get:function(){return FS.isDir(this.mode)}},isDevice:{get:function(){return FS.isChrdev(this.mode)}}});FS.FSNode=FSNode;FS.createPreloadedFile=FS_createPreloadedFile;FS.staticInit();Module["FS_createPath"]=FS.createPath;Module["FS_createDataFile"]=FS.createDataFile;Module["FS_createPreloadedFile"]=FS.createPreloadedFile;Module["FS_unlink"]=FS.unlink;Module["FS_createLazyFile"]=FS.createLazyFile;Module["FS_createDevice"]=FS.createDevice;embind_init_charCodes();BindingError=Module["BindingError"]=class BindingError extends Error{constructor(message){super(message);this.name="BindingError"}};InternalError=Module["InternalError"]=class InternalError extends Error{constructor(message){super(message);this.name="InternalError"}};handleAllocatorInit();init_emval();UnboundTypeError=Module["UnboundTypeError"]=extendError(Error,"UnboundTypeError");var proxiedFunctionTable=[_proc_exit,exitOnMainThread,pthreadCreateProxied,___syscall_fcntl64,___syscall_ioctl,___syscall_openat,_environ_get,_environ_sizes_get,_fd_close,_fd_read,_fd_seek,_fd_write];var wasmImports={d:___cxa_throw,J:___emscripten_init_main_thread_js,m:___emscripten_thread_cleanup,G:___pthread_create_js,q:___syscall_fcntl64,L:___syscall_ioctl,M:___syscall_openat,y:__embind_register_bigint,R:__embind_register_bool,Q:__embind_register_emval,s:__embind_register_float,h:__embind_register_function,g:__embind_register_integer,c:__embind_register_memory_view,r:__embind_register_std_string,k:__embind_register_std_wstring,S:__embind_register_void,P:__emscripten_get_now_is_monotonic,F:__emscripten_notify_mailbox_postmessage,H:__emscripten_receive_on_main_thread_js,I:__emscripten_thread_mailbox_await,O:__emscripten_thread_set_strongref,w:__emval_as,U:__emval_call_void_method,f:__emval_decref,V:__emval_get_method_caller,u:__emval_get_module_property,i:__emval_get_property,l:__emval_incref,t:__emval_new,j:__emval_new_cstring,v:__emval_run_destructors,b:_abort,n:_emscripten_check_blocking_allowed,N:_emscripten_exit_with_live_runtime,z:_emscripten_get_heap_max,e:_emscripten_get_now,A:_emscripten_num_logical_cores,E:_emscripten_resize_heap,C:_environ_get,D:_environ_sizes_get,T:_exit,o:_fd_close,K:_fd_read,x:_fd_seek,p:_fd_write,a:wasmMemory||Module["wasmMemory"],B:_strftime_l};var wasmExports=createWasm();var ___wasm_call_ctors=()=>(___wasm_call_ctors=wasmExports["W"])();var _free=a0=>(_free=wasmExports["Y"])(a0);var _malloc=a0=>(_malloc=wasmExports["Z"])(a0);var __emscripten_tls_init=Module["__emscripten_tls_init"]=()=>(__emscripten_tls_init=Module["__emscripten_tls_init"]=wasmExports["_"])();var _pthread_self=Module["_pthread_self"]=()=>(_pthread_self=Module["_pthread_self"]=wasmExports["$"])();var ___getTypeName=a0=>(___getTypeName=wasmExports["aa"])(a0);var __embind_initialize_bindings=Module["__embind_initialize_bindings"]=()=>(__embind_initialize_bindings=Module["__embind_initialize_bindings"]=wasmExports["ba"])();var ___errno_location=()=>(___errno_location=wasmExports["ca"])();var __emscripten_thread_init=Module["__emscripten_thread_init"]=(a0,a1,a2,a3,a4,a5)=>(__emscripten_thread_init=Module["__emscripten_thread_init"]=wasmExports["da"])(a0,a1,a2,a3,a4,a5);var __emscripten_thread_crashed=Module["__emscripten_thread_crashed"]=()=>(__emscripten_thread_crashed=Module["__emscripten_thread_crashed"]=wasmExports["ea"])();var _emscripten_main_thread_process_queued_calls=()=>(_emscripten_main_thread_process_queued_calls=wasmExports["emscripten_main_thread_process_queued_calls"])();var _emscripten_main_runtime_thread_id=()=>(_emscripten_main_runtime_thread_id=wasmExports["emscripten_main_runtime_thread_id"])();var __emscripten_run_on_main_thread_js=(a0,a1,a2,a3)=>(__emscripten_run_on_main_thread_js=wasmExports["fa"])(a0,a1,a2,a3);var __emscripten_thread_free_data=a0=>(__emscripten_thread_free_data=wasmExports["ga"])(a0);var __emscripten_thread_exit=Module["__emscripten_thread_exit"]=a0=>(__emscripten_thread_exit=Module["__emscripten_thread_exit"]=wasmExports["ha"])(a0);var __emscripten_check_mailbox=Module["__emscripten_check_mailbox"]=()=>(__emscripten_check_mailbox=Module["__emscripten_check_mailbox"]=wasmExports["ia"])();var _emscripten_stack_set_limits=(a0,a1)=>(_emscripten_stack_set_limits=wasmExports["ja"])(a0,a1);var stackSave=()=>(stackSave=wasmExports["ka"])();var stackRestore=a0=>(stackRestore=wasmExports["la"])(a0);var stackAlloc=a0=>(stackAlloc=wasmExports["ma"])(a0);var ___cxa_is_pointer_type=a0=>(___cxa_is_pointer_type=wasmExports["na"])(a0);var dynCall_jiji=Module["dynCall_jiji"]=(a0,a1,a2,a3,a4)=>(dynCall_jiji=Module["dynCall_jiji"]=wasmExports["oa"])(a0,a1,a2,a3,a4);var dynCall_viijii=Module["dynCall_viijii"]=(a0,a1,a2,a3,a4,a5,a6)=>(dynCall_viijii=Module["dynCall_viijii"]=wasmExports["pa"])(a0,a1,a2,a3,a4,a5,a6);var dynCall_iiiiij=Module["dynCall_iiiiij"]=(a0,a1,a2,a3,a4,a5,a6)=>(dynCall_iiiiij=Module["dynCall_iiiiij"]=wasmExports["qa"])(a0,a1,a2,a3,a4,a5,a6);var dynCall_iiiiijj=Module["dynCall_iiiiijj"]=(a0,a1,a2,a3,a4,a5,a6,a7,a8)=>(dynCall_iiiiijj=Module["dynCall_iiiiijj"]=wasmExports["ra"])(a0,a1,a2,a3,a4,a5,a6,a7,a8);var dynCall_iiiiiijj=Module["dynCall_iiiiiijj"]=(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9)=>(dynCall_iiiiiijj=Module["dynCall_iiiiiijj"]=wasmExports["sa"])(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9);function intArrayFromBase64(s){if(typeof ENVIRONMENT_IS_NODE!="undefined"&&ENVIRONMENT_IS_NODE){var buf=Buffer.from(s,"base64");return new Uint8Array(buf.buffer,buf.byteOffset,buf.length)}try{var decoded=atob(s);var bytes=new Uint8Array(decoded.length);for(var i=0;i<decoded.length;++i){bytes[i]=decoded.charCodeAt(i)}return bytes}catch(_){throw new Error("Converting base64 string to bytes failed.")}}function tryParseAsDataURI(filename){if(!isDataURI(filename)){return}return intArrayFromBase64(filename.slice(dataURIPrefix.length))}Module["addRunDependency"]=addRunDependency;Module["removeRunDependency"]=removeRunDependency;Module["FS_createPath"]=FS.createPath;Module["FS_createDataFile"]=FS.createDataFile;Module["FS_createLazyFile"]=FS.createLazyFile;Module["FS_createDevice"]=FS.createDevice;Module["FS_unlink"]=FS.unlink;Module["keepRuntimeAlive"]=keepRuntimeAlive;Module["wasmMemory"]=wasmMemory;Module["ExitStatus"]=ExitStatus;Module["FS_createPreloadedFile"]=FS.createPreloadedFile;Module["PThread"]=PThread;var calledRun;dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(){if(runDependencies>0){return}if(ENVIRONMENT_IS_PTHREAD){readyPromiseResolve(Module);initRuntime();startWorker(Module);return}preRun();if(runDependencies>0){return}function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve(Module);if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}run(); + + + return moduleArg.ready +} + +); +})(); +if (typeof exports === 'object' && typeof module === 'object') + module.exports = whisper_factory; +else if (typeof define === 'function' && define['amd']) + define([], () => whisper_factory); diff --git a/bindings/ruby/.document b/bindings/ruby/.document new file mode 100644 index 0000000..a8e9788 --- /dev/null +++ b/bindings/ruby/.document @@ -0,0 +1,3 @@ +README.md +LICENSE +sig diff --git a/bindings/ruby/.gitignore b/bindings/ruby/.gitignore new file mode 100644 index 0000000..54e3a2a --- /dev/null +++ b/bindings/ruby/.gitignore @@ -0,0 +1,9 @@ +LICENSE +pkg/ +lib/whisper.* +ext/examples/ +ext/ggml/ +ext/include/ +ext/scripts/ +ext/src/ +test/fixtures/ diff --git a/bindings/ruby/.rdoc_options b/bindings/ruby/.rdoc_options new file mode 100644 index 0000000..cf14aa5 --- /dev/null +++ b/bindings/ruby/.rdoc_options @@ -0,0 +1,2 @@ +title: whispercpp +main_page: README.md diff --git a/bindings/ruby/CHANGELOG.md b/bindings/ruby/CHANGELOG.md new file mode 100644 index 0000000..8f7d239 --- /dev/null +++ b/bindings/ruby/CHANGELOG.md @@ -0,0 +1,169 @@ +# Changelog + +## [1.3.9] - Unreleased + +Bundled whisper.cpp: `1.9.3` + +- Fixed test fixture JFKReader's format string. +- Add #free method to `Whisper::Context`, `Whisper::VAD::Context` and `Whisper::Parakeet::Context`. +- Checked MemoryView format more strictly. + +## [1.3.8] - 2026-07-28 + +Bundled whisper.cpp: `1.9.1` + +- Added `Whisper::Context#full_n_vad_segments`, + `#full_get_vad_segment_t0`, and `#full_get_vad_segment_t1` for reading the + speech segments produced by VAD-enabled transcription. +- Changed standalone VAD processing to release Ruby's Global VM Lock. +- Fixed VAD parameter ownership and type handling, compatibility with older + CMake option syntax, and model cache lookup behavior. + ([#3931](https://github.com/ggml-org/whisper.cpp/pull/3931)) + +## [1.3.7] - 2026-06-17 + +Bundled whisper.cpp: `1.9.0` + +- Added Ruby bindings for NVIDIA Parakeet, including models, contexts, + parameters, segments, tokens, callbacks, and shared output helpers. + ([#3885](https://github.com/ggml-org/whisper.cpp/pull/3885)) +- Added Windows build support and support for additional Ruby MemoryView + inputs, and changed transcription to release Ruby's Global VM Lock. + ([#3775](https://github.com/ggml-org/whisper.cpp/pull/3775)) +- Fixed dangling pointers, a memory leak, and a segmentation fault during + parallel transcription. + ([#3715](https://github.com/ggml-org/whisper.cpp/pull/3715)) +- Raised the minimum supported Ruby version from 3.1 to 3.3. + +## [1.3.6] - 2026-03-19 + +Bundled whisper.cpp: `1.8.4` + +- Added the low-level `Whisper::VAD::Context#segments_from_samples` API and + accepted `Pathname`-compatible model and audio paths. + ([#3633](https://github.com/ggml-org/whisper.cpp/pull/3633)) +- Added `Whisper::Context::Params` for configuring context initialization and + fixed token memory ownership. + ([#3647](https://github.com/ggml-org/whisper.cpp/pull/3647)) +- Added a missing context null check. + ([#3689](https://github.com/ggml-org/whisper.cpp/pull/3689)) + +## [1.3.5] - 2026-01-16 + +Bundled whisper.cpp: `1.8.3` + +- Added standalone voice activity detection through + `Whisper::VAD::Context`, `Whisper::VAD::Segments`, and + `Whisper::VAD::Segment`. + ([#3518](https://github.com/ggml-org/whisper.cpp/pull/3518)) +- Updated the packaged Silero VAD model support to v6.2.0. + ([#3524](https://github.com/ggml-org/whisper.cpp/pull/3524)) +- Added `Whisper::Token` and corrected model URI handling. + ([#3575](https://github.com/ggml-org/whisper.cpp/pull/3575)) +- Fixed segmentation faults in token and segment access. + ([#3591](https://github.com/ggml-org/whisper.cpp/pull/3591)) + +## [1.3.4] - 2025-10-08 + +Bundled whisper.cpp: `1.8.0` + +- Added `Whisper::Params#max_len`. + ([#3365](https://github.com/ggml-org/whisper.cpp/pull/3365)) +- Fixed negative model-download padding values and made installation tests less + sensitive to output formatting. + ([#3389](https://github.com/ggml-org/whisper.cpp/pull/3389), + [#3448](https://github.com/ggml-org/whisper.cpp/pull/3448)) + +## [1.3.3] - 2025-07-12 + +Bundled whisper.cpp: `1.7.6` + +- Added Core ML build support. + ([#3214](https://github.com/ggml-org/whisper.cpp/pull/3214)) +- Added parallel transcription through `Whisper::Context#full_parallel`. + ([#3222](https://github.com/ggml-org/whisper.cpp/pull/3222)) +- Added higher-level segment iteration and text, SRT, VTT, CSV, and JSON output + helpers. + ([#3237](https://github.com/ggml-org/whisper.cpp/pull/3237)) +- Added `Whisper::VERSION`. + ([#3292](https://github.com/ggml-org/whisper.cpp/pull/3292)) +- Improved install-time build option handling and Apple framework linkage. + ([#3206](https://github.com/ggml-org/whisper.cpp/pull/3206), + [#3270](https://github.com/ggml-org/whisper.cpp/pull/3270)) + +## [1.3.2] - 2025-05-28 + +Bundled whisper.cpp: `1.7.5` + +- Added VAD-enabled transcription and `Whisper::VAD::Params`. + ([#3197](https://github.com/ggml-org/whisper.cpp/pull/3197)) +- Moved native extension builds to CMake and added install-time CMake build + options. + ([#3043](https://github.com/ggml-org/whisper.cpp/pull/3043), + [#3056](https://github.com/ggml-org/whisper.cpp/pull/3056)) +- Added encoder-begin callback APIs and refined model download caching. + ([#3076](https://github.com/ggml-org/whisper.cpp/pull/3076), + [#3109](https://github.com/ggml-org/whisper.cpp/pull/3109)) +- Expanded context initialization and segment retrieval APIs. + ([#2749](https://github.com/ggml-org/whisper.cpp/pull/2749)) + +## [1.3.1] - 2024-12-20 + +Bundled whisper.cpp: `1.7.3` + +- Added Metal build support and new-segment callbacks. + ([#2516](https://github.com/ggml-org/whisper.cpp/pull/2516), + [#2506](https://github.com/ggml-org/whisper.cpp/pull/2506)) +- Expanded context, model, parameter, and segment APIs, including low-level + transcription methods. + ([#2518](https://github.com/ggml-org/whisper.cpp/pull/2518), + [#2551](https://github.com/ggml-org/whisper.cpp/pull/2551), + [#2585](https://github.com/ggml-org/whisper.cpp/pull/2585)) +- Added model download support and `no_speech_thold`. + ([#2617](https://github.com/ggml-org/whisper.cpp/pull/2617), + [#2641](https://github.com/ggml-org/whisper.cpp/pull/2641)) + +## [1.3.0] - 2024-05-16 + +Bundled whisper.cpp: `1.5.5`-era source snapshot + +- Refreshed the packaged whisper.cpp and ggml sources for the newer backend + architecture. +- Updated the native extension for upstream context initialization changes and + fixed builds against the newer source layout. +- Expanded the Rake packaging and test tasks used to produce the gem. + +## [1.2.0.2] - 2023-02-27 + +Bundled whisper.cpp: `1.2.0`-era source snapshot + +- Added Rake build, clean, package, and test tasks. +- Changed long-running transcription to run without Ruby's Global VM Lock and + added interruption handling. + +## [1.2.0.1] - 2023-02-25 + +Bundled whisper.cpp: `1.2.0`-era source snapshot + +- Fixed gem packaging so `LICENSE` and `README.md` were included at valid + in-package paths. + +## [1.2.0] - 2023-02-25 + +Bundled whisper.cpp: `1.2.0`-era source snapshot + +- First published `whispercpp` Ruby gem, based on the initial Ruby binding. + ([#500](https://github.com/ggml-org/whisper.cpp/pull/500)) + +[1.3.8]: https://rubygems.org/gems/whispercpp/versions/1.3.8 +[1.3.7]: https://rubygems.org/gems/whispercpp/versions/1.3.7 +[1.3.6]: https://rubygems.org/gems/whispercpp/versions/1.3.6 +[1.3.5]: https://rubygems.org/gems/whispercpp/versions/1.3.5 +[1.3.4]: https://rubygems.org/gems/whispercpp/versions/1.3.4 +[1.3.3]: https://rubygems.org/gems/whispercpp/versions/1.3.3 +[1.3.2]: https://rubygems.org/gems/whispercpp/versions/1.3.2 +[1.3.1]: https://rubygems.org/gems/whispercpp/versions/1.3.1 +[1.3.0]: https://rubygems.org/gems/whispercpp/versions/1.3.0 +[1.2.0.2]: https://rubygems.org/gems/whispercpp/versions/1.2.0.2 +[1.2.0.1]: https://rubygems.org/gems/whispercpp/versions/1.2.0.1 +[1.2.0]: https://rubygems.org/gems/whispercpp/versions/1.2.0 diff --git a/bindings/ruby/README.md b/bindings/ruby/README.md new file mode 100644 index 0000000..67a9ec3 --- /dev/null +++ b/bindings/ruby/README.md @@ -0,0 +1,497 @@ +whispercpp +========== + +![whisper.cpp](https://user-images.githubusercontent.com/1991296/235238348-05d0f6a4-da44-4900-a1de-d0707e75b763.jpeg) + +Ruby bindings for [whisper.cpp][], an interface of automatic speech recognition model. + +Usage +----- + +```ruby +require "whisper" + +whisper = Whisper::Context.new("base") + +params = Whisper::Params.new( + language: "en", + offset: 10_000, + duration: 60_000, + max_text_tokens: 300, + translate: true, + print_timestamps: false, + initial_prompt: "Initial prompt here.", + carry_initial_prompt: true +) + +whisper.transcribe("path/to/audio.wav", params) do |whole_text| + puts whole_text +end + +``` + +### Preparing model ### + +Some models are prepared up-front: + +You also can use shorthand for pre-converted models: + +```ruby +whisper = Whisper::Context.new("base.en") +``` + +You can see the list of prepared model names by `Whisper::Model.pre_converted_models.keys`: + +```ruby +puts Whisper::Model.pre_converted_models.keys +# tiny +# tiny.en +# tiny-q5_1 +# tiny.en-q5_1 +# tiny-q8_0 +# base +# base.en +# base-q5_1 +# base.en-q5_1 +# base-q8_0 +# : +# : +``` + +You can also retrieve each model: + +```ruby +base_en = Whisper::Model.pre_converted_models["base.en"] +whisper = Whisper::Context.new(base_en) +``` + +At first time you use a model, it is downloaded automatically. After that, downloaded cached file is used. To clear cache, call `#clear_cache`: + +```ruby +Whisper::Model.pre_converted_models["base"].clear_cache +``` + +You can also use local model files you prepared: + +```ruby +whisper = Whisper::Context.new("path/to/your/model.bin") +``` + +Or, you can download model files: + +```ruby +whisper = Whisper::Context.new("https://example.net/uri/of/your/model.bin") +# Or +uri = URI("https://example.net/uri/of/your/model.bin") +whisper = Whisper::Context.new(uri) +``` + +See [models][] page for details. + +### Preparing audio file ### + +Currently, whisper.cpp accepts only 16-bit WAV files. + +### Voice Activity Detection (VAD) ### + +Support for Voice Activity Detection (VAD) can be enabled by setting `Whisper::Params`'s `vad` argument to `true` and specifying VAD model: + +```ruby +Whisper::Params.new( + vad: true, + vad_model_path: "silero-v6.2.0", + # other arguments... +) +``` + +When you pass the model name (`"silero-v6.2.0"`) or URI (`https://huggingface.co/ggml-org/whisper-vad/resolve/main/ggml-silero-v6.2.0.bin`), it will be downloaded automatically. +Currently, "silero-v6.2.0" is registered as pre-converted model like ASR models. You also specify file path or URI of model. + +If you need configure VAD behavior, pass params for that: + +```ruby +Whisper::Params.new( + vad: true, + vad_model_path: "silero-v6.2.0", + vad_params: Whisper::VAD::Params.new( + threshold: 1.0, # defaults to 0.5 + min_speech_duration_ms: 500, # defaults to 250 + min_silence_duration_ms: 200, # defaults to 100 + max_speech_duration_s: 30000.0, # default is FLT_MAX, + speech_pad_ms: 50, # defaults to 30 + samples_overlap: 0.5 # defaults to 0.1 + ), + # other arguments... +) +``` + +For details on VAD, see [whisper.cpp's README](https://github.com/ggml-org/whisper.cpp?tab=readme-ov-file#voice-activity-detection-vad). + +### Output ### + +whispercpp supports SRT and WebVTT output: + +```ruby +puts whisper.transcribe("path/to/audio.wav", Whisper::Params.new).to_webvtt +# => +WEBVTT + +1 +00:00:00.000 --> 00:00:03.860 + My thought I have nobody by a beauty and will as you poured. + +2 +00:00:03.860 --> 00:00:09.840 + Mr. Rochester is sub in that so-don't find simplest, and devoted about, to let might in + +3 +00:00:09.840 --> 00:00:09.940 + a + +``` + +You may call `#to_srt`, too + +Installation +------------ + +Install the gem and add to the application's Gemfile by executing: + + $ bundle add whispercpp + +If bundler is not being used to manage dependencies, install the gem by executing: + + $ gem install whispercpp + +You can pass build options for whisper.cpp, for instance: + + $ bundle config build.whispercpp --enable-ggml-cuda + +or, + + $ gem install whispercpp -- --enable-ggml-cuda + +See whisper.cpp's [README](https://github.com/ggml-org/whisper.cpp/blob/master/README.md) for available options. You need convert options present in the README to Ruby-style options, for example: + +Boolean options: + +* `-DGGML_BLAS=1` -> `--enable-ggml-blas` +* `-DWHISPER_COREML=OFF` -> `--disable-whisper-coreml` + +Argument options: + +* `-DGGML_CUDA_COMPRESSION_MODE=size` -> `--ggml-cuda-compression-mode=size` + +Combination: + +* `-DGGML_CUDA=1 -DCMAKE_CUDA_ARCHITECTURES="86"` -> `--enable-ggml-cuda --cmake-cuda-architectures="86"` + +For boolean options like `GGML_CUDA`, the README says `-DGGML_CUDA=1`. You need strip `-D`, prepend `--enable-` for `1` or `ON` (`--disable-` for `0` or `OFF`) and make it kebab-case: `--enable-ggml-cuda`. +For options which require arguments like `CMAKE_CUDA_ARCHITECTURES`, the README says `-DCMAKE_CUDA_ARCHITECTURES="86"`. You need strip `-D`, prepend `--`, make it kebab-case, append `=` and append argument: `--cmake-cuda-architectures="86"`. + +API +--- + +### Transcription ### + +By default, `Whisper::Context#transcribe` works in a single thread. You can make it work in parallel by passing `n_processors` option: + +```ruby +whisper.transcribe("path/to/audio.wav", params, n_processors: Etc.nprocessors) +``` + +Note that transcription occasionally might be low accuracy when it works in parallel. + +If n_processors is greater than 1, you cannot set some callbacks including encoder_begin_callback, abort_callback. Also, progress_callback is ignored even if it's set. + +### Segments ### + +Once `Whisper::Context#transcribe` called, you can retrieve segments by `#each_segment`: + +```ruby +def format_time(time_ms) + sec, decimal_part = time_ms.divmod(1000) + min, sec = sec.divmod(60) + hour, min = min.divmod(60) + "%02d:%02d:%02d.%03d" % [hour, min, sec, decimal_part] +end + +whisper + .transcribe("path/to/audio.wav", params) + .each_segment.with_index do |segment, index| + line = "[%{nth}: %{st} --> %{ed}] %{text}" % { + nth: index + 1, + st: format_time(segment.start_time), + ed: format_time(segment.end_time), + text: segment.text + } + line << " (speaker turned)" if segment.speaker_turn_next? + puts line + end + +``` + +You can also add hook to params called on new segment: + +```ruby +# Add hook before calling #transcribe +params.on_new_segment do |segment| + line = "[%{st} --> %{ed}] %{text}" % { + st: format_time(segment.start_time), + ed: format_time(segment.end_time), + text: segment.text + } + line << " (speaker turned)" if segment.speaker_turn_next? + puts line +end + +whisper.transcribe("path/to/audio.wav", params) + +``` + +### Tokens ### + +Each segment has tokens. + +To enable token timestamps, you need to set `Whisper::Params#token_timestamps = true`. Then, retrieve tokens from segments using `Whisper::Segment#each_token`. + +```ruby +whisper = Whisper::Context.new("base.en") +params = Whisper::Params.new(token_timestamps: true) +whisper + .transcribe("path/to/audio.wav", params) + .each_segment do |segment| + segment.each_token do |token| + token => {start_time:, end_time:, text:, probability:} + st = "%05.2fs" % (start_time / 1000.0) + et = "%05.2fs" % (end_time / 1000.0) + prob = "%.1f%%" % (probability * 100) + puts "[#{st} --> #{et}] #{text} (#{prob})" + end + end +``` + +``` +[00.00s --> 00.00s] [_BEG_] (84.2%) +[00.32s --> 00.37s] And (71.2%) +[00.37s --> 00.53s] so (98.5%) +[00.69s --> 00.85s] my (70.7%) +[00.85s --> 01.59s] fellow (99.5%) +[01.59s --> 02.10s] Americans (90.1%) +[02.85s --> 03.30s] , (28.4%) +[03.30s --> 04.14s] ask (79.8%) +[04.14s --> 04.28s] not (78.9%) +[05.03s --> 05.35s] what (93.3%) +[05.41s --> 05.74s] your (98.8%) +[05.74s --> 06.41s] country (99.6%) +[06.41s --> 06.74s] can (97.7%) +[06.74s --> 06.92s] do (99.0%) +[07.00s --> 07.00s] for (95.8%) +[07.01s --> 07.52s] you (98.5%) +[07.81s --> 08.05s] , (49.3%) +[08.19s --> 08.37s] ask (65.6%) +[08.37s --> 08.75s] what (98.8%) +[08.91s --> 09.04s] you (98.2%) +[09.04s --> 09.32s] can (96.9%) +[09.32s --> 09.38s] do (90.3%) +[09.44s --> 09.76s] for (91.8%) +[09.76s --> 09.99s] your (98.2%) +[10.02s --> 10.36s] country (99.6%) +[10.51s --> 10.99s] . (87.0%) +[11.00s --> 11.00s] [_TT_550] (7.6%) +``` + +### Models ### + +You can see model information: + +```ruby +whisper = Whisper::Context.new("base") +model = whisper.model + +model.n_vocab # => 51864 +model.n_audio_ctx # => 1500 +model.n_audio_state # => 512 +model.n_audio_head # => 8 +model.n_audio_layer # => 6 +model.n_text_ctx # => 448 +model.n_text_state # => 512 +model.n_text_head # => 8 +model.n_text_layer # => 6 +model.n_mels # => 80 +model.ftype # => 1 +model.type # => "base" + +``` + +### Logging ### + +You can set log callback: + +```ruby +prefix = "[MyApp] " +log_callback = ->(level, buffer, user_data) { + case level + when Whisper::LOG_LEVEL_NONE + puts "#{user_data}none: #{buffer}" + when Whisper::LOG_LEVEL_INFO + puts "#{user_data}info: #{buffer}" + when Whisper::LOG_LEVEL_WARN + puts "#{user_data}warn: #{buffer}" + when Whisper::LOG_LEVEL_ERROR + puts "#{user_data}error: #{buffer}" + when Whisper::LOG_LEVEL_DEBUG + puts "#{user_data}debug: #{buffer}" + when Whisper::LOG_LEVEL_CONT + puts "#{user_data}same to previous: #{buffer}" + end +} +Whisper.log_set log_callback, prefix +``` + +Using this feature, you are also able to suppress log: + +```ruby +Whisper.log_set ->(level, buffer, user_data) { + # do nothing +}, nil +Whisper::Context.new("base") +``` + +### Low-level API to transcribe ### + +You can also call `Whisper::Context#full` and `#full_parallel` with a Ruby array as samples. Although `#transcribe` with audio file path is recommended because it extracts PCM samples in C++ and is fast, `#full` and `#full_parallel` give you flexibility. Unlike `#transcribe`, these methods requires 16,000 Hz, 32-bit float audio. + +```ruby +require "whisper" +require "wavefile" + +reader = WaveFile::Reader.new("path/to/audio.wav", WaveFile::Format.new(:mono, :float, 16000)) +samples = reader.enum_for(:each_buffer).map(&:samples).flatten + +whisper = Whisper::Context.new("base") +whisper + .full(Whisper::Params.new, samples) + .each_segment do |segment| + puts segment.text + end +``` + +The second argument `samples` may be an array, an object with `length` and `each` method, or a MemoryView. + +If you can prepare audio data as C array and export it as a MemoryView, whispercpp accepts and works with it with zero copy. + +```ruby +require "torchaudio" +require "ndav/torch/tensor" +require "whisper" + +waveform, sample_rate = TorchAudio.load("test/fixtures/jfk.wav") +# Convert Torch::Tensor to NDAV +samples = waveform.squeeze.to_ndav + +whisper = Whisper::Context.new("base") +whisper + # NDAV exports MemoryView + .full(Whisper::Params.new, samples) +``` + +### Parakeet ### + +whispercpp gem now supports NVIDIA's ASR model Parakeet. + +If you want to use Parakeet instead of Whisper, the API should feel familiar. +In most cases, replace `Whisper::Context` and `Whisper::Params` with `Whisper::Parakeet::Context` and `Whisper::Parakeet::Params`, then use `#transcribe`, `#full`, `#each_segment`, and `#each_token` in the same way. + +```ruby +require "whisper" + +# It's useful to assign Whisper::Parakeet to top-level Parakeet constant unless you use Parakeet gem. +Parakeet = Whisper::Parakeet + +parakeet = Parakeet::Context.new("path/to/model") + +params = Parakeet::Params.new( + no_context: true +) + +parakeet + .transcribe("path/to/audio.wav", params) + .each_segment do |segment| + puts "[#{segment.start_time} --> #{segment.end_time}] #{segment.text}" + end +``` + +The main differences are: + +* Namespace is `Whisper::Parakeet`. +* Parakeet also supports `on_new_token` / `new_token_callback` in addition to segment and progress callbacks. + +Custom context params +--------------------- + +You can use customize `Whisper::Context`'s behavior using `Whisper::Context::Params`. + +```ruby +context_params = Whisper::Context::Params.new( + use_gpu: false, + flash_attn: false, + # etc +) +whisper = Whisper::Context.new("base", context_params) +``` + +Using VAD separately from ASR +----------------------------- + +VAD feature itself is useful. You can use it separately from ASR: + +```ruby +vad = Whisper::VAD::Context.new("silero-v6.2.0") +vad + .detect("path/to/audio.wav", Whisper::VAD::Params.new) + .each.with_index do |segment, index| + segment => {start_time: st, end_time: ed} # `Segment` responds to `#deconstruct_keys` + + puts "[%{nth}: %{st} --> %{ed}]" % {nth: index + 1, st:, ed:} + end +``` + +You may also low level API `Whisper::VAD::Context#segments_from_samples` as such `Whisper::Context#full`: + +```ruby +# Ruby Array +reader = WaveFile::Reader.new("path/to/audio.wav", WaveFile::Format.new(:mono, :float, 16000)) +samples = reader.enum_for(:each_buffer).map(&:samples).flatten + +# Or, object which exports MemoryView +waveform, sample_rate = TorchAudio.load("test/fixtures/jfk.wav") +samples = waveform.squeeze.numo.to_arrow.to_arrow_array + +segments = vad.segments_from_samples(Whisper::VAD::Params.new, samples) +``` + +Development +----------- + + % git clone https://github.com/ggml-org/whisper.cpp.git + % cd whisper.cpp/bindings/ruby + % rake test + +First call of `rake test` builds an extension and downloads a model for testing. After that, you add tests in `test` directory and modify `ext/*.{h,c,cpp}`. + +If something seems wrong on build, running `rake clean` solves some cases. + +### Need help ### + +* Windows support +* Check of compilation with various hardware +* Refinement of C/C++ code, especially memory management + +License +------- + +The same to [whisper.cpp][]. + +[whisper.cpp]: https://github.com/ggml-org/whisper.cpp +[models]: https://github.com/ggml-org/whisper.cpp/tree/master/models diff --git a/bindings/ruby/Rakefile b/bindings/ruby/Rakefile new file mode 100644 index 0000000..2327651 --- /dev/null +++ b/bindings/ruby/Rakefile @@ -0,0 +1,111 @@ +require 'rake/clean' +require "bundler/gem_tasks" +require "rake/testtask" +require_relative "extsources" + +SOURCES_DIR = "ext/sources" + +SOURCES = FileList[] + +EXTSOURCES.each do |src| + basename = src.pathmap("%f") + dest = basename == "LICENSE" ? basename + : src.pathmap("%{\\.\\./\\.\\.,#{SOURCES_DIR}}p") + .pathmap("%{\\.\\./javascript,#{SOURCES_DIR}/bindings/javascript}p") + dir = dest.pathmap("%d") + file src + directory dir + file dest => [src, dir] do |t| + copy t.source, t.name + end + SOURCES.include dest +end + +CLEAN.include SOURCES + +SRC = FileList["ext/*.{c,cpp,h}"] + +task build: SOURCES + +directory "pkg" +CLOBBER.include "pkg" + +LIB_NAME = "whisper".ext(RbConfig::CONFIG["DLEXT"]) +SO_FILE = File.join("ext", LIB_NAME) +LIB_FILE = File.join("lib", LIB_NAME) + +file "ext/Makefile" => SRC + SOURCES + FileList["ext/*.rb"] do |t| + chdir "ext" do + ruby "extconf.rb" + end +end +if File.exist? "ext/Makefile" + task :make_clean do + cd "ext" do + sh "make", "clean" + end + end + task clean: :make_clean + task :make_distclean do + cd "ext" do + sh "make", "distclean" + end + end + task clobber: :make_distclean +end + +file SO_FILE => "ext/Makefile" do |t| + chdir "ext" do + sh "make" + end +end +CLEAN.include SO_FILE + +directory "lib" +file LIB_FILE => [SO_FILE, "lib"] do |t| + copy t.source, t.name +end +CLEAN.include LIB_FILE + +Rake::TestTask.new + +TEST_FIXTURE_AUDIO = "test/fixtures/jfk.wav" +TEST_FIXTURE_AUDIO_SRC = File.expand_path(File.join(__dir__, "..", "..", "samples", "jfk.wav")) +TEST_FIXTURE_AUDIO_DIR = TEST_FIXTURE_AUDIO.pathmap("%d") +directory TEST_FIXTURE_AUDIO_DIR +if File.exist? TEST_FIXTURE_AUDIO_SRC + file TEST_FIXTURE_AUDIO => [TEST_FIXTURE_AUDIO_SRC, TEST_FIXTURE_AUDIO_DIR] do |t| + symlink t.source, t.name + end +else + require "open-uri" + file TEST_FIXTURE_AUDIO => TEST_FIXTURE_AUDIO_DIR do |t| + File.write t.name, URI("https://github.com/ggml-org/whisper.cpp/raw/refs/heads/master/samples/jfk.wav").read + end +end + +TEST_PARAKEET_MODEL = "test/fixtures/for-tests-ggml-parakeet-tdt.bin" +TEST_PARAKEET_MODEL_SRC = File.expand_path(File.join(__dir__, "..", "..", "models", "for-tests-ggml-parakeet-tdt.bin")) +TEST_PARAKEET_MODEL_DIR = TEST_PARAKEET_MODEL.pathmap("%d") +directory TEST_PARAKEET_MODEL_DIR +if File.exist? TEST_PARAKEET_MODEL_SRC + file TEST_PARAKEET_MODEL => [TEST_PARAKEET_MODEL_SRC, TEST_PARAKEET_MODEL_DIR] do |t| + symlink t.source, t.name + end +else + require "open-uri" + file TEST_PARAKEET_MODEL => TEST_PARAKEET_MODEL_DIR do |t| + File.write t.name, URI("https://github.com/ggml-org/whisper.cpp/raw/refs/heads/master/models/for-tests-ggml-parakeet-tdt.bin").read + end +end + +TEST_MEMORY_VIEW = "test/jfk_reader/jfk_reader.#{RbConfig::CONFIG['DLEXT']}" +file TEST_MEMORY_VIEW => "test/jfk_reader/jfk_reader.c" do |t| + chdir "test/jfk_reader" do + ruby "extconf.rb" + sh "make" + end +end +CLEAN.include TEST_MEMORY_VIEW + +task test: [LIB_FILE, TEST_MEMORY_VIEW, TEST_FIXTURE_AUDIO, TEST_PARAKEET_MODEL] diff --git a/bindings/ruby/ext/.gitignore b/bindings/ruby/ext/.gitignore new file mode 100644 index 0000000..6fd36e4 --- /dev/null +++ b/bindings/ruby/ext/.gitignore @@ -0,0 +1,9 @@ +Makefile +whisper.so +whisper.bundle +whisper.dll +*.o +*.a +sources/* +!sources/CMakeGraphVizOptions.cmake +mkmf.log diff --git a/bindings/ruby/ext/dependencies.rb b/bindings/ruby/ext/dependencies.rb new file mode 100644 index 0000000..b2eb9be --- /dev/null +++ b/bindings/ruby/ext/dependencies.rb @@ -0,0 +1,79 @@ +require "tsort" + +class Dependencies + include TSort + + def initialize(cmake, options) + @cmake = cmake + @options = options + @static_lib_shape = nil + @nodes = {} + @graph = Hash.new {|h, k| h[k] = []} + + generate_dot + parse_dot + end + + def libs + tsort.filter_map {|node| + label, shape = @nodes[node] + if shape == @static_lib_shape + label.gsub(/\\n\([^)]+\)/, '') + else + nil + end + }.reverse.collect {|lib| "#{prefix(lib)}#{lib}.#{RbConfig::CONFIG['LIBEXT']}"} + end + + def to_s + libs.join(" ") + end + + def local_libs + to_s + end + + private + + def dot_path + File.join(__dir__, "build", "whisper.cpp.dot") + end + + def generate_dot + system @cmake, "-S", "sources", "-B", "build", *@options.graphviz_cmake_args, "--graphviz", dot_path, *@options, exception: true + end + + def parse_dot + File.open(dot_path).each_line do |line| + case line + when /\[\s*label\s*=\s*"Static Library"\s*,\s*shape\s*=\s*(?<shape>\w+)\s*\]/ + @static_lib_shape = $~[:shape] + when /\A\s*"(?<node>\w+)"\s*\[\s*label\s*=\s*"(?<label>\S+)"\s*,\s*shape\s*=\s*(?<shape>\w+)\s*\]\s*;\s*\z/ + node = $~[:node] + label = $~[:label] + shape = $~[:shape] + @nodes[node] = [label, shape] + when /\A\s*"(?<depender>\w+)"\s*->\s*"(?<dependee>\w+)"/ + depender = $~[:depender] + dependee = $~[:dependee] + @graph[depender] << dependee + end + end + end + + def prefix(lib) + "lib" + end + + def tsort_each_node + @nodes.each_key do |node| + yield node + end + end + + def tsort_each_child(node) + @graph[node].each do |child| + yield child + end + end +end diff --git a/bindings/ruby/ext/dependencies_for_windows.rb b/bindings/ruby/ext/dependencies_for_windows.rb new file mode 100644 index 0000000..5574107 --- /dev/null +++ b/bindings/ruby/ext/dependencies_for_windows.rb @@ -0,0 +1,17 @@ +require_relative "dependencies" + +class DependenciesForWindows < Dependencies + def local_libs + libs.collect {|lib| %|"#{lib_path(lib)}"|}.join(" ") + end + + private + + def prefix(lib) + lib.start_with?("ggml") ? "" : "lib" + end + + def lib_path(lib) + File.join(__dir__, lib).tr("\\", "/") + end +end diff --git a/bindings/ruby/ext/extconf.rb b/bindings/ruby/ext/extconf.rb new file mode 100644 index 0000000..99894f1 --- /dev/null +++ b/bindings/ruby/ext/extconf.rb @@ -0,0 +1,35 @@ +require "mkmf" + +if RUBY_PLATFORM.match? /mswin|mingw|ucrt/ + require_relative "options_for_windows" + require_relative "dependencies_for_windows" + + Opts = OptionsForWindows + Deps = DependenciesForWindows +else + require_relative "options" + require_relative "dependencies" + + Opts = Options + Deps = Dependencies +end + +cmake = find_executable("cmake") || abort +options = Opts.new(cmake) +have_library("gomp") rescue nil +libs = Deps.new(cmake, options) + +append_cflags ["-O3", "-march=native"] +$INCFLAGS << " -Isources/include -Isources/ggml/include -Isources/examples" +$LOCAL_LIBS << " #{libs.local_libs}" +$cleanfiles << " build #{libs}" + +create_makefile "whisper" do |conf| + conf << <<~EOF + $(TARGET_SO): #{libs} + #{libs}: cmake-targets + cmake-targets: + #{"\t"}"#{cmake}" -S sources -B build #{options} + #{"\t"}"#{cmake}" --build build --config Release --target common whisper parakeet + EOF +end diff --git a/bindings/ruby/ext/options.rb b/bindings/ruby/ext/options.rb new file mode 100644 index 0000000..c097ccb --- /dev/null +++ b/bindings/ruby/ext/options.rb @@ -0,0 +1,125 @@ +require "fileutils" + +class Options + def initialize(cmake="cmake") + @cmake = cmake + @options = {} + + configure + write_cache_file + end + + def to_a + [ + "-D", "BUILD_SHARED_LIBS=OFF", + "-D", "WHISPER_BUILD_TESTS=OFF", + "-D", "CMAKE_ARCHIVE_OUTPUT_DIRECTORY=#{__dir__}", + "-D", "CMAKE_POSITION_INDEPENDENT_CODE=ON", + "-C", cache_path + ] + end + + def to_s + command_line(*to_a) + end + + def graphviz_cmake_args + [] + end + + private + + def cmake_options + @cmake_options ||= cmake_options_output.lines.drop_while {|line| line.chomp != "-- Cache values"}.drop(1) + .filter_map {|line| + option, value = line.chomp.split("=", 2) + name, type = option.split(":", 2) + [ + name, + [ + type, + type == "BOOL" ? value == "ON" : value + ] + ] + }.to_h + end + + def cmake_options_output + Dir.chdir(__dir__) do + IO.popen([@cmake, "-S", "sources", "-B", "build", "-L"]) {|io| io.read} + end + end + + def configure + cmake_options.each_pair do |name, (type, default_value)| + option = option_name(name) + value = type == "BOOL" ? enable_config(option) : arg_config("--#{option}") + @options[name] = [type, value] + end + + configure_accelerate + configure_metal + configure_coreml + end + + # See ggml/src/ggml-cpu/CMakeLists.txt + def configure_accelerate + if RUBY_PLATFORM.match?(/darwin/) && enabled?("GGML_ACCELERATE") + $LDFLAGS << " -framework Accelerate" + end + end + + # See ggml/src/ggml-metal/CMakeLists.txt + def configure_metal + $LDFLAGS << " -framework Foundation -framework Metal -framework MetalKit" if enabled?("GGML_METAL") + end + + # See src/CmakeLists.txt + def configure_coreml + if enabled?("WHISPER_COREML") + $LDFLAGS << " -framework Foundation -framework CoreML" + $defs << "-DRUBY_WHISPER_USE_COREML" + end + end + + def option_name(name) + name.downcase.gsub("_", "-") + end + + def enabled?(option) + op = @options[option] + return false unless op + return false unless op[0] == "BOOL" + if op[1].nil? + cmake_options[option][1] + else + op[1] + end + end + + def cache_path + File.join(__dir__, "sources", "Options.cmake") + end + + def write_cache_file + FileUtils.mkpath File.dirname(cache_path) + File.open cache_path, "w" do |file| + @options.reject {|name, (type, value)| value.nil?}.each do |name, (type, value)| + line = 'set(%<name>s %<value>s CACHE %<type>s "" FORCE)' %{ + name:, + type:, + value: value == true ? "ON" : value == false ? "OFF" : escape_cmake(value) + } + file.puts line + end + end + end + + def escape_cmake(str) + str.gsub(/[\\"]/, '\\\\\&') + end + + def command_line(*args) + args.collect {|arg| %|"#{arg.to_s.gsub(/[\\"]/, '\\\\\&')}"|}.join(" ") + end +end diff --git a/bindings/ruby/ext/options_for_windows.rb b/bindings/ruby/ext/options_for_windows.rb new file mode 100644 index 0000000..7db785d --- /dev/null +++ b/bindings/ruby/ext/options_for_windows.rb @@ -0,0 +1,51 @@ +require_relative "options" + +class OptionsForWindows < Options + def to_s + command_line(*generator_args, *to_a) + end + + def graphviz_cmake_args + generator_args + end + + private + + def arm? + RbConfig::CONFIG["host_cpu"].to_s.downcase.match?(/\A(?:arm64|aarch64)\z/) + end + + def cmake_options_output + Dir.chdir(__dir__) do + IO.popen([@cmake, "-S", "sources", "-B", "build", *generator_args, "-L"]) {|io| io.read} + end + end + + def generator_args + generator = cmake_generator + ["-G", generator] if generator && !generator.empty? + end + + def cmake_generator + return @cmake_generator if defined?(@cmake_generator) + + generator = ENV["CMAKE_GENERATOR"] + abort "CMAKE_GENERATOR=#{generator} is unsupported for mingw/ucrt Ruby" if visual_studio_generator_name?(generator) + return @cmake_generator = generator unless generator.nil? || generator.empty? + + ninja = find_executable("ninja") + return @cmake_generator = "Ninja" if ninja + + make = find_executable("make") + return @cmake_generator = "MSYS Makefiles" if make + + mingw32_make = find_executable("mingw32-make") + return @cmake_generator = "MinGW Makefiles" if mingw32_make + + @cmake_generator = nil + end + + def visual_studio_generator_name?(generator) + generator && generator.start_with?("Visual Studio") + end +end diff --git a/bindings/ruby/ext/ruby_whisper.c b/bindings/ruby/ext/ruby_whisper.c new file mode 100644 index 0000000..bd852a8 --- /dev/null +++ b/bindings/ruby/ext/ruby_whisper.c @@ -0,0 +1,198 @@ +#include "ruby_whisper.h" + +VALUE mWhisper; +VALUE mLogSettable; +VALUE mVAD; +VALUE mParakeet; +VALUE cContext; +VALUE cParams; +VALUE cVADContext; +VALUE cVADParams; +VALUE cVADSegments; +VALUE cVADSegment; +VALUE cParakeetContext; +VALUE cParakeetContextParams; +VALUE cParakeetParams; +VALUE cParakeetSegment; +VALUE cParakeetModel; +VALUE eError; + +VALUE cSegment; +VALUE cToken; +VALUE cModel; + +VALUE mOutputContext; +VALUE mOutputSegment; + +ID id_to_s; +ID id_call; +ID id___method__; +ID id_to_enum; +ID id_length; +ID id_next; +ID id_new; +ID id_to_path; +ID id_URI; +ID id_pre_converted_models; +ID id_coreml_compiled_models; +ID id_cache; +ID id_n_processors; +ID id_extended; +ID id_start_log_callback_thread; +ID id_log_callback_thread; +ID id_alive_p; +ID id_join; + +// High level API +extern VALUE ruby_whisper_segment_allocate(VALUE klass); + +extern VALUE init_ruby_whisper_context(VALUE *mWhisper); +extern void init_ruby_whisper_context_params(VALUE *cContext); +extern void init_ruby_whisper_params(VALUE *mWhisper); +extern void init_ruby_whisper_error(VALUE *mWhisper); +extern void init_ruby_whisper_segment(VALUE *mWhisper); +extern void init_ruby_whisper_token(VALUE *mWhisper); +extern void init_ruby_whisper_model(VALUE *mWhisper); +extern void init_ruby_whisper_vad_params(VALUE *mVAD); +extern void init_ruby_whisper_vad_context(VALUE *mVAD); +extern void init_ruby_whisper_vad_segment(VALUE *mVAD); +extern void init_ruby_whisper_vad_segments(VALUE *mVAD); +extern void init_ruby_whisper_parakeet(VALUE *mWhisper); +extern void register_callbacks(ruby_whisper_params *rwp, VALUE *context); + +static ruby_whisper_log_queue whisper_log_queue; + +LOG_SETTABLE_SETUP(whisper_log_queue, mWhisper, whisper_log_set) + +/* + * call-seq: + * lang_max_id -> Integer + */ +static VALUE ruby_whisper_s_lang_max_id(VALUE self) { + return INT2NUM(whisper_lang_max_id()); +} + +/* + * call-seq: + * lang_id(lang_name) -> Integer + */ +static VALUE ruby_whisper_s_lang_id(VALUE self, VALUE lang) { + const char * lang_str = StringValueCStr(lang); + const int id = whisper_lang_id(lang_str); + if (-1 == id) { + rb_raise(rb_eArgError, "language not found: %s", lang_str); + } + return INT2NUM(id); +} + +/* + * call-seq: + * lang_str(lang_id) -> String + */ +static VALUE ruby_whisper_s_lang_str(VALUE self, VALUE id) { + const int lang_id = NUM2INT(id); + const char * str = whisper_lang_str(lang_id); + if (NULL == str) { + rb_raise(rb_eIndexError, "id %d outside of language id", lang_id); + } + return rb_str_new2(str); +} + +/* + * call-seq: + * lang_str(lang_id) -> String + */ +static VALUE ruby_whisper_s_lang_str_full(VALUE self, VALUE id) { + const int lang_id = NUM2INT(id); + const char * str_full = whisper_lang_str_full(lang_id); + if (NULL == str_full) { + rb_raise(rb_eIndexError, "id %d outside of language id", lang_id); + } + return rb_str_new2(str_full); +} + +/* + * call-seq: + * system_info_str -> String + */ +static VALUE ruby_whisper_s_system_info_str(VALUE self) { + return rb_str_new2(whisper_print_system_info()); +} + +void Init_whisper() { + id_to_s = rb_intern("to_s"); + id_call = rb_intern("call"); + id___method__ = rb_intern("__method__"); + id_to_enum = rb_intern("to_enum"); + id_length = rb_intern("length"); + id_next = rb_intern("next"); + id_new = rb_intern("new"); + id_to_path = rb_intern("to_path"); + id_URI = rb_intern("URI"); + id_pre_converted_models = rb_intern("pre_converted_models"); + id_coreml_compiled_models = rb_intern("coreml_compiled_models"); + id_cache = rb_intern("cache"); + id_n_processors = rb_intern("n_processors"); + id_start_log_callback_thread = rb_intern("start_log_callback_thread"); + id_log_callback_thread = rb_intern("@log_callback_thread"); + id_alive_p = rb_intern("alive?"); + id_join = rb_intern("join"); + + mWhisper = rb_define_module("Whisper"); + rb_require("whisper/log_settable"); + mLogSettable = rb_path2class("Whisper::LogSettable"); + mVAD = rb_define_module_under(mWhisper, "VAD"); + rb_require("whisper/output"); + mOutputContext = rb_path2class("Whisper::Output::Context"); + mOutputSegment = rb_path2class("Whisper::Output::Segment"); + + rb_define_const(mWhisper, "VERSION", rb_str_new2(whisper_version())); + rb_define_const(mWhisper, "LOG_LEVEL_NONE", INT2NUM(GGML_LOG_LEVEL_NONE)); + rb_define_const(mWhisper, "LOG_LEVEL_INFO", INT2NUM(GGML_LOG_LEVEL_INFO)); + rb_define_const(mWhisper, "LOG_LEVEL_WARN", INT2NUM(GGML_LOG_LEVEL_WARN)); + rb_define_const(mWhisper, "LOG_LEVEL_ERROR", INT2NUM(GGML_LOG_LEVEL_ERROR)); + rb_define_const(mWhisper, "LOG_LEVEL_DEBUG", INT2NUM(GGML_LOG_LEVEL_DEBUG)); + rb_define_const(mWhisper, "LOG_LEVEL_CONT", INT2NUM(GGML_LOG_LEVEL_CONT)); + + rb_define_const(mWhisper, "AHEADS_NONE", INT2NUM(WHISPER_AHEADS_NONE)); + rb_define_const(mWhisper, "AHEADS_N_TOP_MOST", INT2NUM(WHISPER_AHEADS_N_TOP_MOST)); + rb_define_const(mWhisper, "AHEADS_CUSTOM", INT2NUM(WHISPER_AHEADS_CUSTOM)); + rb_define_const(mWhisper, "AHEADS_TINY_EN", INT2NUM(WHISPER_AHEADS_TINY_EN)); + rb_define_const(mWhisper, "AHEADS_TINY", INT2NUM(WHISPER_AHEADS_TINY)); + rb_define_const(mWhisper, "AHEADS_BASE_EN", INT2NUM(WHISPER_AHEADS_BASE_EN)); + rb_define_const(mWhisper, "AHEADS_BASE", INT2NUM(WHISPER_AHEADS_BASE)); + rb_define_const(mWhisper, "AHEADS_SMALL_EN", INT2NUM(WHISPER_AHEADS_SMALL_EN)); + rb_define_const(mWhisper, "AHEADS_SMALL", INT2NUM(WHISPER_AHEADS_SMALL)); + rb_define_const(mWhisper, "AHEADS_MEDIUM_EN", INT2NUM(WHISPER_AHEADS_MEDIUM_EN)); + rb_define_const(mWhisper, "AHEADS_MEDIUM", INT2NUM(WHISPER_AHEADS_MEDIUM)); + rb_define_const(mWhisper, "AHEADS_LARGE_V1", INT2NUM(WHISPER_AHEADS_LARGE_V1)); + rb_define_const(mWhisper, "AHEADS_LARGE_V2", INT2NUM(WHISPER_AHEADS_LARGE_V2)); + rb_define_const(mWhisper, "AHEADS_LARGE_V3", INT2NUM(WHISPER_AHEADS_LARGE_V3)); + rb_define_const(mWhisper, "AHEADS_LARGE_V3_TURBO", INT2NUM(WHISPER_AHEADS_LARGE_V3_TURBO)); + + rb_define_singleton_method(mWhisper, "lang_max_id", ruby_whisper_s_lang_max_id, 0); + rb_define_singleton_method(mWhisper, "lang_id", ruby_whisper_s_lang_id, 1); + rb_define_singleton_method(mWhisper, "lang_str", ruby_whisper_s_lang_str, 1); + rb_define_singleton_method(mWhisper, "lang_str_full", ruby_whisper_s_lang_str_full, 1); + rb_define_singleton_method(mWhisper, "system_info_str", ruby_whisper_s_system_info_str, 0); + + LOG_SETTABLE_INIT(whisper_log_queue, mWhisper) + + cContext = init_ruby_whisper_context(&mWhisper); + init_ruby_whisper_context_params(&cContext); + init_ruby_whisper_params(&mWhisper); + init_ruby_whisper_error(&mWhisper); + init_ruby_whisper_segment(&mWhisper); + init_ruby_whisper_token(&mWhisper); + init_ruby_whisper_model(&mWhisper); + init_ruby_whisper_vad_params(&mVAD); + init_ruby_whisper_vad_segment(&mVAD); + init_ruby_whisper_vad_segments(&mVAD); + init_ruby_whisper_vad_context(&mVAD); + init_ruby_whisper_parakeet(&mWhisper); + + rb_require("whisper/model/uri"); + + rb_include_module(cContext, mOutputContext); + rb_include_module(cSegment, mOutputSegment); +} diff --git a/bindings/ruby/ext/ruby_whisper.h b/bindings/ruby/ext/ruby_whisper.h new file mode 100644 index 0000000..85ac63e --- /dev/null +++ b/bindings/ruby/ext/ruby_whisper.h @@ -0,0 +1,248 @@ +#ifndef RUBY_WHISPER_H +#define RUBY_WHISPER_H + +#include <ruby.h> +#include <ruby/version.h> +#include <ruby/util.h> +#include <ruby/thread.h> +#include <ruby/thread_native.h> +#include <ruby/atomic.h> +#include <ruby/memory_view.h> +#include "whisper.h" +#include "parakeet.h" +#include "ruby_whisper_log_settable.h" + +#if RUBY_API_VERSION_MAJOR < 4 +// Exists but not declared as public API +int ruby_thread_has_gvl_p(void); +#endif + +typedef struct { + VALUE *context; + VALUE user_data; + VALUE callback; + VALUE callbacks; +} ruby_whisper_callback_container; + +typedef struct ruby_whisper_abort_callback_user_data { + volatile rb_atomic_t is_interrupted; + ruby_whisper_callback_container *callback_container; +} ruby_whisper_abort_callback_user_data; + +typedef struct ruby_whisper_log { + enum ggml_log_level level; + char *text; + size_t length; + size_t capacity; +} ruby_whisper_log; + +typedef struct ruby_whisper_log_queue { + rb_nativethread_lock_t lock; + rb_nativethread_cond_t cond; + bool is_open; + + size_t head; + size_t tail; + size_t size; + ruby_whisper_log *logs; +} ruby_whisper_log_queue; + +typedef struct { + struct whisper_context *context; +} ruby_whisper; + +typedef struct ruby_whisper_context_params { + struct whisper_context_params params; +} ruby_whisper_context_params; + +typedef struct { + struct whisper_full_params params; + bool diarize; + ruby_whisper_callback_container *new_segment_callback_container; + ruby_whisper_callback_container *progress_callback_container; + ruby_whisper_callback_container *encoder_begin_callback_container; + ruby_whisper_callback_container *abort_callback_container; + VALUE vad_params; +} ruby_whisper_params; + +typedef struct { + struct whisper_vad_params params; +} ruby_whisper_vad_params; + +typedef struct { + VALUE context; + int index; +} ruby_whisper_segment; + +typedef struct { + whisper_token_data *token_data; + VALUE text; +} ruby_whisper_token; + +typedef struct { + VALUE context; +} ruby_whisper_model; + +typedef struct { + struct whisper_vad_segments *segments; +} ruby_whisper_vad_segments; + +typedef struct { + VALUE segments; + int index; +} ruby_whisper_vad_segment; + +typedef struct { + struct whisper_vad_context *context; +} ruby_whisper_vad_context; + +typedef struct parsed_samples_t { + float *samples; + int n_samples; + rb_memory_view_t memview; + bool memview_exported; +} parsed_samples_t; + +typedef struct { + VALUE *context; + VALUE *params; + float *samples; + int n_samples; +} ruby_whisper_full_args; + +typedef struct segments_from_samples_args { + VALUE *context; + VALUE *params; + float *samples; + int n_samples; +} segments_from_samples_args; + +typedef struct ruby_whisper_full_parallel_args { + VALUE *context; + VALUE *params; + float *samples; + int n_samples; + int n_processors; +} ruby_whisper_full_parallel_args; + +typedef struct { + struct parakeet_full_params params; + ruby_whisper_callback_container *new_segment_callback_container; + ruby_whisper_callback_container *new_token_callback_container; + ruby_whisper_callback_container *progress_callback_container; + ruby_whisper_callback_container *encoder_begin_callback_container; + ruby_whisper_callback_container *abort_callback_container; +} ruby_whisper_parakeet_params; + +typedef struct { + struct parakeet_context_params params; +} ruby_whisper_parakeet_context_params; + +typedef struct { + struct parakeet_context *context; +} ruby_whisper_parakeet_context; + +typedef struct { + VALUE context; + int index; +} ruby_whisper_parakeet_segment; + +typedef struct { + parakeet_token_data *token_data; + VALUE text; +} ruby_whisper_parakeet_token; + +typedef struct { + VALUE context; +} ruby_whisper_parakeet_model; + +extern ID id_log_callback_thread; +extern ID id_start_log_callback_thread; +extern ID id_alive_p; +extern ID id_join; +extern void ruby_whisper_log_queue_initialize(ruby_whisper_log_queue *log_queue); +extern void ruby_whisper_log_queue_open(ruby_whisper_log_queue *log_queue); +extern void ruby_whisper_log_queue_close(ruby_whisper_log_queue *log_queue); +extern void ruby_whisper_log_queue_enqueue(ruby_whisper_log_queue *log_queue, enum ggml_log_level level, const char *text); +extern VALUE ruby_whisper_log_queue_drain(ruby_whisper_log_queue *log_queue); + +#define GetContext(obj, rw) do { \ + TypedData_Get_Struct((obj), ruby_whisper, &ruby_whisper_type, (rw)); \ + if ((rw)->context == NULL) { \ + rb_raise(rb_eRuntimeError, "Already freed or not initialized"); \ + } \ +} while (0) + +#define GetContextParams(obj, rwcp) do { \ + TypedData_Get_Struct((obj), ruby_whisper_context_params, &ruby_whisper_context_params_type, (rwcp)); \ +} while (0) + +#define GetToken(obj, rwt) do { \ + TypedData_Get_Struct((obj), ruby_whisper_token, &ruby_whisper_token_type, (rwt)); \ + if ((rwt)->token_data == NULL) { \ + rb_raise(rb_eRuntimeError, "Not initialized"); \ + } \ +} while (0) + +#define GetVADContext(obj, rwvc) do { \ + TypedData_Get_Struct((obj), ruby_whisper_vad_context, &ruby_whisper_vad_context_type, (rwvc)); \ + if ((rwvc)->context == NULL) { \ + rb_raise(rb_eRuntimeError, "Already freed or not initialized"); \ + } \ +} while (0) + +#define GetVADParams(obj, rwvp) do { \ + TypedData_Get_Struct((obj), ruby_whisper_vad_params, &ruby_whisper_vad_params_type, (rwvp)); \ +} while (0) + +#define GetVADSegments(obj, rwvss) do { \ + TypedData_Get_Struct((obj), ruby_whisper_vad_segments, &ruby_whisper_vad_segments_type, (rwvss)); \ + if ((rwvss)->segments == NULL) { \ + rb_raise(rb_eRuntimeError, "Not initialized"); \ + } \ +} while (0) + +#define GetParakeetContextParams(obj, rwpcp) do { \ + TypedData_Get_Struct((obj), ruby_whisper_parakeet_context_params, &ruby_whisper_parakeet_context_params_type, (rwpcp)); \ +} while (0) + +#define GetParakeetContext(obj, rwpc) do { \ + TypedData_Get_Struct((obj), ruby_whisper_parakeet_context, &ruby_whisper_parakeet_context_type, (rwpc)); \ + if ((rwpc)->context == NULL) { \ + rb_raise(rb_eRuntimeError, "Already freed or not initialized"); \ + } \ +} while (0) + +#define GetParakeetParams(obj, rwpp) do { \ + TypedData_Get_Struct((obj), ruby_whisper_parakeet_params, &ruby_whisper_parakeet_params_type, (rwpp)); \ + if (!(rwpp)->new_segment_callback_container || \ + !(rwpp)->new_token_callback_container || \ + !(rwpp)->progress_callback_container || \ + !(rwpp)->encoder_begin_callback_container || \ + !(rwpp)->abort_callback_container) { \ + rb_raise(rb_eRuntimeError, "Not initialized"); \ + } \ +} while (0) + +#define GetParakeetSegment(obj, rwps) do { \ + TypedData_Get_Struct((obj), ruby_whisper_parakeet_segment, &ruby_whisper_parakeet_segment_type, (rwps)); \ + if (!(rwps)->context) { \ + rb_raise(rb_eRuntimeError, "Not initialized"); \ + } \ +} while (0) + +#define GetParakeetToken(obj, rwpt) do { \ + TypedData_Get_Struct((obj), ruby_whisper_parakeet_token, &ruby_whisper_parakeet_token_type, (rwpt)); \ + if (!(rwpt)->token_data) { \ + rb_raise(rb_eRuntimeError, "Not initialized"); \ + } \ +} while (0) + +#define GetParakeetModel(obj, rwpm) do { \ + TypedData_Get_Struct((obj), ruby_whisper_parakeet_model, &ruby_whisper_parakeet_model_type, (rwpm)); \ + if (NIL_P((rwpm)->context)) { \ + rb_raise(rb_eRuntimeError, "Not initialized"); \ + } \ +} while (0) + +#endif diff --git a/bindings/ruby/ext/ruby_whisper_context.c b/bindings/ruby/ext/ruby_whisper_context.c new file mode 100644 index 0000000..0787902 --- /dev/null +++ b/bindings/ruby/ext/ruby_whisper_context.c @@ -0,0 +1,941 @@ +#include "ruby_whisper.h" + +#ifdef WORDS_BIGENDIAN + #define IS_BIGENDIAN true +#else + #define IS_BIGENDIAN false +#endif + +extern ID id_to_s; +extern ID id___method__; +extern ID id_to_enum; +extern ID id_length; +extern ID id_next; +extern ID id_new; +extern ID id_to_path; +extern ID id_URI; +extern ID id_pre_converted_models; +extern ID id_coreml_compiled_models; +extern ID id_cache; +extern ID id_n_processors; + +extern VALUE cContext; +extern VALUE eError; +extern VALUE cModel; + +extern const rb_data_type_t ruby_whisper_params_type; +extern const rb_data_type_t ruby_whisper_context_params_type; +extern VALUE ruby_whisper_transcribe(int argc, VALUE *argv, VALUE self); +extern VALUE rb_whisper_model_s_new(VALUE context); +extern VALUE rb_whisper_segment_s_new(VALUE context, int index); +extern void prepare_transcription(ruby_whisper_params *rwp, VALUE *context, int n_processors, ruby_whisper_abort_callback_user_data *abort_callback_user_data); + +ID transcribe_option_names[1]; + +typedef struct fill_samples_args { + float *dest; + VALUE *src; + int n_samples; +} fill_samples_args; + +typedef struct full_without_gvl_args { + struct whisper_context *context; + struct whisper_full_params *params; + float *samples; + int n_samples; + int result; +} full_without_gvl_args; + +typedef struct full_parallel_without_gvl_args { + struct whisper_context *context; + struct whisper_full_params *params; + float *samples; + int n_samples; + int n_processors; + int result; +} full_parallel_without_gvl_args; + +typedef struct full_ubf_args { + ruby_whisper_abort_callback_user_data *abort_callback_user_data; +} full_ubf_args; + +static void +ruby_whisper_free(ruby_whisper *rw) +{ + if (rw->context) { + whisper_free(rw->context); + rw->context = NULL; + } +} + +void +rb_whisper_mark(ruby_whisper *rw) +{ + // call rb_gc_mark on any ruby references in rw +} + +void +rb_whisper_free(void *p) +{ + ruby_whisper *rw = (ruby_whisper *)p; + ruby_whisper_free(rw); + free(rw); +} + +static size_t +ruby_whisper_memsize(const void *p) +{ + const ruby_whisper *rw = (const ruby_whisper *)p; + size_t size = sizeof(*rw); + if (!rw) { + return 0; + } + if (rw->context) { + size += sizeof(rw->context); + } + return size; +} + +const rb_data_type_t ruby_whisper_type = { + "ruby_whisper", + {0, rb_whisper_free, ruby_whisper_memsize,}, + 0, 0, + 0 +}; + +static VALUE +ruby_whisper_allocate(VALUE klass) +{ + ruby_whisper *rw; + VALUE obj = TypedData_Make_Struct(klass, ruby_whisper, &ruby_whisper_type, rw); + rw->context = NULL; + return obj; +} + +VALUE +ruby_whisper_normalize_model_path(VALUE model_path) +{ + VALUE pre_converted_models = rb_funcall(cModel, id_pre_converted_models, 0); + VALUE pre_converted_model = rb_hash_aref(pre_converted_models, model_path); + if (!NIL_P(pre_converted_model)) { + model_path = pre_converted_model; +#ifdef RUBY_WHISPER_USE_COREML + VALUE coreml_converted_models = rb_funcall(cModel, id_coreml_compiled_models, 0); + VALUE coreml_converted_model = rb_hash_aref(coreml_converted_models, pre_converted_model); + if (!NIL_P(coreml_converted_model)) { + rb_funcall(coreml_converted_model, id_cache, 0); + } +#endif + } + else if (TYPE(model_path) == T_STRING) { + const char * model_path_str = StringValueCStr(model_path); + if (strncmp("http://", model_path_str, 7) == 0 || strncmp("https://", model_path_str, 8) == 0) { + VALUE uri_class = rb_const_get(cModel, id_URI); + model_path = rb_class_new_instance(1, &model_path, uri_class); + } + } + else if (rb_obj_is_kind_of(model_path, rb_path2class("URI::HTTP"))) { + VALUE uri_class = rb_const_get(cModel, id_URI); + model_path = rb_class_new_instance(1, &model_path, uri_class); + } + if (rb_respond_to(model_path, id_to_path)) { + model_path = rb_funcall(model_path, id_to_path, 0); + } + + return model_path; +} + +/* + * call-seq: + * new("base.en") -> Whisper::Context + * new("path/to/model.bin") -> Whisper::Context + * new(Whisper::Model::URI.new("https://example.net/uri/of/model.bin")) -> Whisper::Context + */ +static VALUE +ruby_whisper_initialize(int argc, VALUE *argv, VALUE self) +{ + ruby_whisper *rw; + VALUE whisper_model_file_path; + VALUE context_params; + struct whisper_context_params params; + + // TODO: we can support init from buffer here too maybe another ruby object to expose + rb_scan_args(argc, argv, "11", &whisper_model_file_path, &context_params); + TypedData_Get_Struct(self, ruby_whisper, &ruby_whisper_type, rw); + + whisper_model_file_path = ruby_whisper_normalize_model_path(whisper_model_file_path); + if (!rb_respond_to(whisper_model_file_path, id_to_s)) { + rb_raise(rb_eRuntimeError, "Expected file path to model to initialize Whisper::Context"); + } + if (NIL_P(context_params)) { + params = whisper_context_default_params(); + } else { + ruby_whisper_context_params *rwcp; + GetContextParams(context_params, rwcp); + params = rwcp->params; + } + rw->context = whisper_init_from_file_with_params(StringValueCStr(whisper_model_file_path), params); + if (rw->context == NULL) { + rb_raise(rb_eRuntimeError, "error: failed to initialize whisper context"); + } + return self; +} + +/* + * call-seq: + * model_n_vocab -> Integer + */ +VALUE ruby_whisper_model_n_vocab(VALUE self) +{ + ruby_whisper *rw; + GetContext(self, rw); + return INT2NUM(whisper_model_n_vocab(rw->context)); +} + +/* + * call-seq: + * model_n_audio_ctx -> Integer + */ +VALUE ruby_whisper_model_n_audio_ctx(VALUE self) +{ + ruby_whisper *rw; + GetContext(self, rw); + return INT2NUM(whisper_model_n_audio_ctx(rw->context)); +} + +/* + * call-seq: + * model_n_audio_state -> Integer + */ +VALUE ruby_whisper_model_n_audio_state(VALUE self) +{ + ruby_whisper *rw; + GetContext(self, rw); + return INT2NUM(whisper_model_n_audio_state(rw->context)); +} + +/* + * call-seq: + * model_n_audio_head -> Integer + */ +VALUE ruby_whisper_model_n_audio_head(VALUE self) +{ + ruby_whisper *rw; + GetContext(self, rw); + return INT2NUM(whisper_model_n_audio_head(rw->context)); +} + +/* + * call-seq: + * model_n_audio_layer -> Integer + */ +VALUE ruby_whisper_model_n_audio_layer(VALUE self) +{ + ruby_whisper *rw; + GetContext(self, rw); + return INT2NUM(whisper_model_n_audio_layer(rw->context)); +} + +/* + * call-seq: + * model_n_text_ctx -> Integer + */ +VALUE ruby_whisper_model_n_text_ctx(VALUE self) +{ + ruby_whisper *rw; + GetContext(self, rw); + return INT2NUM(whisper_model_n_text_ctx(rw->context)); +} + +/* + * call-seq: + * model_n_text_state -> Integer + */ +VALUE ruby_whisper_model_n_text_state(VALUE self) +{ + ruby_whisper *rw; + GetContext(self, rw); + return INT2NUM(whisper_model_n_text_state(rw->context)); +} + +/* + * call-seq: + * model_n_text_head -> Integer + */ +VALUE ruby_whisper_model_n_text_head(VALUE self) +{ + ruby_whisper *rw; + GetContext(self, rw); + return INT2NUM(whisper_model_n_text_head(rw->context)); +} + +/* + * call-seq: + * model_n_text_layer -> Integer + */ +VALUE ruby_whisper_model_n_text_layer(VALUE self) +{ + ruby_whisper *rw; + GetContext(self, rw); + return INT2NUM(whisper_model_n_text_layer(rw->context)); +} + +/* + * call-seq: + * model_n_mels -> Integer + */ +VALUE ruby_whisper_model_n_mels(VALUE self) +{ + ruby_whisper *rw; + GetContext(self, rw); + return INT2NUM(whisper_model_n_mels(rw->context)); +} + +/* + * call-seq: + * model_ftype -> Integer + */ +VALUE ruby_whisper_model_ftype(VALUE self) +{ + ruby_whisper *rw; + GetContext(self, rw); + return INT2NUM(whisper_model_ftype(rw->context)); +} + +/* + * call-seq: + * model_type -> String + */ +VALUE ruby_whisper_model_type(VALUE self) +{ + ruby_whisper *rw; + GetContext(self, rw); + return rb_str_new2(whisper_model_type_readable(rw->context)); +} + +static bool +check_memory_view(rb_memory_view_t *memview) +{ + rb_memory_view_prepare_item_desc(memview); + + if (!memview->format) { + rb_warn("format is required"); + return false; + } + + if (memview->item_desc.length != 1) { + rb_warn("format must be exact one character"); + return false; + } + + rb_memory_view_item_component_t component = memview->item_desc.components[0]; + + if (component.offset) { + rb_warn("format has offset"); + return false; + } + if (component.repeat != 1) { + rb_warn("format repeated"); + return false; + } + switch (component.format) { + case 'f': + // accept + break; + case 'e': + if (IS_BIGENDIAN) { + rb_warn("currently format \"e\" is only supported on little-endian environment"); + return false; + } + break; + case 'g': + if (!IS_BIGENDIAN) { + rb_warn("currently format \"g\" is only supported on big-endian environment"); + return false; + } + break; + default: + rb_warn("currently only format \"f\", \"e\" on little-endian environment and \"g\" on big-endian environment are supported for MemoryView, but given: %c", component.format); + return false; + } + + if (memview->ndim != 1 && !(memview->ndim == 2 && memview->shape[1] == 1)) { + // TODO: Accept ndim == 2 with shape [n_samples, channels] and channels > 1 by averaging the samples in different channels or just taking the first channel + rb_warn("currently only 1 dimensional MemoryView is supported, but given: %zd", memview->ndim); + return false; + } + + return true; +} + +static VALUE +fill_samples(VALUE rb_args) +{ + fill_samples_args *args = (fill_samples_args *)rb_args; + + if (RB_TYPE_P(*args->src, T_ARRAY)) { + for (int i = 0; i < args->n_samples; i++) { + VALUE sample = rb_ary_entry(*args->src, i); + if (!RB_FLOAT_TYPE_P(sample)) { + sample = rb_to_float(sample); + } + args->dest[i] = RFLOAT_VALUE(sample); + } + } else { + // TODO: use rb_block_call + VALUE iter = rb_funcall(*args->src, id_to_enum, 1, rb_str_new2("each")); + for (int i = 0; i < args->n_samples; i++) { + // TODO: check if iter is exhausted and raise ArgumentError appropriately + VALUE sample = rb_funcall(iter, id_next, 0); + if (!RB_FLOAT_TYPE_P(sample)) { + sample = rb_to_float(sample); + } + args->dest[i] = RFLOAT_VALUE(sample); + } + } + + return Qnil; +} + +parsed_samples_t +parse_samples(VALUE *samples, VALUE *n_samples) +{ + bool memview_available = rb_memory_view_available_p(*samples); + struct parsed_samples_t parsed = {0}; + parsed.memview_exported = false; + const bool is_array = RB_TYPE_P(*samples, T_ARRAY); + + if (!NIL_P(*n_samples)) { + parsed.n_samples = NUM2INT(*n_samples); + if (is_array) { + if (RARRAY_LEN(*samples) < parsed.n_samples) { + rb_raise(rb_eArgError, "samples length %ld is less than n_samples %d", RARRAY_LEN(*samples), parsed.n_samples); + } + } + // Should check when samples.respond_to?(:length)? + } else { + if (is_array) { + if (RARRAY_LEN(*samples) > INT_MAX) { + rb_raise(rb_eArgError, "samples are too long"); + } + parsed.n_samples = (int)RARRAY_LEN(*samples); + } else if (memview_available) { + bool memview_got = rb_memory_view_get(*samples, &parsed.memview, RUBY_MEMORY_VIEW_FORMAT | RUBY_MEMORY_VIEW_ROW_MAJOR); + if (memview_got) { + parsed.memview_exported = check_memory_view(&parsed.memview); + if (!parsed.memview_exported) { + rb_memory_view_release(&parsed.memview); + parsed.memview = (rb_memory_view_t){0}; + } + } else { + // Sometimes MemoryView producers accept only SIMPLE flag even when they provide suitable MemoryView + parsed.memview = (rb_memory_view_t){0}; + bool simple_memview_got = rb_memory_view_get(*samples, &parsed.memview, RUBY_MEMORY_VIEW_SIMPLE); + if (simple_memview_got) { + parsed.memview_exported = check_memory_view(&parsed.memview); + if (!parsed.memview_exported) { + rb_memory_view_release(&parsed.memview); + parsed.memview = (rb_memory_view_t){0}; + } + } + } + if (parsed.memview_exported) { + ssize_t n_samples_size = parsed.memview.byte_size / parsed.memview.item_size; + if (n_samples_size > INT_MAX) { + rb_memory_view_release(&parsed.memview); + rb_raise(rb_eArgError, "samples are too long: %zd", n_samples_size); + } + parsed.n_samples = (int)n_samples_size; + } else { + rb_warn("unable to get a memory view. falls back to Ruby object"); + if (rb_respond_to(*samples, id_length)) { + parsed.n_samples = NUM2INT(rb_funcall(*samples, id_length, 0)); + } else { + rb_raise(rb_eArgError, "samples must respond to :length"); + } + } + } else if (rb_respond_to(*samples, id_length)) { + parsed.n_samples = NUM2INT(rb_funcall(*samples, id_length, 0)); + } else { + rb_raise(rb_eArgError, "samples must respond to :length or be a MemoryView of an array of float when n_samples is not given"); + } + } + + if (parsed.memview_exported) { + parsed.samples = (float *)parsed.memview.data; + } else { + parsed.samples = ALLOC_N(float, parsed.n_samples); + fill_samples_args args = { + parsed.samples, + samples, + parsed.n_samples, + }; + int state; + rb_protect(fill_samples, (VALUE)&args, &state); + if (state) { + xfree(parsed.samples); + rb_jump_tag(state); + } + } + + return parsed; +} + +VALUE +release_samples(VALUE rb_parsed_args) +{ + parsed_samples_t *parsed_args = (parsed_samples_t *)rb_parsed_args; + + if (parsed_args->memview_exported) { + rb_memory_view_release(&parsed_args->memview); + } else { + xfree(parsed_args->samples); + } + *parsed_args = (parsed_samples_t){0}; + + return Qnil; +} + +static void* +full_without_gvl(void *rb_args) +{ + full_without_gvl_args *args = (full_without_gvl_args *)rb_args; + args->result = whisper_full(args->context, *args->params, args->samples, args->n_samples); + return NULL; +} + +static void +full_ubf(void *rb_args) +{ + full_ubf_args *args = (full_ubf_args *)rb_args; + + RUBY_ATOMIC_SET(args->abort_callback_user_data->is_interrupted, 1); +} + +VALUE +full_body(VALUE rb_args) +{ + ruby_whisper_full_args *args = (ruby_whisper_full_args *)rb_args; + + ruby_whisper *rw; + ruby_whisper_params *rwp; + GetContext(*args->context, rw); + TypedData_Get_Struct(*args->params, ruby_whisper_params, &ruby_whisper_params_type, rwp); + + ruby_whisper_abort_callback_user_data abort_callback_user_data = { + 0, + NULL, + }; + prepare_transcription(rwp, args->context, 1, &abort_callback_user_data); + + struct full_without_gvl_args full_without_gvl_args = { + rw->context, + &rwp->params, + args->samples, + args->n_samples, + 0, + }; + full_ubf_args full_ubf_args = { + &abort_callback_user_data, + }; + rb_thread_call_without_gvl(full_without_gvl, (void *)&full_without_gvl_args, full_ubf, (void *)&full_ubf_args); + return INT2NUM(full_without_gvl_args.result); +} + +/* + * Run the entire model: PCM -> log mel spectrogram -> encoder -> decoder -> text + * Not thread safe for same context + * Uses the specified decoding strategy to obtain the text. + * + * call-seq: + * full(params, samples, n_samples) -> nil + * full(params, samples) -> nil + * + * The second argument +samples+ must be an array of samples, respond to :length, or be a MemoryView of an array of float. It must be 32 bit float PCM audio data. + */ +VALUE ruby_whisper_full(int argc, VALUE *argv, VALUE self) +{ + if (argc < 2 || argc > 3) { + rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 2..3)", argc); + } + + VALUE n_samples = argc == 2 ? Qnil : argv[2]; + + struct parsed_samples_t parsed = parse_samples(&argv[1], &n_samples); + ruby_whisper_full_args args = { + &self, + &argv[0], + parsed.samples, + parsed.n_samples, + }; + VALUE rb_result = rb_ensure(full_body, (VALUE)&args, release_samples, (VALUE)&parsed); + const int result = NUM2INT(rb_result); + if (0 == result) { + return self; + } else { + rb_exc_raise(rb_funcall(eError, id_new, 1, result)); + } +} + +static void* +full_parallel_without_gvl(void *rb_args) +{ + full_parallel_without_gvl_args *args = (full_parallel_without_gvl_args *)rb_args; + args->result = whisper_full_parallel(args->context, *args->params, args->samples, args->n_samples, args->n_processors); + return NULL; +} + +VALUE +full_parallel_body(VALUE rb_args) +{ + ruby_whisper_full_parallel_args *args = (ruby_whisper_full_parallel_args *)rb_args; + + ruby_whisper *rw; + ruby_whisper_params *rwp; + GetContext(*args->context, rw); + TypedData_Get_Struct(*args->params, ruby_whisper_params, &ruby_whisper_params_type, rwp); + + ruby_whisper_abort_callback_user_data abort_callback_user_data = { + 0, + NULL, + }; + prepare_transcription(rwp, args->context, args->n_processors, &abort_callback_user_data); + + struct full_parallel_without_gvl_args full_parallel_without_gvl_args = { + rw->context, + &rwp->params, + args->samples, + args->n_samples, + args->n_processors, + 0, + }; + full_ubf_args full_ubf_args = { + &abort_callback_user_data, + }; + rb_thread_call_without_gvl(full_parallel_without_gvl, (void *)&full_parallel_without_gvl_args, full_ubf, (void *)&full_ubf_args); + return INT2NUM(full_parallel_without_gvl_args.result); +} + +/* + * Split the input audio in chunks and process each chunk separately using whisper_full_with_state() + * Result is stored in the default state of the context + * Not thread safe if executed in parallel on the same context. + * It seems this approach can offer some speedup in some cases. + * However, the transcription accuracy can be worse at the beginning and end of each chunk. + * + * call-seq: + * full_parallel(params, samples) -> nil + * full_parallel(params, samples, n_samples) -> nil + * full_parallel(params, samples, n_samples, n_processors) -> nil + * full_parallel(params, samples, nil, n_processors) -> nil + */ +static VALUE +ruby_whisper_full_parallel(int argc, VALUE *argv,VALUE self) +{ + if (argc < 2 || argc > 4) { + rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 2..4)", argc); + } + + VALUE n_samples = argc == 2 ? Qnil : argv[2]; + int n_processors; + switch (argc) { + case 2: + n_processors = 1; + break; + case 3: + n_processors = 1; + break; + case 4: + n_processors = NUM2INT(argv[3]); + break; + } + struct parsed_samples_t parsed = parse_samples(&argv[1], &n_samples); + const ruby_whisper_full_parallel_args args = { + &self, + &argv[0], + parsed.samples, + parsed.n_samples, + n_processors, + }; + const VALUE rb_result = rb_ensure(full_parallel_body, (VALUE)&args, release_samples, (VALUE)&parsed); + const int result = NUM2INT(rb_result); + if (0 == result) { + return self; + } else { + rb_exc_raise(rb_funcall(eError, id_new, 1, result)); + } +} + +/* + * Number of segments. + * + * call-seq: + * full_n_segments -> Integer + */ +static VALUE +ruby_whisper_full_n_segments(VALUE self) +{ + ruby_whisper *rw; + GetContext(self, rw); + return INT2NUM(whisper_full_n_segments(rw->context)); +} + +/* + * Language ID, which can be converted to string by Whisper.lang_str and Whisper.lang_str_full. + * + * call-seq: + * full_lang_id -> Integer + */ +static VALUE +ruby_whisper_full_lang_id(VALUE self) +{ + ruby_whisper *rw; + GetContext(self, rw); + return INT2NUM(whisper_full_lang_id(rw->context)); +} + +static int ruby_whisper_full_check_segment_index(const ruby_whisper * rw, const VALUE i_segment) +{ + const int c_i_segment = NUM2INT(i_segment); + if (c_i_segment < 0 || c_i_segment >= whisper_full_n_segments(rw->context)) { + rb_raise(rb_eIndexError, "segment index %d out of range", c_i_segment); + } + return c_i_segment; +} + +/* + * Start time of a segment indexed by +segment_index+ in centiseconds (10 times milliseconds). + * + * full_get_segment_t0(3) # => 1668 (16680 ms) + * + * call-seq: + * full_get_segment_t0(segment_index) -> Integer + */ +static VALUE +ruby_whisper_full_get_segment_t0(VALUE self, VALUE i_segment) +{ + ruby_whisper *rw; + GetContext(self, rw); + const int c_i_segment = ruby_whisper_full_check_segment_index(rw, i_segment); + const int64_t t0 = whisper_full_get_segment_t0(rw->context, c_i_segment); + return LONG2NUM(t0); +} + +/* + * End time of a segment indexed by +segment_index+ in centiseconds (10 times milliseconds). + * + * full_get_segment_t1(3) # => 1668 (16680 ms) + * + * call-seq: + * full_get_segment_t1(segment_index) -> Integer + */ +static VALUE +ruby_whisper_full_get_segment_t1(VALUE self, VALUE i_segment) +{ + ruby_whisper *rw; + GetContext(self, rw); + const int c_i_segment = ruby_whisper_full_check_segment_index(rw, i_segment); + const int64_t t1 = whisper_full_get_segment_t1(rw->context, c_i_segment); + return LONG2NUM(t1); +} + +/* + * Whether the next segment indexed by +segment_index+ is predicated as a speaker turn. + * + * full_get_segment_speacker_turn_next(3) # => true + * + * call-seq: + * full_get_segment_speacker_turn_next(segment_index) -> bool + */ +static VALUE +ruby_whisper_full_get_segment_speaker_turn_next(VALUE self, VALUE i_segment) +{ + ruby_whisper *rw; + GetContext(self, rw); + const int c_i_segment = ruby_whisper_full_check_segment_index(rw, i_segment); + const bool speaker_turn_next = whisper_full_get_segment_speaker_turn_next(rw->context, c_i_segment); + return speaker_turn_next ? Qtrue : Qfalse; +} + +/* + * Text of a segment indexed by +segment_index+. + * + * full_get_segment_text(3) # => "ask not what your country can do for you, ..." + * + * call-seq: + * full_get_segment_text(segment_index) -> String + */ +static VALUE +ruby_whisper_full_get_segment_text(VALUE self, VALUE i_segment) +{ + ruby_whisper *rw; + GetContext(self, rw); + const int c_i_segment = ruby_whisper_full_check_segment_index(rw, i_segment); + const char * text = whisper_full_get_segment_text(rw->context, c_i_segment); + return rb_str_new2(text); +} + +/* + * call-seq: + * full_get_segment_no_speech_prob(segment_index) -> Float + */ +static VALUE +ruby_whisper_full_get_segment_no_speech_prob(VALUE self, VALUE i_segment) +{ + ruby_whisper *rw; + GetContext(self, rw); + const int c_i_segment = ruby_whisper_full_check_segment_index(rw, i_segment); + const float no_speech_prob = whisper_full_get_segment_no_speech_prob(rw->context, c_i_segment); + return DBL2NUM(no_speech_prob); +} + +static VALUE +ruby_whisper_full_n_vad_segments(VALUE self) +{ + ruby_whisper *rw; + GetContext(self, rw); + + return INT2NUM(whisper_full_n_vad_segments(rw->context)); +} + +static int +ruby_whisper_full_check_vad_segment_index(const ruby_whisper *rw, const VALUE i_segment) +{ + const int c_i_segment = NUM2INT(i_segment); + if (c_i_segment < 0 || c_i_segment >= whisper_full_n_vad_segments(rw->context)) { + rb_raise(rb_eIndexError, "segment index %d out of range", c_i_segment); + } + return c_i_segment; +} + +static VALUE +ruby_whisper_full_get_vad_segment_t0(VALUE self, VALUE i_segment) +{ + ruby_whisper *rw; + GetContext(self, rw); + const int c_i_segment = ruby_whisper_full_check_vad_segment_index(rw, i_segment); + + return LONG2NUM(whisper_full_get_vad_segment_t0(rw->context, c_i_segment)); +} + +static VALUE +ruby_whisper_full_get_vad_segment_t1(VALUE self, VALUE i_segment) +{ + ruby_whisper *rw; + GetContext(self, rw); + const int c_i_segment = ruby_whisper_full_check_vad_segment_index(rw, i_segment); + + return LONG2NUM(whisper_full_get_vad_segment_t1(rw->context, c_i_segment)); +} + +static VALUE +ruby_whisper_context_free(VALUE self) +{ + ruby_whisper *rw; + GetContext(self, rw); + whisper_free(rw->context); + rw->context = NULL; + + return Qnil; +} + +// High level API + +static VALUE +ruby_whisper_full_get_segment(VALUE self, VALUE i_segment) +{ + return rb_whisper_segment_s_new(self, NUM2INT(i_segment)); +} + +/* + * Yields each Whisper::Segment: + * + * whisper.transcribe("path/to/audio.wav", params) + * whisper.each_segment do |segment| + * puts segment.text + * end + * + * Returns an Enumerator if no block given: + * + * whisper.transcribe("path/to/audio.wav", params) + * enum = whisper.each_segment + * enum.to_a # => [#<Whisper::Segment>, ...] + * + * call-seq: + * each_segment {|segment| ... } + * each_segment -> Enumerator + */ +static VALUE +ruby_whisper_each_segment(VALUE self) +{ + if (!rb_block_given_p()) { + const VALUE method_name = rb_funcall(self, id___method__, 0); + return rb_funcall(self, id_to_enum, 1, method_name); + } + + ruby_whisper *rw; + GetContext(self, rw); + + const int n_segments = whisper_full_n_segments(rw->context); + for (int i = 0; i < n_segments; ++i) { + rb_yield(rb_whisper_segment_s_new(self, i)); + } + + return self; +} + +/* + * call-seq: + * model -> Whisper::Model + */ +static VALUE +ruby_whisper_get_model(VALUE self) +{ + return rb_whisper_model_s_new(self); +} + +VALUE +init_ruby_whisper_context(VALUE *mWhisper) +{ + cContext = rb_define_class_under(*mWhisper, "Context", rb_cObject); + + transcribe_option_names[0] = id_n_processors; + + rb_define_alloc_func(cContext, ruby_whisper_allocate); + rb_define_method(cContext, "initialize", ruby_whisper_initialize, -1); + + rb_define_method(cContext, "transcribe", ruby_whisper_transcribe, -1); + rb_define_method(cContext, "model_n_vocab", ruby_whisper_model_n_vocab, 0); + rb_define_method(cContext, "model_n_audio_ctx", ruby_whisper_model_n_audio_ctx, 0); + rb_define_method(cContext, "model_n_audio_state", ruby_whisper_model_n_audio_state, 0); + rb_define_method(cContext, "model_n_audio_head", ruby_whisper_model_n_audio_head, 0); + rb_define_method(cContext, "model_n_audio_layer", ruby_whisper_model_n_audio_layer, 0); + rb_define_method(cContext, "model_n_text_ctx", ruby_whisper_model_n_text_ctx, 0); + rb_define_method(cContext, "model_n_text_state", ruby_whisper_model_n_text_state, 0); + rb_define_method(cContext, "model_n_text_head", ruby_whisper_model_n_text_head, 0); + rb_define_method(cContext, "model_n_text_layer", ruby_whisper_model_n_text_layer, 0); + rb_define_method(cContext, "model_n_mels", ruby_whisper_model_n_mels, 0); + rb_define_method(cContext, "model_ftype", ruby_whisper_model_ftype, 0); + rb_define_method(cContext, "model_type", ruby_whisper_model_type, 0); + rb_define_method(cContext, "full_n_segments", ruby_whisper_full_n_segments, 0); + rb_define_method(cContext, "full_lang_id", ruby_whisper_full_lang_id, 0); + rb_define_method(cContext, "full_get_segment_t0", ruby_whisper_full_get_segment_t0, 1); + rb_define_method(cContext, "full_get_segment_t1", ruby_whisper_full_get_segment_t1, 1); + rb_define_method(cContext, "full_get_segment_speaker_turn_next", ruby_whisper_full_get_segment_speaker_turn_next, 1); + rb_define_method(cContext, "full_get_segment_text", ruby_whisper_full_get_segment_text, 1); + rb_define_method(cContext, "full_get_segment_no_speech_prob", ruby_whisper_full_get_segment_no_speech_prob, 1); + rb_define_method(cContext, "full_n_vad_segments", ruby_whisper_full_n_vad_segments, 0); + rb_define_method(cContext, "full_get_vad_segment_t0", ruby_whisper_full_get_vad_segment_t0, 1); + rb_define_method(cContext, "full_get_vad_segment_t1", ruby_whisper_full_get_vad_segment_t1, 1); + rb_define_method(cContext, "full", ruby_whisper_full, -1); + rb_define_method(cContext, "full_parallel", ruby_whisper_full_parallel, -1); + rb_define_method(cContext, "free", ruby_whisper_context_free, 0); + + // High level + rb_define_method(cContext, "full_get_segment", ruby_whisper_full_get_segment, 1); + rb_define_method(cContext, "each_segment", ruby_whisper_each_segment, 0); + + rb_define_method(cContext, "model", ruby_whisper_get_model, 0); + + return cContext; +} diff --git a/bindings/ruby/ext/ruby_whisper_context_params.c b/bindings/ruby/ext/ruby_whisper_context_params.c new file mode 100644 index 0000000..87df21d --- /dev/null +++ b/bindings/ruby/ext/ruby_whisper_context_params.c @@ -0,0 +1,163 @@ +#include "ruby_whisper.h" + +#define NUM_PARAMS 6 + +#define DEF_BOOLEAN_ATTR_METHOD(name) \ +static VALUE \ +ruby_whisper_context_params_get_ ## name(VALUE self) { \ + ruby_whisper_context_params *rwcp; \ + GetContextParams(self, rwcp); \ + return rwcp->params.name ? Qtrue : Qfalse; \ +} \ +static VALUE \ +ruby_whisper_context_params_set_ ## name(VALUE self, VALUE value) { \ + ruby_whisper_context_params *rwcp; \ + GetContextParams(self, rwcp); \ + rwcp->params.name = RTEST(value); \ + return value; \ +} + +#define DEF_INT_ATTR_METHOD(name) \ +static VALUE \ +ruby_whisper_context_params_get_ ## name(VALUE self) { \ + ruby_whisper_context_params *rwcp; \ + GetContextParams(self, rwcp); \ + return INT2NUM(rwcp->params.name); \ +} \ +static VALUE \ +ruby_whisper_context_params_set_ ## name(VALUE self, VALUE value) { \ + ruby_whisper_context_params *rwcp; \ + GetContextParams(self, rwcp); \ + rwcp->params.name = NUM2INT(value); \ + return value; \ +} + +#define DEFINE_PARAM(param_name, nth) \ + id_ ## param_name = rb_intern(#param_name); \ + param_names[nth] = id_ ## param_name; \ + rb_define_method(cContextParams, #param_name, ruby_whisper_context_params_get_ ## param_name, 0); \ + rb_define_method(cContextParams, #param_name "=", ruby_whisper_context_params_set_ ## param_name, 1); + +VALUE cContextParams; + +static ID param_names[NUM_PARAMS]; +static ID id_use_gpu; +static ID id_flash_attn; +static ID id_gpu_device; +static ID id_dtw_token_timestamps; +static ID id_dtw_aheads_preset; +static ID id_dtw_n_top; + +static size_t +ruby_whisper_context_params_memsize(const void *p) +{ + const ruby_whisper_context_params *rwcp = (ruby_whisper_context_params *)p; + if (!rwcp) { + return 0; + } + return sizeof(ruby_whisper_context_params); +} + +const rb_data_type_t ruby_whisper_context_params_type = { + "ruby_whisper_context_params", + {0, RUBY_DEFAULT_FREE, ruby_whisper_context_params_memsize,}, + 0, 0, + 0 +}; + +static VALUE +ruby_whisper_context_params_s_allocate(VALUE klass) +{ + ruby_whisper_context_params *rwcp; + return TypedData_Make_Struct(klass, ruby_whisper_context_params, &ruby_whisper_context_params_type, rwcp); +} + +DEF_BOOLEAN_ATTR_METHOD(use_gpu); +DEF_BOOLEAN_ATTR_METHOD(flash_attn); +DEF_INT_ATTR_METHOD(gpu_device); +DEF_BOOLEAN_ATTR_METHOD(dtw_token_timestamps); +DEF_INT_ATTR_METHOD(dtw_aheads_preset); + +static VALUE +ruby_whisper_context_params_get_dtw_n_top(VALUE self) { + ruby_whisper_context_params *rwcp; + GetContextParams(self, rwcp); + + int dtw_n_top = rwcp->params.dtw_n_top; + + return dtw_n_top == -1 ? Qnil : INT2NUM(dtw_n_top); +} + +static VALUE +ruby_whisper_context_params_set_dtw_n_top(VALUE self, VALUE value) { + ruby_whisper_context_params *rwcp; + GetContextParams(self, rwcp); + + rwcp->params.dtw_n_top = NIL_P(value) ? -1 : NUM2INT(value); + + return value; +} + +#define SET_PARAM_IF_SAME(param_name) \ + if (id == id_ ## param_name) { \ + ruby_whisper_context_params_set_ ## param_name(self, value); \ + continue; \ + } + +static VALUE +ruby_whisper_context_params_initialize(int argc, VALUE *argv, VALUE self) +{ + ruby_whisper_context_params *rwcp; + TypedData_Get_Struct(self, ruby_whisper_context_params, &ruby_whisper_context_params_type, rwcp); + rwcp->params = whisper_context_default_params(); + + VALUE kw_hash; + rb_scan_args_kw(RB_SCAN_ARGS_KEYWORDS, argc, argv, ":", &kw_hash); + if (NIL_P(kw_hash)) { + return Qnil; + } + + VALUE values[NUM_PARAMS] = {Qundef}; + rb_get_kwargs(kw_hash, param_names, 0, NUM_PARAMS, values); + + ID id; + VALUE value; + for (int i = 0; i < NUM_PARAMS; i++) { + id = param_names[i]; + value = values[i]; + if (value == Qundef) { + continue; + } + SET_PARAM_IF_SAME(use_gpu) + SET_PARAM_IF_SAME(flash_attn) + SET_PARAM_IF_SAME(gpu_device) + SET_PARAM_IF_SAME(dtw_token_timestamps) + SET_PARAM_IF_SAME(dtw_aheads_preset) + SET_PARAM_IF_SAME(dtw_n_top) + } + + return Qnil; +} + +#undef SET_PARAM_IF_SAME + +void +init_ruby_whisper_context_params(VALUE *cContext) +{ + cContextParams = rb_define_class_under(*cContext, "Params", rb_cObject); + + rb_define_alloc_func(cContextParams, ruby_whisper_context_params_s_allocate); + rb_define_method(cContextParams, "initialize", ruby_whisper_context_params_initialize, -1); + + DEFINE_PARAM(use_gpu, 0) + DEFINE_PARAM(flash_attn, 1) + DEFINE_PARAM(gpu_device, 2) + DEFINE_PARAM(dtw_token_timestamps, 3) + DEFINE_PARAM(dtw_aheads_preset, 4) + DEFINE_PARAM(dtw_n_top, 5) +} + +#undef DEFINE_PARAM +#undef DEF_INT_ATTR_METHOD +#undef DEF_BOOLEAN_ATTR_METHOD +#undef NUM_PARAMS diff --git a/bindings/ruby/ext/ruby_whisper_error.c b/bindings/ruby/ext/ruby_whisper_error.c new file mode 100644 index 0000000..b4dbec0 --- /dev/null +++ b/bindings/ruby/ext/ruby_whisper_error.c @@ -0,0 +1,52 @@ +#include <ruby.h> + +extern VALUE eError; + +VALUE ruby_whisper_error_initialize(VALUE self, VALUE code) +{ + const int c_code = NUM2INT(code); + const char *raw_message; + switch (c_code) { + case -2: + raw_message = "failed to compute log mel spectrogram"; + break; + case -3: + raw_message = "failed to auto-detect language"; + break; + case -4: + raw_message = "too many decoders requested"; + break; + case -5: + raw_message = "audio_ctx is larger than the maximum allowed"; + break; + case -6: + raw_message = "failed to encode"; + break; + case -7: + raw_message = "whisper_kv_cache_init() failed for self-attention cache"; + break; + case -8: + raw_message = "failed to decode"; + break; + case -9: + raw_message = "failed to decode"; + break; + default: + raw_message = "unknown error"; + break; + } + const VALUE message = rb_str_new2(raw_message); + rb_call_super(1, &message); + rb_iv_set(self, "@code", code); + + return self; +} + +void +init_ruby_whisper_error(VALUE *mWhisper) +{ + eError = rb_define_class_under(*mWhisper, "Error", rb_eStandardError); + + rb_define_attr(eError, "code", true, false); + rb_define_method(eError, "initialize", ruby_whisper_error_initialize, 1); +} diff --git a/bindings/ruby/ext/ruby_whisper_log_queue.c b/bindings/ruby/ext/ruby_whisper_log_queue.c new file mode 100644 index 0000000..6558a33 --- /dev/null +++ b/bindings/ruby/ext/ruby_whisper_log_queue.c @@ -0,0 +1,180 @@ +#include "ruby_whisper.h" + +#define LOG_QUEUE_CAPACITY 256 +#define LOG_DEFAULT_CAPACITY 1024 + +void +ruby_whisper_log_queue_initialize(ruby_whisper_log_queue *log_queue) +{ + rb_nativethread_lock_initialize(&log_queue->lock); + rb_native_cond_initialize(&log_queue->cond); + log_queue->head = 0; + log_queue->tail = 0; + log_queue->size = 0; + log_queue->is_open = true; + log_queue->logs = ALLOC_N(ruby_whisper_log, LOG_QUEUE_CAPACITY); + for (size_t i = 0; i < LOG_QUEUE_CAPACITY; i++) { + // we cannot call Ruby API like ALLOC_N because this slot may be realloced without GVL + // this doesn't be freed because log queue lives until the end of process + char *slot = malloc(sizeof(char) * LOG_QUEUE_CAPACITY); + if (!slot) { + rb_raise(rb_eRuntimeError, "Could not allocate memory for log text"); + } + ruby_whisper_log log = { + 0, + slot, + 0, + LOG_QUEUE_CAPACITY, + }; + log_queue->logs[i] = log; + } +} + +void +ruby_whisper_log_queue_open(ruby_whisper_log_queue *log_queue) +{ + rb_nativethread_lock_lock(&log_queue->lock); + + log_queue->is_open = true; + + rb_native_cond_signal(&log_queue->cond); + + rb_nativethread_lock_unlock(&log_queue->lock); +} + +void +ruby_whisper_log_queue_close(ruby_whisper_log_queue *log_queue) +{ + rb_nativethread_lock_lock(&log_queue->lock); + + log_queue->is_open = false; + rb_native_cond_broadcast(&log_queue->cond); + + rb_nativethread_lock_unlock(&log_queue->lock); +} + +static size_t +calc_enough_cap(size_t len) +{ + size_t quot = len / LOG_DEFAULT_CAPACITY; + size_t rem = len % LOG_DEFAULT_CAPACITY; + + return sizeof(char) * (rem == 0 ? quot : quot + 1) * LOG_DEFAULT_CAPACITY; +} + +void +ruby_whisper_log_queue_enqueue(ruby_whisper_log_queue *log_queue, enum ggml_log_level level, const char *text) +{ + rb_nativethread_lock_lock(&log_queue->lock); + + if (!log_queue->is_open) { + rb_nativethread_lock_unlock(&log_queue->lock); + return; + } + + size_t len = strlen(text); + ruby_whisper_log *log = &log_queue->logs[log_queue->head]; + if (len > log->capacity) { + size_t new_cap = calc_enough_cap(len); + // we cannot call Ruby API like REALLOC_N because this function is called without GVL + char *slot = realloc(log->text, new_cap); + if (!slot) { + rb_nativethread_lock_unlock(&log_queue->lock); + return; + } + log->text = slot; + log->capacity = new_cap; + } + // we cannot call Ruby API like MEMCPY because this function is called without GVL + memcpy(log->text, text, sizeof(char) * len); + log->length = len; + log->level = level; + log_queue->head = (log_queue->head + 1) % LOG_QUEUE_CAPACITY; + bool is_full = log_queue->size >= LOG_QUEUE_CAPACITY; + log_queue->size = is_full ? LOG_QUEUE_CAPACITY : log_queue->size + 1; + if (is_full) { + log_queue->tail = log_queue->head; + } + + rb_native_cond_signal(&log_queue->cond); + rb_nativethread_lock_unlock(&log_queue->lock); +} + +static void* +ruby_whisper_log_queue_wait(void *args) +{ + ruby_whisper_log_queue *log_queue = (ruby_whisper_log_queue *)args; + + rb_native_cond_wait(&log_queue->cond, &log_queue->lock); + rb_nativethread_lock_unlock(&log_queue->lock); + + return NULL; +} + +static void +ruby_whisper_log_queue_wait_ubf(void *args) +{ + ruby_whisper_log_queue *log_queue = (ruby_whisper_log_queue *)args; + + rb_native_cond_broadcast(&log_queue->cond); +} + +typedef struct { + enum ggml_log_level level; + size_t length; + char *text; +} log_snapshot; + +VALUE +ruby_whisper_log_queue_drain(ruby_whisper_log_queue *log_queue) +{ + log_snapshot logs[LOG_QUEUE_CAPACITY]; + + rb_nativethread_lock_lock(&log_queue->lock); + + while (log_queue->size == 0 && log_queue->is_open) { + rb_thread_call_without_gvl(ruby_whisper_log_queue_wait, (void *)log_queue, ruby_whisper_log_queue_wait_ubf, (void *)log_queue); + rb_nativethread_lock_lock(&log_queue->lock); + } + + if (log_queue->size == 0 && !log_queue->is_open) { + rb_native_cond_broadcast(&log_queue->cond); + rb_nativethread_lock_unlock(&log_queue->lock); + return Qnil; + } + + size_t size = log_queue->size; + ruby_whisper_log *log; + size_t i; + for (i = 0; i < size; i++) { + log = &log_queue->logs[(log_queue->tail + i) % LOG_QUEUE_CAPACITY]; + logs[i].level = log->level; + logs[i].length = log->length; + char *text = malloc(log->length); + if (!text) { + logs[i].text = NULL; + continue; + } + logs[i].text = text; + memcpy(logs[i].text, log->text, log->length); + } + log_queue->size = 0; + log_queue->tail = log_queue->head; + + rb_native_cond_signal(&log_queue->cond); + + rb_nativethread_lock_unlock(&log_queue->lock); + + VALUE rb_logs = rb_ary_new2(size); + VALUE rb_text; + for (i = 0; i < size; i++) { + if (!logs[i].text) { + continue; + } + rb_text = rb_str_new(logs[i].text, logs[i].length); + free(logs[i].text); + rb_ary_push(rb_logs, rb_ary_new3(2, INT2NUM(logs[i].level), rb_text)); + } + + return rb_logs; +} diff --git a/bindings/ruby/ext/ruby_whisper_log_settable.h b/bindings/ruby/ext/ruby_whisper_log_settable.h new file mode 100644 index 0000000..04f9877 --- /dev/null +++ b/bindings/ruby/ext/ruby_whisper_log_settable.h @@ -0,0 +1,46 @@ +#ifndef RUBY_WHISPER_LOG_SETTABLE_H +#define RUBY_WHISPER_LOG_SETTABLE_H + +#define LOG_SETTABLE_SETUP(log_queue, mod, log_set) \ + static VALUE \ + ruby_whisper_##log_queue##_s_drain_logs(VALUE self) \ + { \ + return ruby_whisper_log_queue_drain(&log_queue); \ + } \ + static void \ + ruby_whisper_##log_queue##_log_callback(enum ggml_log_level level, const char *text, void *user_data) \ + { \ + ruby_whisper_log_queue_enqueue(&log_queue, level, text); \ + } \ + static VALUE \ + ruby_whisper_##log_queue##_s_log_set(VALUE self, VALUE log_callback, VALUE user_data) \ + { \ + rb_iv_set(self, "@log_callback", log_callback); \ + rb_iv_set(self, "@log_callback_user_data", user_data); \ + if (NIL_P(log_callback)) { \ + log_set(NULL, NULL); \ + } else { \ + ruby_whisper_log_queue_open(&log_queue); \ + rb_funcall((mod), id_start_log_callback_thread, 0); \ + log_set(ruby_whisper_##log_queue##_log_callback, NULL); \ + } \ + return Qnil; \ + } \ + static void \ + ruby_whisper_##log_queue##_end_proc(VALUE args) \ + { \ + ruby_whisper_log_queue_close(&log_queue); \ + VALUE log_callback_thread = rb_ivar_get(mod, id_log_callback_thread); \ + if (!NIL_P(log_callback_thread) && RTEST(rb_funcall(log_callback_thread, id_alive_p, 0))) { \ + rb_funcall(log_callback_thread, id_join, 0); \ + } \ + } + +#define LOG_SETTABLE_INIT(log_queue, mod) \ + ruby_whisper_log_queue_initialize(&log_queue); \ + rb_define_singleton_method(mod, "drain_logs", ruby_whisper_##log_queue##_s_drain_logs, 0); \ + rb_define_singleton_method(mod, "log_set", ruby_whisper_##log_queue##_s_log_set, 2); \ + rb_set_end_proc(ruby_whisper_##log_queue##_end_proc, Qnil); \ + rb_extend_object(mod, mLogSettable); + +#endif diff --git a/bindings/ruby/ext/ruby_whisper_model.c b/bindings/ruby/ext/ruby_whisper_model.c new file mode 100644 index 0000000..0e91fb3 --- /dev/null +++ b/bindings/ruby/ext/ruby_whisper_model.c @@ -0,0 +1,231 @@ +#include "ruby_whisper.h" + +extern const rb_data_type_t ruby_whisper_type; + +extern VALUE cModel; + +static void rb_whisper_model_mark(void *p) { + ruby_whisper_model *rwm = (ruby_whisper_model *)p; + if (rwm->context) { + rb_gc_mark(rwm->context); + } +} + +static size_t +ruby_whisper_model_memsize(const void *p) +{ + const ruby_whisper_model *rwm = (const ruby_whisper_model *)p; + size_t size = sizeof(rwm); + if (!rwm) { + return 0; + } + return size; +} + +static const rb_data_type_t rb_whisper_model_type = { + "ruby_whisper_model", + {rb_whisper_model_mark, RUBY_DEFAULT_FREE, ruby_whisper_model_memsize,}, + 0, 0, + 0 +}; + +static VALUE ruby_whisper_model_allocate(VALUE klass) { + ruby_whisper_model *rwm; + return TypedData_Make_Struct(klass, ruby_whisper_model, &rb_whisper_model_type, rwm); +} + +VALUE rb_whisper_model_s_new(VALUE context) { + ruby_whisper_model *rwm; + const VALUE model = ruby_whisper_model_allocate(cModel); + TypedData_Get_Struct(model, ruby_whisper_model, &rb_whisper_model_type, rwm); + rwm->context = context; + return model; +}; + +/* + * call-seq: + * n_vocab -> Integer + */ +static VALUE +ruby_whisper_model_n_vocab(VALUE self) +{ + ruby_whisper_model *rwm; + TypedData_Get_Struct(self, ruby_whisper_model, &rb_whisper_model_type, rwm); + ruby_whisper *rw; + GetContext(rwm->context, rw); + return INT2NUM(whisper_model_n_vocab(rw->context)); +} + +/* + * call-seq: + * n_audio_ctx -> Integer + */ +static VALUE +ruby_whisper_model_n_audio_ctx(VALUE self) +{ + ruby_whisper_model *rwm; + TypedData_Get_Struct(self, ruby_whisper_model, &rb_whisper_model_type, rwm); + ruby_whisper *rw; + GetContext(rwm->context, rw); + return INT2NUM(whisper_model_n_audio_ctx(rw->context)); +} + +/* + * call-seq: + * n_audio_state -> Integer + */ +static VALUE +ruby_whisper_model_n_audio_state(VALUE self) +{ + ruby_whisper_model *rwm; + TypedData_Get_Struct(self, ruby_whisper_model, &rb_whisper_model_type, rwm); + ruby_whisper *rw; + GetContext(rwm->context, rw); + return INT2NUM(whisper_model_n_audio_state(rw->context)); +} + +/* + * call-seq: + * n_audio_head -> Integer + */ +static VALUE +ruby_whisper_model_n_audio_head(VALUE self) +{ + ruby_whisper_model *rwm; + TypedData_Get_Struct(self, ruby_whisper_model, &rb_whisper_model_type, rwm); + ruby_whisper *rw; + GetContext(rwm->context, rw); + return INT2NUM(whisper_model_n_audio_head(rw->context)); +} + +/* + * call-seq: + * n_audio_layer -> Integer + */ +static VALUE +ruby_whisper_model_n_audio_layer(VALUE self) +{ + ruby_whisper_model *rwm; + TypedData_Get_Struct(self, ruby_whisper_model, &rb_whisper_model_type, rwm); + ruby_whisper *rw; + GetContext(rwm->context, rw); + return INT2NUM(whisper_model_n_audio_layer(rw->context)); +} + +/* + * call-seq: + * n_text_ctx -> Integer + */ +static VALUE +ruby_whisper_model_n_text_ctx(VALUE self) +{ + ruby_whisper_model *rwm; + TypedData_Get_Struct(self, ruby_whisper_model, &rb_whisper_model_type, rwm); + ruby_whisper *rw; + GetContext(rwm->context, rw); + return INT2NUM(whisper_model_n_text_ctx(rw->context)); +} + +/* + * call-seq: + * n_text_state -> Integer + */ +static VALUE +ruby_whisper_model_n_text_state(VALUE self) +{ + ruby_whisper_model *rwm; + TypedData_Get_Struct(self, ruby_whisper_model, &rb_whisper_model_type, rwm); + ruby_whisper *rw; + GetContext(rwm->context, rw); + return INT2NUM(whisper_model_n_text_state(rw->context)); +} + +/* + * call-seq: + * n_text_head -> Integer + */ +static VALUE +ruby_whisper_model_n_text_head(VALUE self) +{ + ruby_whisper_model *rwm; + TypedData_Get_Struct(self, ruby_whisper_model, &rb_whisper_model_type, rwm); + ruby_whisper *rw; + GetContext(rwm->context, rw); + return INT2NUM(whisper_model_n_text_head(rw->context)); +} + +/* + * call-seq: + * n_text_layer -> Integer + */ +static VALUE +ruby_whisper_model_n_text_layer(VALUE self) +{ + ruby_whisper_model *rwm; + TypedData_Get_Struct(self, ruby_whisper_model, &rb_whisper_model_type, rwm); + ruby_whisper *rw; + GetContext(rwm->context, rw); + return INT2NUM(whisper_model_n_text_layer(rw->context)); +} + +/* + * call-seq: + * n_mels -> Integer + */ +static VALUE +ruby_whisper_model_n_mels(VALUE self) +{ + ruby_whisper_model *rwm; + TypedData_Get_Struct(self, ruby_whisper_model, &rb_whisper_model_type, rwm); + ruby_whisper *rw; + GetContext(rwm->context, rw); + return INT2NUM(whisper_model_n_mels(rw->context)); +} + +/* + * call-seq: + * ftype -> Integer + */ +static VALUE +ruby_whisper_model_ftype(VALUE self) +{ + ruby_whisper_model *rwm; + TypedData_Get_Struct(self, ruby_whisper_model, &rb_whisper_model_type, rwm); + ruby_whisper *rw; + GetContext(rwm->context, rw); + return INT2NUM(whisper_model_ftype(rw->context)); +} + +/* + * call-seq: + * type -> String + */ +static VALUE +ruby_whisper_model_type(VALUE self) +{ + ruby_whisper_model *rwm; + TypedData_Get_Struct(self, ruby_whisper_model, &rb_whisper_model_type, rwm); + ruby_whisper *rw; + GetContext(rwm->context, rw); + return rb_str_new2(whisper_model_type_readable(rw->context)); +} + +void +init_ruby_whisper_model(VALUE *mWhisper) +{ + cModel = rb_define_class_under(*mWhisper, "Model", rb_cObject); + + rb_define_alloc_func(cModel, ruby_whisper_model_allocate); + rb_define_method(cModel, "n_vocab", ruby_whisper_model_n_vocab, 0); + rb_define_method(cModel, "n_audio_ctx", ruby_whisper_model_n_audio_ctx, 0); + rb_define_method(cModel, "n_audio_state", ruby_whisper_model_n_audio_state, 0); + rb_define_method(cModel, "n_audio_head", ruby_whisper_model_n_audio_head, 0); + rb_define_method(cModel, "n_audio_layer", ruby_whisper_model_n_audio_layer, 0); + rb_define_method(cModel, "n_text_ctx", ruby_whisper_model_n_text_ctx, 0); + rb_define_method(cModel, "n_text_state", ruby_whisper_model_n_text_state, 0); + rb_define_method(cModel, "n_text_head", ruby_whisper_model_n_text_head, 0); + rb_define_method(cModel, "n_text_layer", ruby_whisper_model_n_text_layer, 0); + rb_define_method(cModel, "n_mels", ruby_whisper_model_n_mels, 0); + rb_define_method(cModel, "ftype", ruby_whisper_model_ftype, 0); + rb_define_method(cModel, "type", ruby_whisper_model_type, 0); +} diff --git a/bindings/ruby/ext/ruby_whisper_parakeet.c b/bindings/ruby/ext/ruby_whisper_parakeet.c new file mode 100644 index 0000000..d693694 --- /dev/null +++ b/bindings/ruby/ext/ruby_whisper_parakeet.c @@ -0,0 +1,49 @@ +#include "ruby_whisper.h" +#include <stdio.h> +#include <unistd.h> + +extern VALUE mParakeet; +extern VALUE mLogSettable; +extern VALUE cParakeetContext; +extern VALUE cParakeetSegment; +extern VALUE mOutputContext; +extern VALUE mOutputSegment; + +extern void init_ruby_whisper_parakeet_params(VALUE *mParakeet); +extern void init_ruby_whisper_parakeet_token(VALUE *mParakeet); +extern void init_ruby_whisper_parakeet_segment(VALUE *mParakeet); +extern VALUE init_ruby_whisper_parakeet_context(VALUE *mParakeet); +extern void init_ruby_whisper_parakeet_context_params(VALUE *cParakeetContext); +extern void init_ruby_whisper_parakeet_model(VALUE *mParakeet); + +static ruby_whisper_log_queue parakeet_log_queue; + +LOG_SETTABLE_SETUP(parakeet_log_queue, mParakeet, parakeet_log_set) + +static VALUE +ruby_whisper_parakeet_s_system_info_str(VALUE self) +{ + return rb_str_new2(parakeet_print_system_info()); +} + +void +init_ruby_whisper_parakeet(VALUE *mWhisper) +{ + mParakeet = rb_define_module_under(*mWhisper, "Parakeet"); + + rb_define_const(mParakeet, "VERSION", rb_str_new2(parakeet_version())); + + LOG_SETTABLE_INIT(parakeet_log_queue, mParakeet) + + rb_define_singleton_method(mParakeet, "system_info_str", ruby_whisper_parakeet_s_system_info_str, 0); + + init_ruby_whisper_parakeet_params(&mParakeet); + init_ruby_whisper_parakeet_token(&mParakeet); + init_ruby_whisper_parakeet_segment(&mParakeet); + cParakeetContext = init_ruby_whisper_parakeet_context(&mParakeet); + init_ruby_whisper_parakeet_context_params(&cParakeetContext); + init_ruby_whisper_parakeet_model(&mParakeet); + + rb_include_module(cParakeetContext, mOutputContext); + rb_include_module(cParakeetSegment, mOutputSegment); +} diff --git a/bindings/ruby/ext/ruby_whisper_parakeet_context.c b/bindings/ruby/ext/ruby_whisper_parakeet_context.c new file mode 100644 index 0000000..8ac551d --- /dev/null +++ b/bindings/ruby/ext/ruby_whisper_parakeet_context.c @@ -0,0 +1,316 @@ +#include "ruby_whisper.h" + +#define ITERATE_SEGMENT_ATTRS(ITERATOR) \ + ITERATOR(get_segment_t0, LONG) \ + ITERATOR(get_segment_t1, LONG) \ + ITERATOR(get_segment_text, STRING) \ + ITERATOR(n_tokens, INT) + +#define ITERATE_TOKEN_ATTRS(ITERATOR) \ + ITERATOR(get_token_text, STRING) \ + ITERATOR(get_token_id, INT) \ + ITERATOR(get_token_p, FLOAT) + +#define VAL_FROM_LONG(v) LONG2NUM(v) +#define VAL_FROM_STRING(v) rb_utf8_str_new_cstr(v) +#define VAL_FROM_INT(v) INT2NUM(v) +#define VAL_FROM_FLOAT(v) DBL2NUM(v) +#define READER(type) VAL_FROM_##type + +extern ID id_to_s; +extern ID id___method__; +extern ID id_to_enum; +extern ID id_new; + +extern VALUE cParakeetContext; +extern VALUE eError; + +extern VALUE ruby_whisper_normalize_model_path(VALUE model_path); +extern VALUE ruby_whisper_parakeet_transcribe(VALUE self, VALUE audio_path, VALUE params); +extern VALUE ruby_whisper_parakeet_segment_init(VALUE context, int index); +extern parsed_samples_t parse_samples(VALUE *samples, VALUE *n_samples); +extern VALUE release_samples(VALUE rb_parsed_args); +extern void ruby_whisper_parakeet_prepare_transcription(ruby_whisper_parakeet_params *rwpp, VALUE *context, ruby_whisper_abort_callback_user_data *abort_callback_user_data); +extern rb_data_type_t ruby_whisper_parakeet_params_type; +extern rb_data_type_t ruby_whisper_parakeet_context_params_type; +extern VALUE ruby_whisper_parakeet_token_s_from_token_data(struct parakeet_context *context, const parakeet_token_data *token_data); +extern VALUE ruby_whisper_parakeet_model_s_new(VALUE context); + +static void +ruby_whisper_parakeet_context_free(void *p) +{ + ruby_whisper_parakeet_context *rwpc = (ruby_whisper_parakeet_context *)p; + if (rwpc->context) { + parakeet_free(rwpc->context); + rwpc->context = NULL; + } + xfree(rwpc); +} + +static size_t +ruby_whisper_parakeet_context_memsize(const void *p) +{ + ruby_whisper_parakeet_context *rwpc = (ruby_whisper_parakeet_context *)p; + if (!rwpc) { + return 0; + } + size_t size = sizeof(*rwpc); + return size; +} + +const rb_data_type_t ruby_whisper_parakeet_context_type = { + "ruby_whisper_parakeet_context", + {0, ruby_whisper_parakeet_context_free, ruby_whisper_parakeet_context_memsize,}, + 0, 0, + 0 +}; + +static VALUE +ruby_whisper_parakeet_context_allocate(VALUE klass) +{ + ruby_whisper_parakeet_context *rwpc; + + VALUE obj = TypedData_Make_Struct(klass, ruby_whisper_parakeet_context, &ruby_whisper_parakeet_context_type, rwpc); + rwpc->context = NULL; + + return obj; +} + +typedef struct { + struct parakeet_context **context; + char *model_path; + struct parakeet_context_params params; +} ruby_whisper_parakeet_context_init_args; + +static void* +ruby_whisper_parakeet_context_init_without_gvl(void *args) +{ + ruby_whisper_parakeet_context_init_args *init_args = (ruby_whisper_parakeet_context_init_args *)args; + *init_args->context = parakeet_init_from_file_with_params(init_args->model_path, init_args->params); + return NULL; +} + +static VALUE +ruby_whisper_parakeet_context_initialize(int argc, VALUE *argv, VALUE self) +{ + ruby_whisper_parakeet_context *rwpc; + VALUE model_path; + VALUE context_params; + struct parakeet_context_params params; + + rb_scan_args(argc, argv, "11", &model_path, &context_params); + TypedData_Get_Struct(self, ruby_whisper_parakeet_context, &ruby_whisper_parakeet_context_type, rwpc); + + model_path = ruby_whisper_normalize_model_path(model_path); + if (!rb_respond_to(model_path, id_to_s)) { + rb_raise(rb_eRuntimeError, "Expected file path to model to initialize Parakeet::Context"); + } + if (NIL_P(context_params)) { + params = parakeet_context_default_params(); + } else { + ruby_whisper_parakeet_context_params *rwpcp; + GetParakeetContextParams(context_params, rwpcp); + params = rwpcp->params; + } + ruby_whisper_parakeet_context_init_args init_args = { + &rwpc->context, + StringValueCStr(model_path), + params, + }; + rb_thread_call_without_gvl(ruby_whisper_parakeet_context_init_without_gvl, (void *)&init_args, NULL, NULL); + if (rwpc->context == NULL) { + rb_raise(rb_eRuntimeError, "Failed to load model"); + } + + return Qnil; +} + +static VALUE +ruby_whisper_parakeet_context_full_n_segments(VALUE self) +{ + ruby_whisper_parakeet_context *rwpc; + GetParakeetContext(self, rwpc); + + return INT2NUM(parakeet_full_n_segments(rwpc->context)); +} + +#define DEF_SEGMENT_ATTR(name, type) \ + static VALUE \ + ruby_whisper_parakeet_context_full_##name(VALUE self, VALUE i_segment) \ + { \ + ruby_whisper_parakeet_context *rwpc; \ + GetParakeetContext(self, rwpc); \ + return READER(type)(parakeet_full_##name(rwpc->context, NUM2INT(i_segment))); \ + } + +ITERATE_SEGMENT_ATTRS(DEF_SEGMENT_ATTR) + +#define DEF_TOKEN_ATTR(name, type) \ + static VALUE \ + ruby_whisper_parakeet_context_full_##name(VALUE self, VALUE i_segment, VALUE i_token) \ + { \ + ruby_whisper_parakeet_context *rwpc; \ + GetParakeetContext(self, rwpc); \ + return READER(type)(parakeet_full_##name(rwpc->context, NUM2INT(i_segment), NUM2INT(i_token))); \ + } + +ITERATE_TOKEN_ATTRS(DEF_TOKEN_ATTR) + +static VALUE +ruby_whisper_parakeet_context_full_get_token_data(VALUE self, VALUE i_segment, VALUE i_token) +{ + ruby_whisper_parakeet_context *rwpc; + GetParakeetContext(self, rwpc); + parakeet_token_data token_data = parakeet_full_get_token_data(rwpc->context, NUM2INT(i_segment), NUM2INT(i_token)); + + return ruby_whisper_parakeet_token_s_from_token_data(rwpc->context, &token_data); +} + +static VALUE +ruby_whisper_parakeet_context_each_segment(VALUE self) +{ + if (!rb_block_given_p()) { + const VALUE method_name = rb_funcall(self, id___method__, 0); + return rb_funcall(self, id_to_enum, 1, method_name); + } + + ruby_whisper_parakeet_context *rwpc; + GetParakeetContext(self, rwpc); + + const int n_segments = parakeet_full_n_segments(rwpc->context); + for (int i = 0; i < n_segments; ++i) { + rb_yield(ruby_whisper_parakeet_segment_init(self, i)); + } + + return self; +} + +typedef struct { + struct parakeet_context *context; + struct parakeet_full_params *params; + float *samples; + int n_samples; + int result; +} parakeet_full_without_gvl_args; + +static void* +parakeet_full_without_gvl(void *rb_args) +{ + parakeet_full_without_gvl_args *args = (parakeet_full_without_gvl_args *)rb_args; + args->result = parakeet_full(args->context, *args->params, args->samples, args->n_samples); + + return NULL; +} + +typedef struct { + ruby_whisper_abort_callback_user_data *abort_callback_user_data; +} parakeet_full_ubf_args; + +static void +parakeet_full_ubf(void *rb_args) +{ + parakeet_full_ubf_args *args = (parakeet_full_ubf_args *)rb_args; + + RUBY_ATOMIC_SET(args->abort_callback_user_data->is_interrupted, 1); +} + +VALUE +ruby_whisper_parakeet_context_full_body(VALUE rb_args) +{ + ruby_whisper_full_args *args = (ruby_whisper_full_args *)rb_args; + ruby_whisper_parakeet_context *rwpc; + GetParakeetContext(*args->context, rwpc); + ruby_whisper_parakeet_params *rwpp; + GetParakeetParams(*args->params, rwpp); + + ruby_whisper_abort_callback_user_data abort_callback_user_data = { + 0, + NULL, + }; + ruby_whisper_parakeet_prepare_transcription(rwpp, args->context, &abort_callback_user_data); + + parakeet_full_without_gvl_args full_without_gvl_args = { + rwpc->context, + &rwpp->params, + args->samples, + args->n_samples, + 0 + }; + parakeet_full_ubf_args full_ubf_args = { + &abort_callback_user_data, + }; + rb_thread_call_without_gvl(parakeet_full_without_gvl, (void *)&full_without_gvl_args, parakeet_full_ubf, (void *)&full_ubf_args); + + return INT2NUM(full_without_gvl_args.result); +} + +static VALUE +ruby_whisper_parakeet_context_full(int argc, VALUE *argv, VALUE self) +{ + if (argc < 2 || argc > 3) { + rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 2..3)", argc); + } + + VALUE n_samples = argc == 2 ? Qnil : argv[2]; + + struct parsed_samples_t parsed = parse_samples(&argv[1], &n_samples); + ruby_whisper_full_args args = { + &self, + &argv[0], + parsed.samples, + parsed.n_samples, + }; + VALUE rb_result = rb_ensure(ruby_whisper_parakeet_context_full_body, (VALUE)&args, release_samples, (VALUE)&parsed); + const int result = NUM2INT(rb_result); + if (result == 0) { + return self; + } else { + rb_exc_raise(rb_funcall(eError, id_new, 1, rb_result)); + } +} + +static VALUE +ruby_whisper_parakeet_free(VALUE self) +{ + ruby_whisper_parakeet_context *rwpc; + GetParakeetContext(self, rwpc); + parakeet_free(rwpc->context); + rwpc->context = NULL; + + return Qnil; +} + +static VALUE +ruby_whisper_parakeet_context_get_model(VALUE self) +{ + return ruby_whisper_parakeet_model_s_new(self); +} + +VALUE +init_ruby_whisper_parakeet_context(VALUE *mParakeet) +{ + cParakeetContext = rb_define_class_under(*mParakeet, "Context", rb_cObject); + + rb_define_alloc_func(cParakeetContext, ruby_whisper_parakeet_context_allocate); + + rb_define_method(cParakeetContext, "initialize", ruby_whisper_parakeet_context_initialize, -1); + rb_define_method(cParakeetContext, "transcribe", ruby_whisper_parakeet_transcribe, 2); + rb_define_method(cParakeetContext, "full_n_segments", ruby_whisper_parakeet_context_full_n_segments, 0); + rb_define_method(cParakeetContext, "full_get_token_data", ruby_whisper_parakeet_context_full_get_token_data, 2); + rb_define_method(cParakeetContext, "model", ruby_whisper_parakeet_context_get_model, 0); + rb_define_method(cParakeetContext, "each_segment", ruby_whisper_parakeet_context_each_segment, 0); + rb_define_method(cParakeetContext, "full", ruby_whisper_parakeet_context_full, -1); + rb_define_method(cParakeetContext, "free", ruby_whisper_parakeet_free, 0); + +#define REGISTER_SEGMENT_ATTR(name, type) \ + rb_define_method(cParakeetContext, "full_" #name, ruby_whisper_parakeet_context_full_##name, 1); + + ITERATE_SEGMENT_ATTRS(REGISTER_SEGMENT_ATTR) + +#define REGISTER_TOKEN_ATTR(name, type) \ + rb_define_method(cParakeetContext, "full_" #name, ruby_whisper_parakeet_context_full_##name, 2); + + ITERATE_TOKEN_ATTRS(REGISTER_TOKEN_ATTR) + + return cParakeetContext; +} diff --git a/bindings/ruby/ext/ruby_whisper_parakeet_context_params.c b/bindings/ruby/ext/ruby_whisper_parakeet_context_params.c new file mode 100644 index 0000000..38bd6d5 --- /dev/null +++ b/bindings/ruby/ext/ruby_whisper_parakeet_context_params.c @@ -0,0 +1,117 @@ +#include "ruby_whisper.h" + +#define ITERATE_ATTRS(ITERATOR) \ + ITERATOR(use_gpu, BOOL) \ + ITERATOR(gpu_device, INT) + +#define VAL_FROM_BOOL(v) ((v) ? Qtrue : Qfalse) +#define VAL_TO_BOOL(v) (RTEST(v)) +#define VAL_FROM_INT(v) (INT2NUM(v)) +#define VAL_TO_INT(v) (NUM2INT(v)) +#define READER(type) VAL_FROM_##type +#define WRITER(type) VAL_TO_##type + +#define DEF_ATTR(name, type) \ + static VALUE \ + ruby_whisper_parakeet_context_params_get_##name(VALUE self) \ + { \ + ruby_whisper_parakeet_context_params *rwpcp; \ + GetParakeetContextParams(self, rwpcp); \ + return READER(type)(rwpcp->params.name); \ + } \ + static VALUE \ + ruby_whisper_parakeet_context_params_set_##name(VALUE self, VALUE val) \ + { \ + ruby_whisper_parakeet_context_params *rwpcp; \ + GetParakeetContextParams(self, rwpcp); \ + rwpcp->params.name = WRITER(type)(val); \ + return val; \ + } + +enum { +#define DEF_IDX(name, type) RUBY_WHISPER_PARAKEET_CONTEXT_PARAMS_##name, + + ITERATE_ATTRS(DEF_IDX) + RUBY_WHISPER_PARAKEET_NUM_CONTEXT_PARAMS +}; + +extern VALUE cParakeetContextParams; + +typedef VALUE (*param_writer_t)(VALUE, VALUE); + +static ID param_names[RUBY_WHISPER_PARAKEET_NUM_CONTEXT_PARAMS]; +static param_writer_t param_writers[RUBY_WHISPER_PARAKEET_NUM_CONTEXT_PARAMS]; + +static size_t +ruby_whisper_parakeet_context_params_memsize(const void *p) +{ + if (!p) { + return 0; + } + return sizeof(ruby_whisper_parakeet_context_params); +} + +const rb_data_type_t ruby_whisper_parakeet_context_params_type = { + "ruby_whisper_parakeet_context_params", + {0, RUBY_DEFAULT_FREE, ruby_whisper_parakeet_context_params_memsize,}, + 0, 0, + 0, +}; + +static VALUE +ruby_whisper_parakeet_context_params_s_allocate(VALUE klass) +{ + ruby_whisper_parakeet_context_params *rwpcp; + return TypedData_Make_Struct(klass, ruby_whisper_parakeet_context_params, &ruby_whisper_parakeet_context_params_type, rwpcp); +} + +static VALUE +ruby_whisper_parakeet_context_params_initialize(int argc, VALUE *argv, VALUE self) +{ + VALUE kw_hash; + VALUE values[RUBY_WHISPER_PARAKEET_NUM_CONTEXT_PARAMS] = {Qundef}; + VALUE value; + ruby_whisper_parakeet_context_params *rwpcp; + int i; + + TypedData_Get_Struct(self, ruby_whisper_parakeet_context_params, &ruby_whisper_parakeet_context_params_type, rwpcp); + rwpcp->params = parakeet_context_default_params(); + + rb_scan_args_kw(RB_SCAN_ARGS_KEYWORDS, argc, argv, ":", &kw_hash); + if (NIL_P(kw_hash)) { + return Qnil; + } + + rb_get_kwargs(kw_hash, param_names, 0, RUBY_WHISPER_PARAKEET_NUM_CONTEXT_PARAMS, values); + for (i = 0; i < RUBY_WHISPER_PARAKEET_NUM_CONTEXT_PARAMS; i++) { + value = values[i]; + if (value == Qundef) { + continue; + } + param_writers[i](self, value); + } + + return Qnil; +} + +ITERATE_ATTRS(DEF_ATTR) + +void +init_ruby_whisper_parakeet_context_params(VALUE *cParakeetContext) +{ + cParakeetContextParams = rb_define_class_under(*cParakeetContext, "Params", rb_cObject); + + rb_define_alloc_func(cParakeetContextParams, ruby_whisper_parakeet_context_params_s_allocate); + + rb_define_method(cParakeetContextParams, "initialize", ruby_whisper_parakeet_context_params_initialize, -1); + + int i = 0; +#define REGISTER_ATTR(name, type) \ + param_names[i] = rb_intern(#name); \ + param_writers[i] = ruby_whisper_parakeet_context_params_set_##name; \ + rb_define_method(cParakeetContextParams, #name, ruby_whisper_parakeet_context_params_get_##name, 0); \ + rb_define_method(cParakeetContextParams, #name "=", ruby_whisper_parakeet_context_params_set_##name, 1); \ + i++; + + ITERATE_ATTRS(REGISTER_ATTR) +} diff --git a/bindings/ruby/ext/ruby_whisper_parakeet_model.c b/bindings/ruby/ext/ruby_whisper_parakeet_model.c new file mode 100644 index 0000000..dce43c6 --- /dev/null +++ b/bindings/ruby/ext/ruby_whisper_parakeet_model.c @@ -0,0 +1,84 @@ +#include "ruby_whisper.h" + +#define ITERATE_ATTRS(ITERATOR) \ + ITERATOR(n_vocab) \ + ITERATOR(n_audio_ctx) \ + ITERATOR(n_audio_state) \ + ITERATOR(n_audio_head) \ + ITERATOR(n_audio_layer) \ + ITERATOR(n_mels) \ + ITERATOR(ftype) + +extern rb_data_type_t ruby_whisper_parakeet_context_type; +extern VALUE cParakeetModel; + +static void +ruby_whisper_parakeet_model_mark(void *p) +{ + ruby_whisper_parakeet_model *rwpm = (ruby_whisper_parakeet_model *)p; + if (!NIL_P(rwpm->context)) { + rb_gc_mark(rwpm->context); + } +} + +static size_t +ruby_whisper_parakeet_model_memsize(const void *p) +{ + if (!p) { + return 0; + } + return sizeof(ruby_whisper_parakeet_model); +} + +static const rb_data_type_t ruby_whisper_parakeet_model_type = { + "ruby_whisper_parakeet_model", + {ruby_whisper_parakeet_model_mark, RUBY_DEFAULT_FREE, ruby_whisper_parakeet_model_memsize}, + 0, 0, + 0 +}; + +static VALUE +ruby_whisper_parakeet_model_s_allocate(VALUE klass) +{ + ruby_whisper_parakeet_model *rwpm; + VALUE model = TypedData_Make_Struct(klass, ruby_whisper_parakeet_model, &ruby_whisper_parakeet_model_type, rwpm); + rwpm->context = Qnil; + + return model; +} + +VALUE +ruby_whisper_parakeet_model_s_new(VALUE context) +{ + const VALUE model = ruby_whisper_parakeet_model_s_allocate(cParakeetModel); + ruby_whisper_parakeet_model *rwpm; + TypedData_Get_Struct(model, ruby_whisper_parakeet_model, &ruby_whisper_parakeet_model_type, rwpm); + rwpm->context = context; + return model; +} + +#define DEF_ATTR(name) \ + static VALUE \ + ruby_whisper_parakeet_model_get_##name(VALUE self) \ + { \ + ruby_whisper_parakeet_model *rwpm; \ + ruby_whisper_parakeet_context *rwpc; \ + GetParakeetModel(self, rwpm); \ + GetParakeetContext(rwpm->context, rwpc); \ + return INT2NUM(parakeet_model_##name(rwpc->context)); \ + } + +ITERATE_ATTRS(DEF_ATTR) + +void +init_ruby_whisper_parakeet_model(VALUE *mParakeet) +{ + cParakeetModel = rb_define_class_under(*mParakeet, "Model", rb_cObject); + + rb_define_alloc_func(cParakeetModel, ruby_whisper_parakeet_model_s_allocate); + +#define REGISTER_ATTR(name) \ + rb_define_method(cParakeetModel, #name, ruby_whisper_parakeet_model_get_##name, 0); + + ITERATE_ATTRS(REGISTER_ATTR) +} diff --git a/bindings/ruby/ext/ruby_whisper_parakeet_params.c b/bindings/ruby/ext/ruby_whisper_parakeet_params.c new file mode 100644 index 0000000..076e2a0 --- /dev/null +++ b/bindings/ruby/ext/ruby_whisper_parakeet_params.c @@ -0,0 +1,548 @@ +#include "ruby_whisper.h" + +#define ITERATE_PARAMS(ITERATOR) \ + ITERATOR(n_threads, INT) \ + ITERATOR(offset_ms, INT) \ + ITERATOR(duration_ms, INT) \ + ITERATOR(no_context, BOOL) \ + ITERATOR(audio_ctx, INT) + +#define ITERATE_NORMAL_CALLBACK_NAMES(ITERATOR, DATA) \ + ITERATOR(new_segment, DATA) \ + ITERATOR(new_token, DATA) \ + ITERATOR(progress, DATA) \ + ITERATOR(encoder_begin, DATA) + +#define ITERATE_NORMAL_CALLBACK_PARAM(name, ITERATOR) ITERATOR(name##_callback) +#define ITERATE_NORMAL_CALLBACK_PARAMS(ITERATOR) \ + ITERATE_NORMAL_CALLBACK_NAMES(ITERATE_NORMAL_CALLBACK_PARAM, ITERATOR) + +#define ITERATE_CALLBACK_PARAMS(ITERATOR) \ + ITERATE_NORMAL_CALLBACK_PARAMS(ITERATOR) \ + ITERATOR(abort_callback) + +enum { +#define DEF_IDX(name, type) RUBY_WHISPER_PARAKEET_PARAM_##name, +#define DEF_IDX_CALLBACK(name) RUBY_WHISPER_PARAKEET_PARAM_##name, +#define DEF_IDX_USER_DATA(name) RUBY_WHISPER_PARAKEET_PARAM_##name##_user_data, + ITERATE_PARAMS(DEF_IDX) + ITERATE_CALLBACK_PARAMS(DEF_IDX_CALLBACK) + ITERATE_CALLBACK_PARAMS(DEF_IDX_USER_DATA) + + RUBY_WHISPER_PARAKEET_NUM_PARAMS +}; + +#define VAL_TO_INT(v) (NUM2INT(v)) +#define VAL_FROM_INT(v) (INT2NUM(v)) +#define VAL_TO_BOOL(v) (RTEST(v)) +#define VAL_FROM_BOOL(v) (v ? Qtrue : Qfalse) + +extern VALUE cParakeetParams; +extern ID id_call; + +extern void ruby_whisper_callback_container_mark(ruby_whisper_callback_container *rwc); +extern ruby_whisper_callback_container* ruby_whisper_callback_container_allocate(void); +extern bool ruby_whisper_callback_container_is_present(const ruby_whisper_callback_container *container); +extern VALUE ruby_whisper_parakeet_segment_init(VALUE context, int index); +extern VALUE ruby_whisper_parakeet_token_s_from_token_data(struct parakeet_context *context, const parakeet_token_data *token_data); + +static ID param_names[RUBY_WHISPER_PARAKEET_NUM_PARAMS]; +typedef VALUE (*param_writer_t)(VALUE, VALUE); +static param_writer_t param_writers[RUBY_WHISPER_PARAKEET_NUM_PARAMS]; + +typedef struct { + const ruby_whisper_callback_container *container; + struct parakeet_state *state; + int n_new; +} call_parakeet_new_segment_callbacks_args; + +static void* +call_parakeet_new_segment_callbacks(void *v_args) +{ + call_parakeet_new_segment_callbacks_args *args = (call_parakeet_new_segment_callbacks_args *)v_args; + const ruby_whisper_callback_container *container = args->container; + + if (!NIL_P(container->callback)) { + rb_funcall(container->callback, id_call, 4, *container->context, Qnil, INT2NUM(args->n_new), container->user_data); + } + if (NIL_P(container->callbacks)) { + return NULL; + } + const long n_callbacks = RARRAY_LEN(container->callbacks); + if (n_callbacks == 0) { + return NULL; + } + const int n_segments = parakeet_full_n_segments_from_state(args->state); + for (int i = args->n_new; i > 0; i--) { + int i_segment = n_segments - i; + VALUE segment = ruby_whisper_parakeet_segment_init(*container->context, i_segment); + for (int j = 0; j < n_callbacks; j++) { + VALUE cb = rb_ary_entry(container->callbacks, j); + rb_funcall(cb, id_call, 1, segment); + } + } + + return NULL; +} + +static void +ruby_whisper_parakeet_new_segment_callback(struct parakeet_context *context, struct parakeet_state *state, int n_new, void *user_data) +{ + const ruby_whisper_callback_container *container = (ruby_whisper_callback_container *)user_data; + if (!ruby_whisper_callback_container_is_present(container)) { + return; + } + + call_parakeet_new_segment_callbacks_args args = { + container, + state, + n_new, + }; + rb_thread_call_with_gvl(call_parakeet_new_segment_callbacks, (void *)&args); +} + +typedef struct { + const ruby_whisper_callback_container *container; + struct parakeet_context *context; + struct parakeet_state *state; + const parakeet_token_data *token_data; +} call_parakeet_new_token_callbacks_args; + +static void* +call_parakeet_new_token_callbacks(void *v_args) +{ + call_parakeet_new_token_callbacks_args *args = (call_parakeet_new_token_callbacks_args *)v_args; + VALUE token = Qnil; + const ruby_whisper_callback_container *container = args->container; + + if (!NIL_P(container->callback)) { + token = ruby_whisper_parakeet_token_s_from_token_data(args->context, args->token_data); + rb_funcall(container->callback, id_call, 4, *container->context, Qnil, token, container->user_data); + } + if (NIL_P(container->callbacks)) { + return NULL; + } + const long n_callbacks = RARRAY_LEN(container->callbacks); + if (n_callbacks == 0) { + return NULL; + } + if (NIL_P(token)) { + token = ruby_whisper_parakeet_token_s_from_token_data(args->context, args->token_data); + } + for (int i = 0; i < n_callbacks; i++) { + VALUE cb = rb_ary_entry(container->callbacks, i); + rb_funcall(cb, id_call, 1, token); + } + + return NULL; +} + +static void +ruby_whisper_parakeet_new_token_callback(struct parakeet_context *context, struct parakeet_state *state, const parakeet_token_data *token_data, void *user_data) +{ + const ruby_whisper_callback_container *container = (ruby_whisper_callback_container *)user_data; + if (!ruby_whisper_callback_container_is_present(container)) { + return; + } + + call_parakeet_new_token_callbacks_args args = { + container, + context, + state, + token_data, + }; + rb_thread_call_with_gvl(call_parakeet_new_token_callbacks, (void *)&args); +} + +typedef struct { + const ruby_whisper_callback_container *container; + struct parakeet_state *state; + int progress; +} call_parakeet_progress_callbacks_args; + +static void* +call_parakeet_progress_callback(void *v_args) +{ + call_parakeet_progress_callbacks_args *args = (call_parakeet_progress_callbacks_args *)v_args; + const ruby_whisper_callback_container *container = args->container; + + if (!NIL_P(container->callback)) { + rb_funcall(container->callback, id_call, 4, *container->context, Qnil, INT2NUM(args->progress), container->user_data); + } + if (NIL_P(container->callbacks)) { + return NULL; + } + const long n_callbacks = RARRAY_LEN(container->callbacks); + if (n_callbacks == 0) { + return NULL; + } + for (long i = 0; i < n_callbacks; i++) { + VALUE cb = rb_ary_entry(container->callbacks, i); + rb_funcall(cb, id_call, 1, INT2NUM(args->progress)); + } + + return NULL; +} + +static void +ruby_whisper_parakeet_progress_callback(struct parakeet_context *context, struct parakeet_state *state, int progress, void *user_data) +{ + const ruby_whisper_callback_container *container = (ruby_whisper_callback_container *)user_data; + if (!ruby_whisper_callback_container_is_present(container)) { + return; + } + + call_parakeet_progress_callbacks_args args = { + container, + state, + progress, + }; + rb_thread_call_with_gvl(call_parakeet_progress_callback, (void *)&args); +} + +typedef struct { + const ruby_whisper_callback_container *container; + struct parakeet_state *state; + bool is_continued; +} call_parakeet_encoder_begin_callbacks_args; + +static void* +call_parakeet_encoder_begin_callbacks(void *v_args) +{ + call_parakeet_encoder_begin_callbacks_args *args = (call_parakeet_encoder_begin_callbacks_args *)v_args; + const ruby_whisper_callback_container *container = args->container; + VALUE result = Qnil; + + if (!NIL_P(container->callback)) { + result = rb_funcall(container->callback, id_call, 3, *container->context, Qnil, container->user_data); + if (result == Qfalse) { + args->is_continued = false; + return NULL; + } + } + if (NIL_P(container->callbacks)) { + return NULL; + } + const long n_callbacks = RARRAY_LEN(container->callbacks); + if (n_callbacks == 0) { + return NULL; + } + for (long i = 0; i < n_callbacks; i++) { + VALUE cb = rb_ary_entry(container->callbacks, i); + result = rb_funcall(cb, id_call, 0); + if (result == Qfalse) { + args->is_continued = false; + return NULL; + } + } + + return NULL; +} + +static bool +ruby_whisper_parakeet_encoder_begin_callback(struct parakeet_context *context, struct parakeet_state *state, void *user_data) +{ + const ruby_whisper_callback_container *container = (ruby_whisper_callback_container *)user_data; + if (!ruby_whisper_callback_container_is_present(container)) { + return true; + } + + call_parakeet_encoder_begin_callbacks_args args = { + container, + state, + true, + }; + rb_thread_call_with_gvl(call_parakeet_encoder_begin_callbacks, (void *)&args); + + return args.is_continued; +} + +typedef struct { + const ruby_whisper_callback_container *container; + bool is_interrupted; +} call_parakeet_abort_callbacks_args; + +static void* +call_parakeet_abort_callbacks(void *v_args) +{ + call_parakeet_abort_callbacks_args *args = (call_parakeet_abort_callbacks_args *)v_args; + const ruby_whisper_callback_container *container = args->container; + VALUE result = Qnil; + + if (!NIL_P(container->callback)) { + result = rb_funcall(container->callback, id_call, 1, container->user_data); + if (RTEST(result)) { + args->is_interrupted = true; + return NULL; + } + } + if (NIL_P(container->callbacks)) { + return NULL; + } + const long n_callbacks = RARRAY_LEN(container->callbacks); + if (n_callbacks == 0) { + return NULL; + } + VALUE cb; + for (long i = 0; i < n_callbacks; i++) { + cb = rb_ary_entry(container->callbacks, i); + result = rb_funcall(cb, id_call, 0); + if (RTEST(result)) { + args->is_interrupted = true; + return NULL; + } + } + + return NULL; +} + +static bool +ruby_whisper_parakeet_abort_callback(void *user_data) +{ + ruby_whisper_abort_callback_user_data *data = (ruby_whisper_abort_callback_user_data *)user_data; + + int is_interrupted = RUBY_ATOMIC_LOAD(data->is_interrupted); + if (is_interrupted) { + return true; + } + + if (!(data->callback_container) || !ruby_whisper_callback_container_is_present(data->callback_container)) { + return false; + } + + call_parakeet_abort_callbacks_args args = { + data->callback_container, + false, + }; + rb_thread_call_with_gvl(call_parakeet_abort_callbacks, (void *)&args); + + return args.is_interrupted; +} + +#define CALLBACK_CONTAINER_NAME(name) name ## _container + +void +ruby_whisper_parakeet_prepare_transcription(ruby_whisper_parakeet_params *rwpp, VALUE *context, ruby_whisper_abort_callback_user_data *abort_callback_user_data) +{ +#define PARAM_NAME(name) name +#define USER_DATA_NAME(name) name##_user_data +#define REGISTER_CALLBACK(name) \ + if (ruby_whisper_callback_container_is_present(rwpp->CALLBACK_CONTAINER_NAME(name))) { \ + rwpp->CALLBACK_CONTAINER_NAME(name)->context = context; \ + rwpp->params.PARAM_NAME(name) = ruby_whisper_parakeet_##name; \ + rwpp->params.USER_DATA_NAME(name) = rwpp->CALLBACK_CONTAINER_NAME(name); \ + } + + ITERATE_NORMAL_CALLBACK_PARAMS(REGISTER_CALLBACK) + + if (ruby_whisper_callback_container_is_present(rwpp->abort_callback_container)) { + abort_callback_user_data->callback_container = rwpp->abort_callback_container; + } + rwpp->params.abort_callback = ruby_whisper_parakeet_abort_callback; + rwpp->params.abort_callback_user_data = (void *)abort_callback_user_data; +} + +static void +ruby_whisper_parakeet_params_mark(void *p) +{ + ruby_whisper_parakeet_params *rwpp = (ruby_whisper_parakeet_params *)p; + +#define MARK_CONTAINER(name) \ + if (rwpp->name##_container) { \ + ruby_whisper_callback_container_mark(rwpp->name##_container); \ + } + + ITERATE_CALLBACK_PARAMS(MARK_CONTAINER) +} + +static void +ruby_whisper_parakeet_params_free(void *p) +{ + ruby_whisper_parakeet_params *rwpp = (ruby_whisper_parakeet_params *)p; + +#define FREE_CONTAINER(name) \ + if (rwpp->name##_container) { \ + xfree(rwpp->name##_container); \ + } + + ITERATE_CALLBACK_PARAMS(FREE_CONTAINER) + + xfree(rwpp); +} + +static size_t +ruby_whisper_parakeet_params_memsize(const void *p) +{ + const struct ruby_whisper_parakeet_params *params = p; + if (!params) { + return 0; + } + return sizeof(ruby_whisper_parakeet_params); +} + +const rb_data_type_t ruby_whisper_parakeet_params_type = { + "ruby_whisper_parakeet_params", + {ruby_whisper_parakeet_params_mark, ruby_whisper_parakeet_params_free, ruby_whisper_parakeet_params_memsize,}, + 0, 0, + 0 +}; + +#define READER(type) VAL_FROM_##type +#define WRITER(type) VAL_TO_##type +#define DEF_PARAM_ATTR(name, type) \ + static VALUE \ + ruby_whisper_parakeet_params_get_##name(VALUE self) \ + { \ + ruby_whisper_parakeet_params *rwpp; \ + GetParakeetParams(self, rwpp); \ + return READER(type)(rwpp->params.name); \ + } \ + static VALUE \ + ruby_whisper_parakeet_params_set_##name(VALUE self, VALUE val) \ + { \ + ruby_whisper_parakeet_params *rwpp; \ + GetParakeetParams(self, rwpp); \ + rwpp->params.name = WRITER(type)(val); \ + return val; \ + } + +#define DEF_CALLBACK_PARAM_ATTR(name) \ + static VALUE \ + ruby_whisper_parakeet_params_get_##name(VALUE self) \ + { \ + ruby_whisper_parakeet_params *rwpp; \ + GetParakeetParams(self, rwpp); \ + return rwpp->CALLBACK_CONTAINER_NAME(name)->callback; \ + } \ + static VALUE \ + ruby_whisper_parakeet_params_set_##name(VALUE self, VALUE val) \ + { \ + ruby_whisper_parakeet_params *rwpp; \ + GetParakeetParams(self, rwpp); \ + rwpp->CALLBACK_CONTAINER_NAME(name)->callback = (val); \ + return val; \ + } + +#define DEF_USER_DATA_PARAM_ATTR(name) \ + static VALUE \ + ruby_whisper_parakeet_params_get_##name##_user_data(VALUE self) \ + { \ + ruby_whisper_parakeet_params *rwpp; \ + GetParakeetParams(self, rwpp); \ + return rwpp->CALLBACK_CONTAINER_NAME(name)->user_data; \ + } \ + static VALUE \ + ruby_whisper_parakeet_params_set_##name##_user_data(VALUE self, VALUE val) \ + { \ + ruby_whisper_parakeet_params *rwpp; \ + GetParakeetParams(self, rwpp); \ + rwpp->CALLBACK_CONTAINER_NAME(name)->user_data = val; \ + return val; \ + } + +#define DEF_HOOK(name, data) \ + static VALUE \ + ruby_whisper_parakeet_params_on_##name(VALUE self) \ + { \ + ruby_whisper_parakeet_params *rwpp; \ + GetParakeetParams(self, rwpp); \ + const VALUE blk = rb_block_proc(); \ + if (NIL_P(rwpp->name##_callback_container->callbacks)) { \ + rwpp->name##_callback_container->callbacks = rb_ary_new(); \ + } \ + rb_ary_push(rwpp->name##_callback_container->callbacks, blk); \ + return Qnil; \ + } + +ITERATE_PARAMS(DEF_PARAM_ATTR) +ITERATE_CALLBACK_PARAMS(DEF_CALLBACK_PARAM_ATTR) +ITERATE_CALLBACK_PARAMS(DEF_USER_DATA_PARAM_ATTR) +ITERATE_NORMAL_CALLBACK_NAMES(DEF_HOOK, _) + +static VALUE +ruby_whisper_parakeet_params_abort_on(VALUE self) +{ + ruby_whisper_parakeet_params *rwpp; + GetParakeetParams(self, rwpp); + const VALUE blk = rb_block_proc(); + if (NIL_P(rwpp->abort_callback_container->callbacks)) { + rwpp->abort_callback_container->callbacks = rb_ary_new(); + } + rb_ary_push(rwpp->abort_callback_container->callbacks, blk); + + return Qnil; +} + +static VALUE +ruby_whisper_parakeet_params_s_allocate(VALUE klass) +{ + ruby_whisper_parakeet_params *rwpp; + VALUE obj = TypedData_Make_Struct(klass, ruby_whisper_parakeet_params, &ruby_whisper_parakeet_params_type, rwpp); + rwpp->params = parakeet_full_default_params(PARAKEET_SAMPLING_GREEDY); + return obj; +} + +static VALUE +ruby_whisper_parakeet_params_initialize(int argc, VALUE *argv, VALUE self) +{ + VALUE kw_hash; + VALUE values[RUBY_WHISPER_PARAKEET_NUM_PARAMS] = {Qundef}; + VALUE value; + ruby_whisper_parakeet_params *rwpp; + int i; + + TypedData_Get_Struct(self, ruby_whisper_parakeet_params, &ruby_whisper_parakeet_params_type, rwpp); + +#define INIT_CONTAINER(name) rwpp->name##_container = ruby_whisper_callback_container_allocate(); + + ITERATE_CALLBACK_PARAMS(INIT_CONTAINER) + + rb_scan_args_kw(RB_SCAN_ARGS_KEYWORDS, argc, argv, ":", &kw_hash); + if (NIL_P(kw_hash)) { + return Qnil; + } + + rb_get_kwargs(kw_hash, param_names, 0, RUBY_WHISPER_PARAKEET_NUM_PARAMS, values); + + for (i = 0; i < RUBY_WHISPER_PARAKEET_NUM_PARAMS; i++) { + value = values[i]; + if (value == Qundef) { + continue; + } + param_writers[i](self, value); + } + + return Qnil; +} + +void +init_ruby_whisper_parakeet_params(VALUE *mParakeet) +{ + cParakeetParams = rb_define_class_under(*mParakeet, "Params", rb_cObject); + rb_define_alloc_func(cParakeetParams, ruby_whisper_parakeet_params_s_allocate); + + rb_define_method(cParakeetParams, "initialize", ruby_whisper_parakeet_params_initialize, -1); + + int i = 0; +#define REGISTER_PARAM(name) \ + param_names[i] = rb_intern(#name); \ + param_writers[i] = ruby_whisper_parakeet_params_set_##name; \ + rb_define_method(cParakeetParams, #name, ruby_whisper_parakeet_params_get_##name, 0); \ + rb_define_method(cParakeetParams, #name "=", ruby_whisper_parakeet_params_set_##name, 1); \ + i++; + +#define REGISTER_PARAM_ATTR(name, type) REGISTER_PARAM(name) +#define REGISTER_CALLBACK_PARAM_ATTR(name) REGISTER_PARAM(name) +#define REGISTER_USER_DATA_PARAM_ATTR(name) REGISTER_PARAM(name##_user_data) + + ITERATE_PARAMS(REGISTER_PARAM_ATTR) + ITERATE_CALLBACK_PARAMS(REGISTER_CALLBACK_PARAM_ATTR) + ITERATE_CALLBACK_PARAMS(REGISTER_USER_DATA_PARAM_ATTR) + +#define REGISTER_HOOK(name, data) \ + rb_define_method(cParakeetParams, "on_" #name, ruby_whisper_parakeet_params_on_##name, 0); + + ITERATE_NORMAL_CALLBACK_NAMES(REGISTER_HOOK, _) + + rb_define_method(cParakeetParams, "abort_on", ruby_whisper_parakeet_params_abort_on, 0); +} diff --git a/bindings/ruby/ext/ruby_whisper_parakeet_segment.c b/bindings/ruby/ext/ruby_whisper_parakeet_segment.c new file mode 100644 index 0000000..b1e81ba --- /dev/null +++ b/bindings/ruby/ext/ruby_whisper_parakeet_segment.c @@ -0,0 +1,157 @@ +#include "ruby_whisper.h" + +#define ITERATE_ATTRS(ITERATOR) \ + ITERATOR(start_time, t0, TIME) \ + ITERATOR(end_time, t1, TIME) \ + ITERATOR(text, text, STRING) + +enum { +#define DEF_IDX(name, c_name, type) RUBY_WHISPER_PARAKEET_SEGMENT_##name, + + ITERATE_ATTRS(DEF_IDX) + RUBY_WHISPER_PARAKEET_SEGMENT_NUM_ATTRS, +}; + +#define VAL_FROM_TIME(v) (LONG2NUM((v) * 10)) +#define VAL_FROM_STRING(v) (rb_str_new2(v)) +#define READER(type) VAL_FROM_##type +#define DEF_ATTR(rb_name, c_name, type) \ + static VALUE \ + ruby_whisper_parakeet_get_##rb_name(VALUE self) \ + { \ + ruby_whisper_parakeet_segment *rwps; \ + GetParakeetSegment(self, rwps); \ + ruby_whisper_parakeet_context *rwpc; \ + GetParakeetContext(rwps->context, rwpc); \ + return READER(type)(parakeet_full_get_segment_##c_name(rwpc->context, rwps->index)); \ + } + +extern ID id___method__; +extern ID id_to_enum; +extern VALUE cParakeetSegment; +extern VALUE sym_start_time; +extern VALUE sym_end_time; +extern VALUE sym_text; +extern const rb_data_type_t ruby_whisper_parakeet_context_type; +extern VALUE ruby_whisper_parakeet_token_s_from_index(struct parakeet_context *context, int i_segment, int i_token); + +static void +rb_whisper_parakeet_segment_mark(void *p) +{ + ruby_whisper_parakeet_segment *rwps = (ruby_whisper_parakeet_segment *)p; + rb_gc_mark(rwps->context); +} + +static size_t +ruby_whisper_parakeet_segment_memsize(const void *p) +{ + const ruby_whisper_parakeet_segment *rwps = (const ruby_whisper_parakeet_segment *)p; + if (!rwps) { + return 0; + } + return sizeof(*rwps); +} + +static const rb_data_type_t ruby_whisper_parakeet_segment_type = { + "ruby_whisper_parakeet_segment", + {rb_whisper_parakeet_segment_mark, RUBY_DEFAULT_FREE, ruby_whisper_parakeet_segment_memsize,}, + 0, 0, + 0 +}; + +static VALUE +ruby_whisper_parakeet_segment_s_allocate(VALUE klass) +{ + ruby_whisper_parakeet_segment *rwps; + return TypedData_Make_Struct(klass, ruby_whisper_parakeet_segment, &ruby_whisper_parakeet_segment_type, rwps); +} + +VALUE +ruby_whisper_parakeet_segment_init(VALUE context, int index) +{ + ruby_whisper_parakeet_segment *rwps; + + const VALUE segment = ruby_whisper_parakeet_segment_s_allocate(cParakeetSegment); + TypedData_Get_Struct(segment, ruby_whisper_parakeet_segment, &ruby_whisper_parakeet_segment_type, rwps); + rwps->context = context; + rwps->index = index; + + return segment; +} + +ITERATE_ATTRS(DEF_ATTR) + +static VALUE +ruby_whisper_parakeet_segment_each_token(VALUE self) +{ + if (!rb_block_given_p()) { + const VALUE method_name = rb_funcall(self, id___method__, 0); + return rb_funcall(self, id_to_enum, 1, method_name); + } + + ruby_whisper_parakeet_segment *rwps; + GetParakeetSegment(self, rwps); + ruby_whisper_parakeet_context *rwpc; + GetParakeetContext(rwps->context, rwpc); + + const int n_tokens = parakeet_full_n_tokens(rwpc->context, rwps->index); + for (int i = 0; i < n_tokens; i++) { + rb_yield(ruby_whisper_parakeet_token_s_from_index(rwpc->context, rwps->index, i)); + } + + return self; +} + +static VALUE +ruby_whisper_parakeet_segment_deconstruct_keys(VALUE self, VALUE keys) +{ + ruby_whisper_parakeet_segment *rwps; + GetParakeetSegment(self, rwps); + ruby_whisper_parakeet_context *rwpc; + GetParakeetContext(rwps->context, rwpc); + + VALUE hash = rb_hash_new(); + long n_keys; + if (NIL_P(keys)) { + keys = rb_ary_new3( + RUBY_WHISPER_PARAKEET_SEGMENT_NUM_ATTRS, + sym_start_time, + sym_end_time, + sym_text + ); + n_keys = RUBY_WHISPER_PARAKEET_SEGMENT_NUM_ATTRS; + } else { + n_keys = RARRAY_LEN(keys); + if (n_keys > RUBY_WHISPER_PARAKEET_SEGMENT_NUM_ATTRS) { + return hash; + } + } + for (int i = 0; i < n_keys; i++) { + VALUE key = rb_ary_entry(keys, i); + +#define CHECK_AND_SET_KEY(rb_name, c_name, type) \ + if (key == sym_##rb_name) { \ + rb_hash_aset(hash, key, ruby_whisper_parakeet_get_##rb_name(self)); \ + } + + ITERATE_ATTRS(CHECK_AND_SET_KEY) + } + + return hash; +} + +void +init_ruby_whisper_parakeet_segment(VALUE *mParakeet) +{ + cParakeetSegment = rb_define_class_under(*mParakeet, "Segment", rb_cObject); + + rb_define_alloc_func(cParakeetSegment, ruby_whisper_parakeet_segment_s_allocate); + +#define REGISTER_ATTR(rb_name, c_name, type) \ + rb_define_method(cParakeetSegment, #rb_name, ruby_whisper_parakeet_get_##rb_name, 0); + + ITERATE_ATTRS(REGISTER_ATTR) + + rb_define_method(cParakeetSegment, "each_token", ruby_whisper_parakeet_segment_each_token, 0); + rb_define_method(cParakeetSegment, "deconstruct_keys", ruby_whisper_parakeet_segment_deconstruct_keys, 1); +} diff --git a/bindings/ruby/ext/ruby_whisper_parakeet_token.c b/bindings/ruby/ext/ruby_whisper_parakeet_token.c new file mode 100644 index 0000000..a00b7ae --- /dev/null +++ b/bindings/ruby/ext/ruby_whisper_parakeet_token.c @@ -0,0 +1,188 @@ +#include "ruby_whisper.h" + +#define ITERATE_MEMBERS(ITERATOR) \ + ITERATOR(id, id, id, id, INT) \ + ITERATOR(duration_idx, duration_idx, duration_idx, duration_idx, INT) \ + ITERATOR(duration_value, duration_value, duration_value, duration_value, INT) \ + ITERATOR(frame_index, frame_index, frame_index, frame_index, INT) \ + ITERATOR(probability, probability, p, p, FLOAT) \ + ITERATOR(log_probability, log_probability, plog, plog, FLOAT) \ + ITERATOR(start_time, start_time, start_time, t0, TIME) \ + ITERATOR(end_time, end_time, end_time, t1, TIME) \ + ITERATOR(word_start?, word_start, word_start_p, is_word_start, BOOL) + +#define ITERATE_ATTRS(ITERATOR) \ + ITERATOR(text, text, text, text, STRING) + +enum { +#define DEF_IDX(rb_name, s_key, c_name, p_name, type) RUBY_WHISPER_PARAKEET_TOKEN_##c_name, + + ITERATE_MEMBERS(DEF_IDX) + ITERATE_ATTRS(DEF_IDX) + RUBY_WHISPER_PARAKEET_TOKEN_NUM_ATTRS, +}; + +#define VAL_FROM_INT(v) (INT2NUM(v)) +#define VAL_FROM_FLOAT(v) (DBL2NUM(v)) +#define VAL_FROM_TIME(v) (LONG2NUM(v * 10)) +#define VAL_FROM_BOOL(v) ((v) ? Qtrue : Qfalse) +#define VAL_FROM_STRING(v) (rb_str_new2(v)) + +#define READER(type) VAL_FROM_##type +#define MEMBER_NAME(name) name +#define DEF_MEMBER_ATTR(rb_name, s_key, c_name, p_name, type) \ + static VALUE \ + ruby_whisper_parakeet_token_get_##c_name(VALUE self) \ + { \ + ruby_whisper_parakeet_token *rwpt; \ + GetParakeetToken(self, rwpt); \ + return READER(type)(rwpt->token_data->MEMBER_NAME(p_name)); \ + } + +#define DEF_ATTR(rb_name, s_key, c_name, p_name, type) \ + static VALUE \ + ruby_whisper_parakeet_token_get_##c_name(VALUE self) \ + { \ + ruby_whisper_parakeet_token *rwpt; \ + GetParakeetToken(self, rwpt); \ + return rwpt->p_name; \ + } + +VALUE cParakeetToken; + +#define DEC_ATTR_SYMS(rb_name, s_key, c_name, p_name, type) static VALUE sym_##s_key; + +ITERATE_MEMBERS(DEC_ATTR_SYMS) +ITERATE_ATTRS(DEC_ATTR_SYMS) + +static void +ruby_whisper_parakeet_token_mark(void *p) +{ + ruby_whisper_parakeet_token *rwpt = (ruby_whisper_parakeet_token *)p; + rb_gc_mark(rwpt->text); +} + +static void +ruby_whisper_parakeet_token_free(void *p) +{ + ruby_whisper_parakeet_token *rwpt = (ruby_whisper_parakeet_token *)p; + if (rwpt->token_data) { + xfree(rwpt->token_data); + rwpt->token_data = NULL; + } + xfree(rwpt); +} + +static size_t +ruby_whisper_parakeet_token_memsize(const void *p) +{ + ruby_whisper_parakeet_token *rwpt = (ruby_whisper_parakeet_token *)p; + if (!rwpt) { + return 0; + } + size_t size = sizeof(*rwpt); + if (rwpt->token_data) { + size += sizeof(*rwpt->token_data); + } + + return size; +} + +static const rb_data_type_t ruby_whisper_parakeet_token_type = { + "ruby_whisper_parakeet_token", + {ruby_whisper_parakeet_token_mark, ruby_whisper_parakeet_token_free, ruby_whisper_parakeet_token_memsize}, + 0, 0, + 0, +}; + +static VALUE +ruby_whisper_parakeet_token_s_allocate(VALUE klass) +{ + ruby_whisper_parakeet_token *rwpt; + VALUE token = TypedData_Make_Struct(klass, ruby_whisper_parakeet_token, &ruby_whisper_parakeet_token_type, rwpt); + + rwpt->token_data = NULL; + rwpt->text = Qnil; + + return token; +} + +VALUE +ruby_whisper_parakeet_token_s_from_token_data(struct parakeet_context *context, const parakeet_token_data *token_data) +{ + const VALUE token = ruby_whisper_parakeet_token_s_allocate(cParakeetToken); + ruby_whisper_parakeet_token *rwpt; + TypedData_Get_Struct(token, ruby_whisper_parakeet_token, &ruby_whisper_parakeet_token_type, rwpt); + + rwpt->token_data = ALLOC(parakeet_token_data); + *rwpt->token_data = *token_data; + rwpt->text = rb_utf8_str_new_cstr(parakeet_token_to_str(context, token_data->id)); + + return token; +} + +VALUE +ruby_whisper_parakeet_token_s_from_index(struct parakeet_context *context, int i_segment, int i_token) +{ + parakeet_token_data token_data = parakeet_full_get_token_data(context, i_segment, i_token); + return ruby_whisper_parakeet_token_s_from_token_data(context, &token_data); +} + +ITERATE_MEMBERS(DEF_MEMBER_ATTR) +// Define #text using parakeet_token_to_str or parakeet_token_to_text +ITERATE_ATTRS(DEF_ATTR) + +static VALUE +ruby_whisper_parakeet_token_deconstruct_keys(VALUE self, VALUE keys) +{ + ruby_whisper_parakeet_token *rwpt; + GetParakeetToken(self, rwpt); + + VALUE hash = rb_hash_new(); + long n_keys = 0; + + if (NIL_P(keys)) { + VALUE attrs[] = { +#define LIST_SYMS(rb_name, s_key, c_name, p_name, type) sym_##s_key, + + ITERATE_MEMBERS(LIST_SYMS) + ITERATE_ATTRS(LIST_SYMS) + }; + keys = rb_ary_new_from_values(RUBY_WHISPER_PARAKEET_TOKEN_NUM_ATTRS, attrs); + n_keys = RUBY_WHISPER_PARAKEET_TOKEN_NUM_ATTRS; + } else { + n_keys = RARRAY_LEN(keys); + if (n_keys > RUBY_WHISPER_PARAKEET_TOKEN_NUM_ATTRS) { + return hash; + } + } + for (long i = 0; i < n_keys; i++) { + VALUE key = rb_ary_entry(keys, i); + +#define CHECK_AND_SET_KEY(rb_name, s_key, c_name, p_name, type) \ + if (key == sym_##s_key) { \ + rb_hash_aset(hash, key, ruby_whisper_parakeet_token_get_##c_name(self)); \ + } + + ITERATE_MEMBERS(CHECK_AND_SET_KEY) + ITERATE_ATTRS(CHECK_AND_SET_KEY) + } + + return hash; +} + +void +init_ruby_whisper_parakeet_token(VALUE *mParakeet) +{ + cParakeetToken = rb_define_class_under(*mParakeet, "Token", rb_cObject); + rb_define_alloc_func(cParakeetToken, ruby_whisper_parakeet_token_s_allocate); + +#define REGISTER_ATTR(rb_name, s_key, c_name, p_name, type) \ + sym_##s_key = ID2SYM(rb_intern(#s_key)); \ + rb_define_method(cParakeetToken, #rb_name, ruby_whisper_parakeet_token_get_##c_name, 0); + + ITERATE_MEMBERS(REGISTER_ATTR) + ITERATE_ATTRS(REGISTER_ATTR) + + rb_define_method(cParakeetToken, "deconstruct_keys", ruby_whisper_parakeet_token_deconstruct_keys, 1); +} diff --git a/bindings/ruby/ext/ruby_whisper_parakeet_transcribe.cpp b/bindings/ruby/ext/ruby_whisper_parakeet_transcribe.cpp new file mode 100644 index 0000000..c4deccc --- /dev/null +++ b/bindings/ruby/ext/ruby_whisper_parakeet_transcribe.cpp @@ -0,0 +1,58 @@ +#include "ruby_whisper.h" +#include "common-whisper.h" +#include <string> +#include <vector> + +#ifdef __cplusplus +extern "C" { +#endif + +extern const rb_data_type_t ruby_whisper_parakeet_context_type; +extern const rb_data_type_t ruby_whisper_parakeet_params_type; + +extern VALUE ruby_whisper_parakeet_context_full_body(VALUE rb_args); + +extern ID id_to_path; +extern ID id_new; + +extern VALUE eError; + +VALUE +ruby_whisper_parakeet_transcribe(VALUE self, VALUE audio_path, VALUE params) +{ + if (rb_respond_to(audio_path, id_to_path)) { + audio_path = rb_funcall(audio_path, id_to_path, 0); + } + + std::string fname = StringValueCStr(audio_path); + std::vector<float> pcmf32; + std::vector<std::vector<float>> pcmf32s; + + if (!read_audio_data(fname, pcmf32, pcmf32s, false)) { + rb_raise(rb_eRuntimeError, "Failed to open %s", fname.c_str()); + return Qnil; + } + + ruby_whisper_parakeet_context *rwpc; + ruby_whisper_parakeet_params *rwpp; + GetParakeetContext(self, rwpc); + GetParakeetParams(params, rwpp); + + ruby_whisper_full_args args = { + &self, + ¶ms, + pcmf32.data(), + (int)pcmf32.size(), + }; + VALUE rb_result = ruby_whisper_parakeet_context_full_body((VALUE)&args); + const int result = NUM2INT(rb_result); + if (result == 0) { + return self; + } else { + rb_exc_raise(rb_funcall(eError, id_new, 1, rb_result)); + } +} + +#ifdef __cplusplus +} +#endif diff --git a/bindings/ruby/ext/ruby_whisper_params.c b/bindings/ruby/ext/ruby_whisper_params.c new file mode 100644 index 0000000..be874d5 --- /dev/null +++ b/bindings/ruby/ext/ruby_whisper_params.c @@ -0,0 +1,1548 @@ +#include "ruby_whisper.h" + +#define BOOL_PARAMS_SETTER(self, prop, value) \ + ruby_whisper_params *rwp; \ + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); \ + if (value == Qfalse || value == Qnil) { \ + rwp->params.prop = false; \ + } else { \ + rwp->params.prop = true; \ + } \ + return value; \ + +#define BOOL_PARAMS_GETTER(self, prop) \ + ruby_whisper_params *rwp; \ + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); \ + if (rwp->params.prop) { \ + return Qtrue; \ + } else { \ + return Qfalse; \ + } + +#define DEFINE_PARAM(param_name, nth) \ + id_ ## param_name = rb_intern(#param_name); \ + param_names[nth] = id_ ## param_name; \ + rb_define_method(cParams, #param_name, ruby_whisper_params_get_ ## param_name, 0); \ + rb_define_method(cParams, #param_name "=", ruby_whisper_params_set_ ## param_name, 1); + +#define RUBY_WHISPER_PARAMS_PARAM_NAMES_COUNT 37 + +extern VALUE cParams; +extern VALUE cVADParams; +extern VALUE mWhisper; + +extern ID id_call; + +extern VALUE ruby_whisper_normalize_model_path(VALUE model_path); +extern VALUE rb_whisper_segment_s_new(VALUE context, int index); +extern const rb_data_type_t ruby_whisper_vad_params_type; + +static ID param_names[RUBY_WHISPER_PARAMS_PARAM_NAMES_COUNT]; +static ID id_language; +static ID id_translate; +static ID id_no_context; +static ID id_single_segment; +static ID id_print_special; +static ID id_print_progress; +static ID id_print_realtime; +static ID id_print_timestamps; +static ID id_carry_initial_prompt; +static ID id_suppress_blank; +static ID id_suppress_nst; +static ID id_token_timestamps; +static ID id_max_len; +static ID id_split_on_word; +static ID id_initial_prompt; +static ID id_diarize; +static ID id_offset; +static ID id_duration; +static ID id_max_text_tokens; +static ID id_temperature; +static ID id_max_initial_ts; +static ID id_length_penalty; +static ID id_temperature_inc; +static ID id_entropy_thold; +static ID id_logprob_thold; +static ID id_no_speech_thold; +static ID id_new_segment_callback; +static ID id_new_segment_callback_user_data; +static ID id_progress_callback; +static ID id_progress_callback_user_data; +static ID id_encoder_begin_callback; +static ID id_encoder_begin_callback_user_data; +static ID id_abort_callback; +static ID id_abort_callback_user_data; +static ID id_vad; +static ID id_vad_model_path; +static ID id_vad_params; + +void +ruby_whisper_callback_container_mark(ruby_whisper_callback_container *rwc) +{ + if (rwc == NULL) return; + + rb_gc_mark(rwc->user_data); + rb_gc_mark(rwc->callback); + rb_gc_mark(rwc->callbacks); +} + +ruby_whisper_callback_container* +ruby_whisper_callback_container_allocate() { + ruby_whisper_callback_container *container; + container = ALLOC(ruby_whisper_callback_container); + container->context = NULL; + container->user_data = Qnil; + container->callback = Qnil; + container->callbacks = Qnil; + return container; +} + +bool +ruby_whisper_callback_container_is_present(const ruby_whisper_callback_container *container) { + return !NIL_P(container->callback) || !NIL_P(container->callbacks); +} + +typedef struct { + const ruby_whisper_callback_container *container; + struct whisper_state *state; + int n_new; +} call_new_segment_callbacks_args; + +static void* +call_new_segment_callbacks(void *v_args) { + call_new_segment_callbacks_args *args = (call_new_segment_callbacks_args *)v_args; + const ruby_whisper_callback_container *container = args->container; + struct whisper_state *state = args->state; + int n_new = args->n_new; + + // Currently, doesn't support state because + // those require to resolve GC-related problems. + if (!NIL_P(container->callback)) { + rb_funcall(container->callback, id_call, 4, *container->context, Qnil, INT2NUM(n_new), container->user_data); + } + if (NIL_P(container->callbacks)) { + return NULL; + } + const long callbacks_len = RARRAY_LEN(container->callbacks); + if (0 == callbacks_len) { + return NULL; + } + const int n_segments = whisper_full_n_segments_from_state(state); + for (int i = n_new; i > 0; i--) { + int i_segment = n_segments - i; + VALUE segment = rb_whisper_segment_s_new(*container->context, i_segment); + for (int j = 0; j < callbacks_len; j++) { + VALUE cb = rb_ary_entry(container->callbacks, j); + rb_funcall(cb, id_call, 1, segment); + } + } + + return NULL; +} + +static void new_segment_callback(struct whisper_context *ctx, struct whisper_state *state, int n_new, void *user_data) { + const ruby_whisper_callback_container *container = (ruby_whisper_callback_container *)user_data; + if (!ruby_whisper_callback_container_is_present(container)) { + return; + } + + call_new_segment_callbacks_args args = { + container, + state, + n_new + }; + rb_thread_call_with_gvl(call_new_segment_callbacks, (void *)&args); +} + +typedef struct { + const ruby_whisper_callback_container *container; + struct whisper_state *state; + int progress_cur; +} call_progress_callbacks_args; + +static void* +call_progress_callbacks(void *v_args) { + call_progress_callbacks_args *args = (call_progress_callbacks_args *)v_args; + const ruby_whisper_callback_container *container = args->container; + int progress_cur = args->progress_cur; + + // Currently, doesn't support state because + // those require to resolve GC-related problems. + if (!NIL_P(args->container->callback)) { + rb_funcall(container->callback, id_call, 4, *container->context, Qnil, INT2NUM(progress_cur), container->user_data); + } + if (NIL_P(container->callbacks)) { + return NULL; + } + const long callbacks_len = RARRAY_LEN(container->callbacks); + if (0 == callbacks_len) { + return NULL; + } + for (int j = 0; j < callbacks_len; j++) { + VALUE cb = rb_ary_entry(container->callbacks, j); + rb_funcall(cb, id_call, 1, INT2NUM(progress_cur)); + } + + return NULL; +} + +static void progress_callback(struct whisper_context *ctx, struct whisper_state *state, int progress_cur, void *user_data) { + const ruby_whisper_callback_container *container = (ruby_whisper_callback_container *)user_data; + if (!ruby_whisper_callback_container_is_present(container)) { + return; + } + + call_progress_callbacks_args args = { + container, + state, + progress_cur + }; + rb_thread_call_with_gvl(call_progress_callbacks, (void *)&args); +} + +typedef struct { + const ruby_whisper_callback_container *container; + struct whisper_state *state; + bool is_continued; +} call_encoder_begin_callbacks_args; + +static void* +call_encoder_begin_callbacks(void *v_args) { + call_encoder_begin_callbacks_args *args = (call_encoder_begin_callbacks_args *)v_args; + const ruby_whisper_callback_container *container = args->container; + VALUE result = Qnil; + + // Currently, doesn't support state because + // those require to resolve GC-related problems. + if (!NIL_P(container->callback)) { + result = rb_funcall(container->callback, id_call, 3, *container->context, Qnil, container->user_data); + if (result == Qfalse) { + args->is_continued = false; + return NULL; + } + } + if (!NIL_P(container->callbacks)) { + const long callbacks_len = RARRAY_LEN(container->callbacks); + if (0 == callbacks_len) { + return NULL; + } + for (int j = 0; j < callbacks_len; j++) { + VALUE cb = rb_ary_entry(container->callbacks, j); + result = rb_funcall(cb, id_call, 0); + if (result == Qfalse) { + args->is_continued = false; + return NULL; + } + } + } + + return NULL; +} + +static bool encoder_begin_callback(struct whisper_context *ctx, struct whisper_state *state, void *user_data) { + const ruby_whisper_callback_container *container = (ruby_whisper_callback_container *)user_data; + if (!ruby_whisper_callback_container_is_present(container)) { + return true; + } + + call_encoder_begin_callbacks_args args = { + container, + state, + true + }; + rb_thread_call_with_gvl(call_encoder_begin_callbacks, (void *)&args); + + return args.is_continued; +} + +typedef struct { + const ruby_whisper_callback_container *container; + bool is_interrupted; +} call_abort_callbacks_args; + +static void* +call_abort_callbacks(void *v_args) { + call_abort_callbacks_args *args = (call_abort_callbacks_args *)v_args; + const ruby_whisper_callback_container *container = args->container; + VALUE result = Qnil; + + if (!NIL_P(container->callback)) { + result = rb_funcall(container->callback, id_call, 1, container->user_data); + if (RTEST(result)) { + args->is_interrupted = true; + return NULL; + } + } + if (NIL_P(container->callbacks)) { + return NULL; + } + const long n_callbacks = RARRAY_LEN(container->callbacks); + if (0 == n_callbacks) { + return NULL; + } + for (int j = 0; j < n_callbacks; j++) { + VALUE cb = rb_ary_entry(container->callbacks, j); + VALUE result = rb_funcall(cb, id_call, 0); + if (RTEST(result)) { + args->is_interrupted = true; + return NULL; + } + } + + return NULL; +} + +static bool abort_callback(void * user_data) { + ruby_whisper_abort_callback_user_data *data = (ruby_whisper_abort_callback_user_data *)user_data; + + int is_interrupted = RUBY_ATOMIC_LOAD(data->is_interrupted); + if (is_interrupted) { + return true; + } + + if (!(data->callback_container) || !ruby_whisper_callback_container_is_present(data->callback_container)) { + return false; + } + + call_abort_callbacks_args args = { + data->callback_container, + false + }; + rb_thread_call_with_gvl(call_abort_callbacks, (void *)&args); + + return args.is_interrupted; +} + +static void +check_thread_safety(ruby_whisper_params *rwp, int n_processors) +{ + if (n_processors == 1) { + return; + } + + // new_segment_callback is called only after multiple threads are joined + // progress_callback is not called when parallel + + if (ruby_whisper_callback_container_is_present(rwp->encoder_begin_callback_container)) { + rb_raise(rb_eRuntimeError, "encoder begin callback not supported on parallel transcription"); + } + + if (ruby_whisper_callback_container_is_present(rwp->abort_callback_container)) { + rb_raise(rb_eRuntimeError, "abort callback not supported on parallel transcription"); + } +} + +static void register_callbacks(ruby_whisper_params * rwp, VALUE * context, ruby_whisper_abort_callback_user_data *abort_callback_user_data) { + if (ruby_whisper_callback_container_is_present(rwp->new_segment_callback_container)) { + rwp->new_segment_callback_container->context = context; + rwp->params.new_segment_callback = new_segment_callback; + rwp->params.new_segment_callback_user_data = rwp->new_segment_callback_container; + } + + if (ruby_whisper_callback_container_is_present(rwp->progress_callback_container)) { + rwp->progress_callback_container->context = context; + rwp->params.progress_callback = progress_callback; + rwp->params.progress_callback_user_data = rwp->progress_callback_container; + } + + if (ruby_whisper_callback_container_is_present(rwp->encoder_begin_callback_container)) { + rwp->encoder_begin_callback_container->context = context; + rwp->params.encoder_begin_callback = encoder_begin_callback; + rwp->params.encoder_begin_callback_user_data = rwp->encoder_begin_callback_container; + } + + abort_callback_user_data->callback_container = rwp->abort_callback_container; + rwp->abort_callback_container->context = context; + rwp->params.abort_callback = abort_callback; + rwp->params.abort_callback_user_data = (void *)abort_callback_user_data; +} + +static void set_vad_params(ruby_whisper_params *rwp) +{ + ruby_whisper_vad_params * rwvp; + TypedData_Get_Struct(rwp->vad_params, ruby_whisper_vad_params, &ruby_whisper_vad_params_type, rwvp); + rwp->params.vad_params = rwvp->params; +} + +void +prepare_transcription(ruby_whisper_params *rwp, VALUE *context, int n_processors, ruby_whisper_abort_callback_user_data *abort_callback_user_data) +{ + check_thread_safety(rwp, n_processors); + register_callbacks(rwp, context, abort_callback_user_data); + set_vad_params(rwp); +} + +void +rb_whisper_params_mark(void *p) +{ + ruby_whisper_params *rwp = (ruby_whisper_params *)p; + ruby_whisper_callback_container_mark(rwp->new_segment_callback_container); + ruby_whisper_callback_container_mark(rwp->progress_callback_container); + ruby_whisper_callback_container_mark(rwp->encoder_begin_callback_container); + ruby_whisper_callback_container_mark(rwp->abort_callback_container); + rb_gc_mark(rwp->vad_params); +} + +void +ruby_whisper_params_free(ruby_whisper_params *rwp) +{ + if (rwp->params.language) { + ruby_xfree((void *)rwp->params.language); + } + if (rwp->params.initial_prompt) { + ruby_xfree((void *)rwp->params.initial_prompt); + } + if (rwp->params.vad_model_path) { + ruby_xfree((void *)rwp->params.vad_model_path); + } + + xfree(rwp->new_segment_callback_container); + xfree(rwp->progress_callback_container); + xfree(rwp->encoder_begin_callback_container); + xfree(rwp->abort_callback_container); +} + +void +rb_whisper_params_free(void *p) +{ + ruby_whisper_params *rwp = (ruby_whisper_params *)p; + // How to free user_data and callback only when not referred to by others? + ruby_whisper_params_free(rwp); + xfree(rwp); +} + +static size_t +ruby_whisper_params_memsize(const void *p) +{ + const ruby_whisper_params *rwp = (const ruby_whisper_params *)p; + + return sizeof(ruby_whisper_params) + sizeof(rwp->params) + sizeof(rwp->vad_params); +} + +const rb_data_type_t ruby_whisper_params_type = { + "ruby_whisper_params", + { + rb_whisper_params_mark, + rb_whisper_params_free, + ruby_whisper_params_memsize, + }, + 0, 0, + 0 +}; + +static VALUE +ruby_whisper_params_allocate(VALUE klass) +{ + ruby_whisper_params *rwp; + VALUE obj = TypedData_Make_Struct(klass, ruby_whisper_params, &ruby_whisper_params_type, rwp); + rwp->params = whisper_full_default_params(WHISPER_SAMPLING_GREEDY); + if (rwp->params.language != NULL) { + rwp->params.language = ruby_strdup(rwp->params.language); + } + if (rwp->params.initial_prompt != NULL) { + rwp->params.initial_prompt = ruby_strdup(rwp->params.initial_prompt); + } + if (rwp->params.vad_model_path != NULL) { + rwp->params.vad_model_path = ruby_strdup(rwp->params.vad_model_path); + } + rwp->diarize = false; + rwp->vad_params = rb_class_new_instance(0, NULL, cVADParams); + rwp->new_segment_callback_container = ruby_whisper_callback_container_allocate(); + rwp->progress_callback_container = ruby_whisper_callback_container_allocate(); + rwp->encoder_begin_callback_container = ruby_whisper_callback_container_allocate(); + rwp->abort_callback_container = ruby_whisper_callback_container_allocate(); + return obj; +} + +/* + * params.language = "auto" | "en", etc... + * + * call-seq: + * language = lang_name -> lang_name + */ +static VALUE +ruby_whisper_params_set_language(VALUE self, VALUE value) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + ruby_xfree((void *)rwp->params.language); + rwp->params.language = NULL; + if (value == Qfalse || value == Qnil) { + rwp->params.language = ruby_strdup("auto"); + } else { + rwp->params.language = ruby_strdup(StringValueCStr(value)); + } + return value; +} +/* + * call-seq: + * language -> String + */ +static VALUE +ruby_whisper_params_get_language(VALUE self) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + if (rwp->params.language) { + return rb_str_new2(rwp->params.language); + } else { + return rb_str_new2("auto"); + } +} +/* + * call-seq: + * translate = do_translate -> do_translate + */ +static VALUE +ruby_whisper_params_set_translate(VALUE self, VALUE value) +{ + BOOL_PARAMS_SETTER(self, translate, value) +} +/* + * call-seq: + * translate -> bool + */ +static VALUE +ruby_whisper_params_get_translate(VALUE self) +{ + BOOL_PARAMS_GETTER(self, translate) +} +/* + * call-seq: + * no_context = dont_use_context -> dont_use_context + */ +static VALUE +ruby_whisper_params_set_no_context(VALUE self, VALUE value) +{ + BOOL_PARAMS_SETTER(self, no_context, value) +} +/* + * If true, does not use past transcription (if any) as initial prompt for the decoder. + * + * call-seq: + * no_context -> bool + */ +static VALUE +ruby_whisper_params_get_no_context(VALUE self) +{ + BOOL_PARAMS_GETTER(self, no_context) +} +/* + * call-seq: + * single_segment = force_single -> force_single + */ +static VALUE +ruby_whisper_params_set_single_segment(VALUE self, VALUE value) +{ + BOOL_PARAMS_SETTER(self, single_segment, value) +} +/* + * If true, forces single segment output (useful for streaming). + * + * call-seq: + * single_segment -> bool + */ +static VALUE +ruby_whisper_params_get_single_segment(VALUE self) +{ + BOOL_PARAMS_GETTER(self, single_segment) +} +/* + * call-seq: + * print_special = force_print -> force_print + */ +static VALUE +ruby_whisper_params_set_print_special(VALUE self, VALUE value) +{ + BOOL_PARAMS_SETTER(self, print_special, value) +} +/* + * If true, prints special tokens (e.g. <SOT>, <EOT>, <BEG>, etc.). + * + * call-seq: + * print_special -> bool + */ +static VALUE +ruby_whisper_params_get_print_special(VALUE self) +{ + BOOL_PARAMS_GETTER(self, print_special) +} +/* + * call-seq: + * print_progress = force_print -> force_print + */ +static VALUE +ruby_whisper_params_set_print_progress(VALUE self, VALUE value) +{ + BOOL_PARAMS_SETTER(self, print_progress, value) +} +/* + * If true, prints progress information. + * + * call-seq: + * print_progress -> bool + */ +static VALUE +ruby_whisper_params_get_print_progress(VALUE self) +{ + BOOL_PARAMS_GETTER(self, print_progress) +} +/* + * call-seq: + * print_realtime = force_print -> force_print + */ +static VALUE +ruby_whisper_params_set_print_realtime(VALUE self, VALUE value) +{ + BOOL_PARAMS_SETTER(self, print_realtime, value) +} +/* + * If true, prints results from within whisper.cpp. (avoid it, use callback instead) + * + * call-seq: + * print_realtime -> bool + */ +static VALUE +ruby_whisper_params_get_print_realtime(VALUE self) +{ + BOOL_PARAMS_GETTER(self, print_realtime) +} +/* + * call-seq: + * print_timestamps = force_print -> force_print + */ +static VALUE +ruby_whisper_params_set_print_timestamps(VALUE self, VALUE value) +{ + BOOL_PARAMS_SETTER(self, print_timestamps, value) +} +/* + * If true, prints timestamps for each text segment when printing realtime. + * + * call-seq: + * print_timestamps -> bool + */ +static VALUE +ruby_whisper_params_get_print_timestamps(VALUE self) +{ + BOOL_PARAMS_GETTER(self, print_timestamps) +} + +/* + * call-seq: + * carry_initial_prompt -> true or false + */ +static VALUE +ruby_whisper_params_get_carry_initial_prompt(VALUE self) +{ + BOOL_PARAMS_GETTER(self, carry_initial_prompt) +} + +/* + * call-seq: + * carry_initial_prompt = bool -> bool + */ +static VALUE +ruby_whisper_params_set_carry_initial_prompt(VALUE self, VALUE value) +{ + BOOL_PARAMS_SETTER(self, carry_initial_prompt, value) +} +/* + * call-seq: + * suppress_blank = force_suppress -> force_suppress + */ +static VALUE +ruby_whisper_params_set_suppress_blank(VALUE self, VALUE value) +{ + BOOL_PARAMS_SETTER(self, suppress_blank, value) +} +/* + * If true, suppresses blank outputs. + * + * call-seq: + * suppress_blank -> bool + */ +static VALUE +ruby_whisper_params_get_suppress_blank(VALUE self) +{ + BOOL_PARAMS_GETTER(self, suppress_blank) +} +/* + * call-seq: + * suppress_nst = force_suppress -> force_suppress + */ +static VALUE +ruby_whisper_params_set_suppress_nst(VALUE self, VALUE value) +{ + BOOL_PARAMS_SETTER(self, suppress_nst, value) +} +/* + * If true, suppresses non-speech-tokens. + * + * call-seq: + * suppress_nst -> bool + */ +static VALUE +ruby_whisper_params_get_suppress_nst(VALUE self) +{ + BOOL_PARAMS_GETTER(self, suppress_nst) +} +/* + * If true, enables token-level timestamps. + * + * call-seq: + * token_timestamps -> bool + */ +static VALUE +ruby_whisper_params_get_token_timestamps(VALUE self) +{ + BOOL_PARAMS_GETTER(self, token_timestamps) +} +/* + * call-seq: + * token_timestamps = force_timestamps -> force_timestamps + */ +static VALUE +ruby_whisper_params_set_token_timestamps(VALUE self, VALUE value) +{ + BOOL_PARAMS_SETTER(self, token_timestamps, value) +} + +/* + * max segment length in characters. + * + * call-seq: + * max_len -> Integer + */ +static VALUE +ruby_whisper_params_get_max_len(VALUE self) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + return INT2NUM(rwp->params.max_len); +} +/* + * call-seq: + * max_len = length -> length + */ +static VALUE +ruby_whisper_params_set_max_len(VALUE self, VALUE value) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + rwp->params.max_len = NUM2INT(value); + return value; +} + +/* + * If true, split on word rather than on token (when used with max_len). + * + * call-seq: + * translate -> bool + */ +static VALUE +ruby_whisper_params_get_split_on_word(VALUE self) +{ + BOOL_PARAMS_GETTER(self, split_on_word) +} +/* + * call-seq: + * split_on_word = force_split -> force_split + */ +static VALUE +ruby_whisper_params_set_split_on_word(VALUE self, VALUE value) +{ + BOOL_PARAMS_SETTER(self, split_on_word, value) +} +/* + * Tokens to provide to the whisper decoder as initial prompt + * these are prepended to any existing text context from a previous call + * use whisper_tokenize() to convert text to tokens. + * Maximum of whisper_n_text_ctx()/2 tokens are used (typically 224). + * + * call-seq: + * initial_prompt -> String + */ +static VALUE +ruby_whisper_params_get_initial_prompt(VALUE self) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + return rwp->params.initial_prompt == NULL ? Qnil : rb_str_new2(rwp->params.initial_prompt); +} +/* + * call-seq: + * initial_prompt = prompt -> prompt + */ +static VALUE +ruby_whisper_params_set_initial_prompt(VALUE self, VALUE value) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + ruby_xfree((void *)rwp->params.initial_prompt); + rwp->params.initial_prompt = NULL; + if (NIL_P(value)) { + rwp->params.initial_prompt = NULL; + } else { + rwp->params.initial_prompt = ruby_strdup(StringValueCStr(value)); + } + return value; +} +/* + * If true, enables diarization. + * + * call-seq: + * diarize -> bool + */ +static VALUE +ruby_whisper_params_get_diarize(VALUE self) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + if (rwp->diarize) { + return Qtrue; + } else { + return Qfalse; + } +} +/* + * call-seq: + * diarize = force_diarize -> force_diarize + */ +static VALUE +ruby_whisper_params_set_diarize(VALUE self, VALUE value) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + if (value == Qfalse || value == Qnil) { + rwp->diarize = false; + } else { + rwp->diarize = true; + } \ + return value; +} + +/* + * Start offset in ms. + * + * call-seq: + * offset -> Integer + */ +static VALUE +ruby_whisper_params_get_offset(VALUE self) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + return INT2NUM(rwp->params.offset_ms); +} +/* + * call-seq: + * offset = offset_ms -> offset_ms + */ +static VALUE +ruby_whisper_params_set_offset(VALUE self, VALUE value) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + rwp->params.offset_ms = NUM2INT(value); + return value; +} +/* + * Audio duration to process in ms. + * + * call-seq: + * duration -> Integer + */ +static VALUE +ruby_whisper_params_get_duration(VALUE self) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + return INT2NUM(rwp->params.duration_ms); +} +/* + * call-seq: + * duration = duration_ms -> duration_ms + */ +static VALUE +ruby_whisper_params_set_duration(VALUE self, VALUE value) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + rwp->params.duration_ms = NUM2INT(value); + return value; +} + +/* + * Max tokens to use from past text as prompt for the decoder. + * + * call-seq: + * max_text_tokens -> Integer + */ +static VALUE +ruby_whisper_params_get_max_text_tokens(VALUE self) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + return INT2NUM(rwp->params.n_max_text_ctx); +} +/* + * call-seq: + * max_text_tokens = n_tokens -> n_tokens + */ +static VALUE +ruby_whisper_params_set_max_text_tokens(VALUE self, VALUE value) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + rwp->params.n_max_text_ctx = NUM2INT(value); + return value; +} +/* + * call-seq: + * temperature -> Float + */ +static VALUE +ruby_whisper_params_get_temperature(VALUE self) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + return DBL2NUM(rwp->params.temperature); +} +/* + * call-seq: + * temperature = temp -> temp + */ +static VALUE +ruby_whisper_params_set_temperature(VALUE self, VALUE value) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + rwp->params.temperature = NUM2DBL(value); + return value; +} +/* + * See https://github.com/openai/whisper/blob/f82bc59f5ea234d4b97fb2860842ed38519f7e65/whisper/decoding.py#L97 + * + * call-seq: + * max_initial_ts -> Flaot + */ +static VALUE +ruby_whisper_params_get_max_initial_ts(VALUE self) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + return DBL2NUM(rwp->params.max_initial_ts); +} +/* + * call-seq: + * max_initial_ts = timestamp -> timestamp + */ +static VALUE +ruby_whisper_params_set_max_initial_ts(VALUE self, VALUE value) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + rwp->params.max_initial_ts = NUM2DBL(value); + return value; +} +/* + * call-seq: + * length_penalty -> Float + */ +static VALUE +ruby_whisper_params_get_length_penalty(VALUE self) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + return DBL2NUM(rwp->params.length_penalty); +} +/* + * call-seq: + * length_penalty = penalty -> penalty + */ +static VALUE +ruby_whisper_params_set_length_penalty(VALUE self, VALUE value) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + rwp->params.length_penalty = NUM2DBL(value); + return value; +} +/* + * call-seq: + * temperature_inc -> Float + */ +static VALUE +ruby_whisper_params_get_temperature_inc(VALUE self) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + return DBL2NUM(rwp->params.temperature_inc); +} +/* + * call-seq: + * temperature_inc = inc -> inc + */ +static VALUE +ruby_whisper_params_set_temperature_inc(VALUE self, VALUE value) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + rwp->params.temperature_inc = NUM2DBL(value); + return value; +} +/* + * Similar to OpenAI's "compression_ratio_threshold" + * + * call-seq: + * entropy_thold -> Float + */ +static VALUE +ruby_whisper_params_get_entropy_thold(VALUE self) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + return DBL2NUM(rwp->params.entropy_thold); +} +/* + * call-seq: + * entropy_thold = threshold -> threshold + */ +static VALUE +ruby_whisper_params_set_entropy_thold(VALUE self, VALUE value) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + rwp->params.entropy_thold = NUM2DBL(value); + return value; +} +/* + * call-seq: + * logprob_thold -> Float + */ +static VALUE +ruby_whisper_params_get_logprob_thold(VALUE self) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + return DBL2NUM(rwp->params.logprob_thold); +} +/* + * call-seq: + * logprob_thold = threshold -> threshold + */ +static VALUE +ruby_whisper_params_set_logprob_thold(VALUE self, VALUE value) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + rwp->params.logprob_thold = NUM2DBL(value); + return value; +} +/* + * call-seq: + * no_speech_thold -> Float + */ +static VALUE +ruby_whisper_params_get_no_speech_thold(VALUE self) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + return DBL2NUM(rwp->params.no_speech_thold); +} +/* + * call-seq: + * no_speech_thold = threshold -> threshold + */ +static VALUE +ruby_whisper_params_set_no_speech_thold(VALUE self, VALUE value) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + rwp->params.no_speech_thold = NUM2DBL(value); + return value; +} +static VALUE +ruby_whisper_params_get_new_segment_callback(VALUE self) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + return rwp->new_segment_callback_container->callback; +} +/* + * Sets new segment callback, called for every newly generated text segment. + * + * params.new_segment_callback = ->(context, _, n_new, user_data) { + * # ... + * } + * + * call-seq: + * new_segment_callback = callback -> callback + */ +static VALUE +ruby_whisper_params_set_new_segment_callback(VALUE self, VALUE value) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + rwp->new_segment_callback_container->callback = value; + return value; +} +static VALUE +ruby_whisper_params_get_new_segment_callback_user_data(VALUE self) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + return rwp->new_segment_callback_container->user_data; +} +/* + * Sets user data passed to the last argument of new segment callback. + * + * call-seq: + * new_segment_callback_user_data = user_data -> use_data + */ +static VALUE +ruby_whisper_params_set_new_segment_callback_user_data(VALUE self, VALUE value) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + rwp->new_segment_callback_container->user_data = value; + return value; +} +static VALUE +ruby_whisper_params_get_progress_callback(VALUE self) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + return rwp->progress_callback_container->callback; +} +/* + * Sets progress callback, called on each progress update. + * + * params.new_segment_callback = ->(context, _, progress, user_data) { + * # ... + * } + * + * +progress+ is an Integer between 0 and 100. + * + * call-seq: + * progress_callback = callback -> callback + */ +static VALUE +ruby_whisper_params_set_progress_callback(VALUE self, VALUE value) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + rwp->progress_callback_container->callback = value; + return value; +} +static VALUE +ruby_whisper_params_get_progress_callback_user_data(VALUE self) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + return rwp->progress_callback_container->user_data; +} +/* + * Sets user data passed to the last argument of progress callback. + * + * call-seq: + * progress_callback_user_data = user_data -> use_data + */ +static VALUE +ruby_whisper_params_set_progress_callback_user_data(VALUE self, VALUE value) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + rwp->progress_callback_container->user_data = value; + return value; +} + +static VALUE +ruby_whisper_params_get_encoder_begin_callback(VALUE self) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + return rwp->encoder_begin_callback_container->callback; +} + +/* + * Sets encoder begin callback, called when the encoder starts. + * + * params.encoder_begin_callback = ->(context, _, user_data) { + * # ... + * } + * + * call-seq: + * encoder_begin_callback = callback -> callback + */ +static VALUE +ruby_whisper_params_set_encoder_begin_callback(VALUE self, VALUE value) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + rwp->encoder_begin_callback_container->callback = value; + return value; +} + +static VALUE +ruby_whisper_params_get_encoder_begin_callback_user_data(VALUE self) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + return rwp->encoder_begin_callback_container->user_data; +} + +/* + * Sets user data passed to the last argument of encoder begin callback. + * + * call-seq: + * encoder_begin_callback_user_data = user_data -> use_data + */ +static VALUE +ruby_whisper_params_set_encoder_begin_callback_user_data(VALUE self, VALUE value) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + rwp->encoder_begin_callback_container->user_data = value; + return value; +} + +static VALUE +ruby_whisper_params_get_abort_callback(VALUE self) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + return rwp->abort_callback_container->callback; +} +/* + * Sets abort callback, called to check if the process should be aborted. + * + * params.abort_callback = ->(user_data) { + * # ... + * } + * + * call-seq: + * abort_callback = callback -> callback + */ +static VALUE +ruby_whisper_params_set_abort_callback(VALUE self, VALUE value) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + rwp->abort_callback_container->callback = value; + return value; +} +static VALUE +ruby_whisper_params_get_abort_callback_user_data(VALUE self) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + return rwp->abort_callback_container->user_data; +} +/* + * Sets user data passed to the last argument of abort callback. + * + * call-seq: + * abort_callback_user_data = user_data -> use_data + */ +static VALUE +ruby_whisper_params_set_abort_callback_user_data(VALUE self, VALUE value) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + rwp->abort_callback_container->user_data = value; + return value; +} + +/* + * call-seq: + * vad = use_vad -> use_vad + */ +static VALUE +ruby_whisper_params_get_vad(VALUE self) +{ + BOOL_PARAMS_GETTER(self, vad) +} + +static VALUE +ruby_whisper_params_set_vad(VALUE self, VALUE value) +{ + BOOL_PARAMS_SETTER(self, vad, value) +} + +/* + * call-seq: + * vad_model_path = model_path -> model_path + */ +static VALUE +ruby_whisper_params_set_vad_model_path(VALUE self, VALUE value) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + ruby_xfree((void *)rwp->params.vad_model_path); + rwp->params.vad_model_path = NULL; + if (NIL_P(value)) { + rwp->params.vad_model_path = NULL; + return value; + } + VALUE path = ruby_whisper_normalize_model_path(value); + rwp->params.vad_model_path = ruby_strdup(StringValueCStr(path)); + return value; +} + +static VALUE +ruby_whisper_params_get_vad_model_path(VALUE self) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + return rwp->params.vad_model_path == NULL ? Qnil : rb_str_new2(rwp->params.vad_model_path); +} + +/* + * call-seq: + * vad_params = params -> params + */ +static VALUE +ruby_whisper_params_set_vad_params(VALUE self, VALUE value) +{ + ruby_whisper_params *rwp; + rb_check_typeddata(value, &ruby_whisper_vad_params_type); + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + rwp->vad_params = value; + return value; +} + +static VALUE +ruby_whisper_params_get_vad_params(VALUE self) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + return rwp->vad_params; +} + +#define SET_PARAM_IF_SAME(param_name) \ + if (id == id_ ## param_name) { \ + ruby_whisper_params_set_ ## param_name(self, value); \ + continue; \ + } + +static VALUE +ruby_whisper_params_initialize(int argc, VALUE *argv, VALUE self) +{ + VALUE kw_hash; + VALUE values[RUBY_WHISPER_PARAMS_PARAM_NAMES_COUNT] = {Qundef}; + VALUE value; + ruby_whisper_params *rwp; + ID id; + int i; + + rb_scan_args_kw(RB_SCAN_ARGS_KEYWORDS, argc, argv, ":", &kw_hash); + if (NIL_P(kw_hash)) { + return self; + } + + rb_get_kwargs(kw_hash, param_names, 0, RUBY_WHISPER_PARAMS_PARAM_NAMES_COUNT, values); + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + + for (i = 0; i < RUBY_WHISPER_PARAMS_PARAM_NAMES_COUNT; i++) { + id = param_names[i]; + value = values[i]; + if (value == Qundef) { + continue; + } + if (id == id_diarize) { + rwp->diarize = value; + continue; + } else { + SET_PARAM_IF_SAME(language) + SET_PARAM_IF_SAME(translate) + SET_PARAM_IF_SAME(no_context) + SET_PARAM_IF_SAME(single_segment) + SET_PARAM_IF_SAME(print_special) + SET_PARAM_IF_SAME(print_progress) + SET_PARAM_IF_SAME(print_realtime) + SET_PARAM_IF_SAME(print_timestamps) + SET_PARAM_IF_SAME(suppress_blank) + SET_PARAM_IF_SAME(suppress_nst) + SET_PARAM_IF_SAME(token_timestamps) + SET_PARAM_IF_SAME(max_len) + SET_PARAM_IF_SAME(split_on_word) + SET_PARAM_IF_SAME(initial_prompt) + SET_PARAM_IF_SAME(carry_initial_prompt) + SET_PARAM_IF_SAME(offset) + SET_PARAM_IF_SAME(duration) + SET_PARAM_IF_SAME(max_text_tokens) + SET_PARAM_IF_SAME(temperature) + SET_PARAM_IF_SAME(max_initial_ts) + SET_PARAM_IF_SAME(length_penalty) + SET_PARAM_IF_SAME(temperature_inc) + SET_PARAM_IF_SAME(entropy_thold) + SET_PARAM_IF_SAME(logprob_thold) + SET_PARAM_IF_SAME(no_speech_thold) + SET_PARAM_IF_SAME(new_segment_callback) + SET_PARAM_IF_SAME(new_segment_callback_user_data) + SET_PARAM_IF_SAME(progress_callback) + SET_PARAM_IF_SAME(progress_callback_user_data) + SET_PARAM_IF_SAME(encoder_begin_callback) + SET_PARAM_IF_SAME(encoder_begin_callback_user_data) + SET_PARAM_IF_SAME(abort_callback) + SET_PARAM_IF_SAME(abort_callback_user_data) + SET_PARAM_IF_SAME(vad) + SET_PARAM_IF_SAME(vad_model_path) + SET_PARAM_IF_SAME(vad_params) + } + } + + return self; +} + +#undef SET_PARAM_IF_SAME + +/* + * Hook called on new segment. Yields each Whisper::Segment. + * + * whisper.on_new_segment do |segment| + * # ... + * end + * + * call-seq: + * on_new_segment {|segment| ... } + */ +static VALUE +ruby_whisper_params_on_new_segment(VALUE self) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + const VALUE blk = rb_block_proc(); + if (NIL_P(rwp->new_segment_callback_container->callbacks)) { + rwp->new_segment_callback_container->callbacks = rb_ary_new(); + } + rb_ary_push(rwp->new_segment_callback_container->callbacks, blk); + return Qnil; +} + +/* + * Hook called on progress update. Yields each progress Integer between 0 and 100. + * + * whisper.on_progress do |progress| + * # ... + * end + * + * call-seq: + * on_progress {|progress| ... } + */ +static VALUE +ruby_whisper_params_on_progress(VALUE self) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + const VALUE blk = rb_block_proc(); + if (NIL_P(rwp->progress_callback_container->callbacks)) { + rwp->progress_callback_container->callbacks = rb_ary_new(); + } + rb_ary_push(rwp->progress_callback_container->callbacks, blk); + return Qnil; +} + +/* + * Hook called when the encoder starts. + * + * whisper.on_encoder_begin do + * # ... + * end + * + * call-seq: + * on_encoder_begin { ... } + */ +static VALUE +ruby_whisper_params_on_encoder_begin(VALUE self) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + const VALUE blk = rb_block_proc(); + if (NIL_P(rwp->encoder_begin_callback_container->callbacks)) { + rwp->encoder_begin_callback_container->callbacks = rb_ary_new(); + } + rb_ary_push(rwp->encoder_begin_callback_container->callbacks, blk); + return Qnil; +} + +/* + * Call block to determine whether abort or not. Return +true+ when you want to abort. + * + * params.abort_on do + * if some_condition + * true # abort + * else + * false # continue + * end + * end + * + * call-seq: + * abort_on { ... } + */ +static VALUE +ruby_whisper_params_abort_on(VALUE self) +{ + ruby_whisper_params *rwp; + TypedData_Get_Struct(self, ruby_whisper_params, &ruby_whisper_params_type, rwp); + const VALUE blk = rb_block_proc(); + if (NIL_P(rwp->abort_callback_container->callbacks)) { + rwp->abort_callback_container->callbacks = rb_ary_new(); + } + rb_ary_push(rwp->abort_callback_container->callbacks, blk); + return Qnil; +} + +void +init_ruby_whisper_params(VALUE *mWhisper) +{ + cParams = rb_define_class_under(*mWhisper, "Params", rb_cObject); + + rb_define_alloc_func(cParams, ruby_whisper_params_allocate); + rb_define_method(cParams, "initialize", ruby_whisper_params_initialize, -1); + + DEFINE_PARAM(language, 0) + DEFINE_PARAM(translate, 1) + DEFINE_PARAM(no_context, 2) + DEFINE_PARAM(single_segment, 3) + DEFINE_PARAM(print_special, 4) + DEFINE_PARAM(print_progress, 5) + DEFINE_PARAM(print_realtime, 6) + DEFINE_PARAM(print_timestamps, 7) + DEFINE_PARAM(suppress_blank, 8) + DEFINE_PARAM(suppress_nst, 9) + DEFINE_PARAM(token_timestamps, 10) + DEFINE_PARAM(max_len, 11) + DEFINE_PARAM(split_on_word, 12) + DEFINE_PARAM(initial_prompt, 13) + DEFINE_PARAM(carry_initial_prompt, 14) + DEFINE_PARAM(diarize, 15) + DEFINE_PARAM(offset, 16) + DEFINE_PARAM(duration, 17) + DEFINE_PARAM(max_text_tokens, 18) + DEFINE_PARAM(temperature, 19) + DEFINE_PARAM(max_initial_ts, 20) + DEFINE_PARAM(length_penalty, 21) + DEFINE_PARAM(temperature_inc, 22) + DEFINE_PARAM(entropy_thold, 23) + DEFINE_PARAM(logprob_thold, 24) + DEFINE_PARAM(no_speech_thold, 25) + DEFINE_PARAM(new_segment_callback, 26) + DEFINE_PARAM(new_segment_callback_user_data, 27) + DEFINE_PARAM(progress_callback, 28) + DEFINE_PARAM(progress_callback_user_data, 29) + DEFINE_PARAM(encoder_begin_callback, 30) + DEFINE_PARAM(encoder_begin_callback_user_data, 31) + DEFINE_PARAM(abort_callback, 32) + DEFINE_PARAM(abort_callback_user_data, 33) + DEFINE_PARAM(vad, 34) + DEFINE_PARAM(vad_model_path, 35) + DEFINE_PARAM(vad_params, 36) + + rb_define_method(cParams, "on_new_segment", ruby_whisper_params_on_new_segment, 0); + rb_define_method(cParams, "on_progress", ruby_whisper_params_on_progress, 0); + rb_define_method(cParams, "on_encoder_begin", ruby_whisper_params_on_encoder_begin, 0); + rb_define_method(cParams, "abort_on", ruby_whisper_params_abort_on, 0); +} diff --git a/bindings/ruby/ext/ruby_whisper_segment.c b/bindings/ruby/ext/ruby_whisper_segment.c new file mode 100644 index 0000000..cf03727 --- /dev/null +++ b/bindings/ruby/ext/ruby_whisper_segment.c @@ -0,0 +1,284 @@ +#include "ruby_whisper.h" + +#define N_KEY_NAMES 6 + +extern ID id___method__; +extern ID id_to_enum; +VALUE sym_start_time; +VALUE sym_end_time; +VALUE sym_text; +VALUE sym_no_speech_prob; +VALUE sym_speaker_turn_next; +VALUE sym_n_tokens; + +extern const rb_data_type_t ruby_whisper_type; + +extern VALUE cSegment; + +extern VALUE ruby_whisper_token_s_init(struct whisper_context *context, int i_segment, int index); + +static void +rb_whisper_segment_mark(void *p) +{ + ruby_whisper_segment *rws = (ruby_whisper_segment *)p; + rb_gc_mark(rws->context); +} + +static size_t +ruby_whisper_segment_memsize(const void *p) +{ + const ruby_whisper_segment *rws = (const ruby_whisper_segment *)p; + size_t size = sizeof(rws); + if (!rws) { + return 0; + } + if (rws->index) { + size += sizeof(rws->index); + } + return size; +} + +static const rb_data_type_t ruby_whisper_segment_type = { + "ruby_whisper_segment", + {rb_whisper_segment_mark, RUBY_DEFAULT_FREE, ruby_whisper_segment_memsize,}, + 0, 0, + 0 +}; + +VALUE +ruby_whisper_segment_allocate(VALUE klass) +{ + ruby_whisper_segment *rws; + return TypedData_Make_Struct(klass, ruby_whisper_segment, &ruby_whisper_segment_type, rws); +} + +VALUE +rb_whisper_segment_s_new(VALUE context, int index) +{ + ruby_whisper_segment *rws; + const VALUE segment = ruby_whisper_segment_allocate(cSegment); + TypedData_Get_Struct(segment, ruby_whisper_segment, &ruby_whisper_segment_type, rws); + rws->context = context; + rws->index = index; + return segment; +}; + +/* + * Start time in milliseconds. + * + * call-seq: + * start_time -> Integer + */ +static VALUE +ruby_whisper_segment_get_start_time(VALUE self) +{ + ruby_whisper_segment *rws; + TypedData_Get_Struct(self, ruby_whisper_segment, &ruby_whisper_segment_type, rws); + ruby_whisper *rw; + GetContext(rws->context, rw); + const int64_t t0 = whisper_full_get_segment_t0(rw->context, rws->index); + // able to multiply 10 without overflow because to_timestamp() in whisper.cpp does it + return LONG2NUM(t0 * 10); +} + +/* + * End time in milliseconds. + * + * call-seq: + * end_time -> Integer + */ +static VALUE +ruby_whisper_segment_get_end_time(VALUE self) +{ + ruby_whisper_segment *rws; + TypedData_Get_Struct(self, ruby_whisper_segment, &ruby_whisper_segment_type, rws); + ruby_whisper *rw; + GetContext(rws->context, rw); + const int64_t t1 = whisper_full_get_segment_t1(rw->context, rws->index); + // able to multiply 10 without overflow because to_timestamp() in whisper.cpp does it + return LONG2NUM(t1 * 10); +} + +/* + * Whether the next segment is predicted as a speaker turn. + * + * call-seq: + * speaker_turn_next? -> bool + */ +static VALUE +ruby_whisper_segment_get_speaker_turn_next(VALUE self) +{ + ruby_whisper_segment *rws; + TypedData_Get_Struct(self, ruby_whisper_segment, &ruby_whisper_segment_type, rws); + ruby_whisper *rw; + GetContext(rws->context, rw); + return whisper_full_get_segment_speaker_turn_next(rw->context, rws->index) ? Qtrue : Qfalse; +} + +/* + * call-seq: + * text -> String + */ +static VALUE +ruby_whisper_segment_get_text(VALUE self) +{ + ruby_whisper_segment *rws; + TypedData_Get_Struct(self, ruby_whisper_segment, &ruby_whisper_segment_type, rws); + ruby_whisper *rw; + GetContext(rws->context, rw); + const char * text = whisper_full_get_segment_text(rw->context, rws->index); + return rb_str_new2(text); +} + +/* + * call-seq: + * no_speech_prob -> Float + */ +static VALUE +ruby_whisper_segment_get_no_speech_prob(VALUE self) +{ + ruby_whisper_segment *rws; + TypedData_Get_Struct(self, ruby_whisper_segment, &ruby_whisper_segment_type, rws); + ruby_whisper *rw; + GetContext(rws->context, rw); + return DBL2NUM(whisper_full_get_segment_no_speech_prob(rw->context, rws->index)); +} + +/* + * Get number of tokens in the segment + * + * call-seq: + * n_tokens -> Integer + */ +static VALUE +ruby_whisper_segment_get_n_tokens(VALUE self) +{ + ruby_whisper_segment *rws; + TypedData_Get_Struct(self, ruby_whisper_segment, &ruby_whisper_segment_type, rws); + ruby_whisper *rw; + GetContext(rws->context, rw); + return INT2NUM(whisper_full_n_tokens(rw->context, rws->index)); +} + +/* + * Yields each Whisper::Token: + * + * whisper.each_segment.first.each_token do |token| + * p token + * end + * + * Returns an Enumerator if no block is given: + * + * whisper.each_segment.first.each_token.to_a # => [#<Whisper::Token>, ...] + * + * call-seq: + * each_token {|token| ... } + * each_token -> Enumerator + */ +static VALUE +ruby_whisper_segment_each_token(VALUE self) +{ + if (!rb_block_given_p()) { + const VALUE method_name = rb_funcall(self, id___method__, 0); + return rb_funcall(self, id_to_enum, 1, method_name); + } + + ruby_whisper_segment *rws; + TypedData_Get_Struct(self, ruby_whisper_segment, &ruby_whisper_segment_type, rws); + ruby_whisper *rw; + GetContext(rws->context, rw); + + const int n_tokens = whisper_full_n_tokens(rw->context, rws->index); + for (int i = 0; i < n_tokens; ++i) { + rb_yield(ruby_whisper_token_s_init(rw->context, rws->index, i)); + } + + return self; +} + +/* + * call-seq: + * deconstruct_keys(keys) -> hash + * + * Possible keys: :start_time, :end_time, :text, :no_speech_prob, :speaker_turn_next, :n_tokens + * + * whisper.each_segment do |segment| + * segment => {start_time:, end_time:, text:, no_speech_prob:, speaker_turn_next:} + * + * puts "[#{start_time} --> #{end_time}] #{text} (no speech prob: #{no_speech_prob}#{speaker_turn_next ? ', speaker turns next' : ''})" + * end + */ +static VALUE +ruby_whisper_segment_deconstruct_keys(VALUE self, VALUE keys) +{ + ruby_whisper_segment *rws; + TypedData_Get_Struct(self, ruby_whisper_segment, &ruby_whisper_segment_type, rws); + ruby_whisper *rw; + GetContext(rws->context, rw); + + VALUE hash = rb_hash_new(); + long n_keys; + if (NIL_P(keys)) { + keys = rb_ary_new3( + N_KEY_NAMES, + sym_start_time, + sym_end_time, + sym_text, + sym_no_speech_prob, + sym_speaker_turn_next + ); + n_keys = N_KEY_NAMES; + } else { + n_keys = RARRAY_LEN(keys); + if (n_keys > N_KEY_NAMES) { + return hash; + } + } + for (int i = 0; i < n_keys; i++) { + VALUE key = rb_ary_entry(keys, i); + if (key == sym_start_time) { + rb_hash_aset(hash, key, ruby_whisper_segment_get_start_time(self)); + } + if (key == sym_end_time) { + rb_hash_aset(hash, key, ruby_whisper_segment_get_end_time(self)); + } + if (key == sym_text) { + rb_hash_aset(hash, key, ruby_whisper_segment_get_text(self)); + } + if (key == sym_no_speech_prob) { + rb_hash_aset(hash, key, ruby_whisper_segment_get_no_speech_prob(self)); + } + if (key == sym_speaker_turn_next) { + rb_hash_aset(hash, key, ruby_whisper_segment_get_speaker_turn_next(self)); + } + if (key == sym_n_tokens) { + rb_hash_aset(hash, key, ruby_whisper_segment_get_n_tokens(self)); + } + } + + return hash; +} + +void +init_ruby_whisper_segment(VALUE *mWhisper) +{ + cSegment = rb_define_class_under(*mWhisper, "Segment", rb_cObject); + + sym_start_time = ID2SYM(rb_intern("start_time")); + sym_end_time = ID2SYM(rb_intern("end_time")); + sym_text = ID2SYM(rb_intern("text")); + sym_no_speech_prob = ID2SYM(rb_intern("no_speech_prob")); + sym_speaker_turn_next = ID2SYM(rb_intern("speaker_turn_next")); + sym_n_tokens = ID2SYM(rb_intern("n_tokens")); + + rb_define_alloc_func(cSegment, ruby_whisper_segment_allocate); + rb_define_method(cSegment, "start_time", ruby_whisper_segment_get_start_time, 0); + rb_define_method(cSegment, "end_time", ruby_whisper_segment_get_end_time, 0); + rb_define_method(cSegment, "speaker_turn_next?", ruby_whisper_segment_get_speaker_turn_next, 0); + rb_define_method(cSegment, "text", ruby_whisper_segment_get_text, 0); + rb_define_method(cSegment, "no_speech_prob", ruby_whisper_segment_get_no_speech_prob, 0); + rb_define_method(cSegment, "n_tokens", ruby_whisper_segment_get_n_tokens, 0); + rb_define_method(cSegment, "each_token", ruby_whisper_segment_each_token, 0); + rb_define_method(cSegment, "deconstruct_keys", ruby_whisper_segment_deconstruct_keys, 1); +} +#undef N_KEY_NAMES diff --git a/bindings/ruby/ext/ruby_whisper_token.c b/bindings/ruby/ext/ruby_whisper_token.c new file mode 100644 index 0000000..73f5a54 --- /dev/null +++ b/bindings/ruby/ext/ruby_whisper_token.c @@ -0,0 +1,371 @@ +#include "ruby_whisper.h" + +#define N_KEY_NAMES 11 + +extern VALUE cToken; +extern const rb_data_type_t ruby_whisper_type; + +static VALUE sym_id; +static VALUE sym_tid; +static VALUE sym_probability; +static VALUE sym_log_probability; +static VALUE sym_pt; +static VALUE sym_ptsum; +static VALUE sym_t_dtw; +static VALUE sym_voice_length; +static VALUE sym_start_time; +static VALUE sym_end_time; +static VALUE sym_text; + +static size_t +ruby_whisper_token_memsize(const void *p) +{ + const ruby_whisper_token *rwt = (const ruby_whisper_token *)p; + if (!rwt) { + return 0; + } + size_t size = sizeof(*rwt); + if (rwt->token_data) { + size += sizeof(*rwt->token_data); + } + return size; +} + +static void +ruby_whisper_token_mark(void *p) +{ + ruby_whisper_token *rwt = (ruby_whisper_token *)p; + rb_gc_mark(rwt->text); +} + +static void +ruby_whisper_token_free(void *p) +{ + ruby_whisper_token *rwt = (ruby_whisper_token *)p; + if (rwt->token_data) { + xfree(rwt->token_data); + rwt->token_data = NULL; + } + xfree(rwt); +} + +static const rb_data_type_t ruby_whisper_token_type = { + "ruby_whisper_token", + {ruby_whisper_token_mark, ruby_whisper_token_free, ruby_whisper_token_memsize,}, + 0, 0, + 0 +}; + +static VALUE +ruby_whisper_token_allocate(VALUE klass) +{ + ruby_whisper_token *rwt; + VALUE token = TypedData_Make_Struct(klass, ruby_whisper_token, &ruby_whisper_token_type, rwt); + rwt->token_data = NULL; + rwt->text = Qnil; + return token; +} + +VALUE +ruby_whisper_token_s_init(struct whisper_context *context, int i_segment, int i_token) +{ + const VALUE token = ruby_whisper_token_allocate(cToken); + ruby_whisper_token *rwt; + TypedData_Get_Struct(token, ruby_whisper_token, &ruby_whisper_token_type, rwt); + rwt->token_data = ALLOC(whisper_token_data); + *(rwt->token_data) = whisper_full_get_token_data(context, i_segment, i_token); + rwt->text = rb_str_new2(whisper_full_get_token_text(context, i_segment, i_token)); + return token; +} + +/* + * Token ID. + * + * call-seq: + * id -> Integer + */ +static VALUE +ruby_whisper_token_get_id(VALUE self) +{ + ruby_whisper_token *rwt; + GetToken(self, rwt); + return INT2NUM(rwt->token_data->id); +} + +/* + * Forced timestamp token ID. + * + * call-seq: + * tid -> Integer + */ +static VALUE +ruby_whisper_token_get_tid(VALUE self) +{ + ruby_whisper_token *rwt; + GetToken(self, rwt); + return INT2NUM(rwt->token_data->tid); +} + +/* + * Probability of the token. + * + * call-seq: + * probability -> Float + */ +static VALUE +ruby_whisper_token_get_p(VALUE self) +{ + ruby_whisper_token *rwt; + GetToken(self, rwt); + return DBL2NUM(rwt->token_data->p); +} + +/* + * Log probability of the token. + * + * call-seq: + * log_probability -> Float + */ +static VALUE +ruby_whisper_token_get_plog(VALUE self) +{ + ruby_whisper_token *rwt; + GetToken(self, rwt); + return DBL2NUM(rwt->token_data->plog); +} + +/* + * Probability of the timestamp token. + * + * call-seq: + * pt -> Float + */ +static VALUE +ruby_whisper_token_get_pt(VALUE self) +{ + ruby_whisper_token *rwt; + GetToken(self, rwt); + return DBL2NUM(rwt->token_data->pt); +} + +/* + * Sum of probability of all timestamp tokens. + * + * call-seq: + * ptsum -> Float + */ +static VALUE +ruby_whisper_token_get_ptsum(VALUE self) +{ + ruby_whisper_token *rwt; + GetToken(self, rwt); + return DBL2NUM(rwt->token_data->ptsum); +} + +/* + * [EXPERIMENTAL] Token-level timestamps with DTW + * + * Do not use if you haven't computed token-level timestamps with dtw. + * Roughly corresponds to the moment in audio in which the token was output. + * + * call-seq: + * t_dtw -> Integer + */ +static VALUE +ruby_whisper_token_get_t_dtw(VALUE self) +{ + ruby_whisper_token *rwt; + GetToken(self, rwt); + return LONG2NUM(rwt->token_data->t_dtw); +} + +/* + * Voice length of the token. + * + * call-seq: + * voice_length -> Float + */ +static VALUE +ruby_whisper_token_get_vlen(VALUE self) +{ + ruby_whisper_token *rwt; + GetToken(self, rwt); + return DBL2NUM(rwt->token_data->vlen); +} + +/* + * Get the token text of the token. + * + * call-seq: + * text -> String + */ +static VALUE +ruby_whisper_token_get_text(VALUE self) +{ + ruby_whisper_token *rwt; + GetToken(self, rwt); + return rwt->text; +} + +/* + * Start time of the token. + * + * Token-level timestamp data. + * Do not use if you haven't computed token-level timestamps. + * + * call-seq: + * start_time -> Integer + */ +static VALUE +ruby_whisper_token_get_start_time(VALUE self) +{ + ruby_whisper_token *rwt; + GetToken(self, rwt); + return LONG2NUM(rwt->token_data->t0 * 10); +} + +/* + * End time of the token. + * + * Token-level timestamp data. + * Do not use if you haven't computed token-level timestamps. + * + * call-seq: + * end_time -> Integer + */ +static VALUE +ruby_whisper_token_get_end_time(VALUE self) +{ + ruby_whisper_token *rwt; + GetToken(self, rwt); + return LONG2NUM(rwt->token_data->t1 * 10); +} + +/* + * call-seq: + * deconstruct_keys(keys) -> hash + * + * Possible keys: :id, :tid, :probability, :log_probability, :pt, :ptsum, + * :t_dtw, :voice_length, :start_time, :end_time, :text + * segment.each_token do |token| + * token => {text:, probability:} + puts "#{text} (#{probability})" + * end + */ +static VALUE ruby_whisper_token_deconstruct_keys(VALUE self, VALUE keys) +{ + ruby_whisper_token *rwt; + GetToken(self, rwt); + VALUE hash = rb_hash_new(); + long n_keys = 0; + + if (NIL_P(keys)) { + keys = rb_ary_new3( + N_KEY_NAMES, + sym_id, + sym_tid, + sym_probability, + sym_log_probability, + sym_pt, + sym_ptsum, + sym_t_dtw, + sym_voice_length, + sym_start_time, + sym_end_time, + sym_text + ); + n_keys = N_KEY_NAMES; + } else { + n_keys = RARRAY_LEN(keys); + if (n_keys > N_KEY_NAMES) { + return hash; + } + } + + for (int i = 0; i < n_keys; i++) { + VALUE key = rb_ary_entry(keys, i); + if (key == sym_start_time) { + rb_hash_aset(hash, key, ruby_whisper_token_get_start_time(self)); + continue; + } + if (key == sym_end_time) { + rb_hash_aset(hash, key, ruby_whisper_token_get_end_time(self)); + continue; + } + if (key == sym_text) { + rb_hash_aset(hash, key, ruby_whisper_token_get_text(self)); + continue; + } + if (key == sym_probability) { + rb_hash_aset(hash, key, ruby_whisper_token_get_p(self)); + continue; + } + if (key == sym_id) { + rb_hash_aset(hash, key, ruby_whisper_token_get_id(self)); + continue; + } + if (key == sym_tid) { + rb_hash_aset(hash, key, ruby_whisper_token_get_tid(self)); + continue; + } + if (key == sym_log_probability) { + rb_hash_aset(hash, key, ruby_whisper_token_get_plog(self)); + continue; + } + if (key == sym_pt) { + rb_hash_aset(hash, key, ruby_whisper_token_get_pt(self)); + continue; + } + if (key == sym_ptsum) { + rb_hash_aset(hash, key, ruby_whisper_token_get_ptsum(self)); + continue; + } + if (key == sym_t_dtw) { + rb_hash_aset(hash, key, ruby_whisper_token_get_t_dtw(self)); + continue; + } + if (key == sym_voice_length) { + rb_hash_aset(hash, key, ruby_whisper_token_get_vlen(self)); + continue; + } + } + + return hash; +} + + +void +init_ruby_whisper_token(VALUE *mWhisper) +{ + cToken = rb_define_class_under(*mWhisper, "Token", rb_cObject); + + rb_define_alloc_func(cToken, ruby_whisper_token_allocate); + + sym_id = ID2SYM(rb_intern("id")); + sym_tid = ID2SYM(rb_intern("tid")); + sym_probability = ID2SYM(rb_intern("probability")); + sym_log_probability = ID2SYM(rb_intern("log_probability")); + sym_pt = ID2SYM(rb_intern("pt")); + sym_ptsum = ID2SYM(rb_intern("ptsum")); + sym_t_dtw = ID2SYM(rb_intern("t_dtw")); + sym_voice_length = ID2SYM(rb_intern("voice_length")); + sym_start_time = ID2SYM(rb_intern("start_time")); + sym_end_time = ID2SYM(rb_intern("end_time")); + sym_text = ID2SYM(rb_intern("text")); + + rb_define_method(cToken, "id", ruby_whisper_token_get_id, 0); + rb_define_method(cToken, "tid", ruby_whisper_token_get_tid, 0); + rb_define_method(cToken, "probability", ruby_whisper_token_get_p, 0); + rb_define_method(cToken, "log_probability", ruby_whisper_token_get_plog, 0); + rb_define_method(cToken, "pt", ruby_whisper_token_get_pt, 0); + rb_define_method(cToken, "ptsum", ruby_whisper_token_get_ptsum, 0); + rb_define_method(cToken, "t_dtw", ruby_whisper_token_get_t_dtw, 0); + rb_define_method(cToken, "voice_length", ruby_whisper_token_get_vlen, 0); + rb_define_method(cToken, "start_time", ruby_whisper_token_get_start_time, 0); + rb_define_method(cToken, "end_time", ruby_whisper_token_get_end_time, 0); + rb_define_method(cToken, "text", ruby_whisper_token_get_text, 0); + + rb_define_method(cToken, "deconstruct_keys", ruby_whisper_token_deconstruct_keys, 1); +} + +#undef N_KEY_NAMES diff --git a/bindings/ruby/ext/ruby_whisper_transcribe.cpp b/bindings/ruby/ext/ruby_whisper_transcribe.cpp new file mode 100644 index 0000000..699f9c6 --- /dev/null +++ b/bindings/ruby/ext/ruby_whisper_transcribe.cpp @@ -0,0 +1,104 @@ +#include "ruby_whisper.h" +#include "common-whisper.h" +#include <string> +#include <vector> + +#ifdef __cplusplus +extern "C" { +#endif + +extern const rb_data_type_t ruby_whisper_type; +extern const rb_data_type_t ruby_whisper_params_type; + +extern ID id_to_s; +extern ID id_call; +extern ID id_to_path; +extern ID transcribe_option_names[1]; + +extern VALUE full_body(VALUE rb_args); +extern VALUE full_parallel_body(VALUE rb_args); + +/* + * transcribe a single file + * can emit to a block results + * + * params = Whisper::Params.new + * params.duration = 60_000 + * whisper.transcribe "path/to/audio.wav", params do |text| + * puts text + * end + * + * call-seq: + * transcribe(path_to_audio, params) {|text| ...} + **/ +VALUE +ruby_whisper_transcribe(int argc, VALUE *argv, VALUE self) { + ruby_whisper *rw; + ruby_whisper_params *rwp; + VALUE wave_file_path, blk, params, kws; + VALUE opts[1]; + + rb_scan_args_kw(RB_SCAN_ARGS_LAST_HASH_KEYWORDS, argc, argv, "2:&", &wave_file_path, ¶ms, &kws, &blk); + rb_get_kwargs(kws, transcribe_option_names, 0, 1, opts); + + int n_processors = opts[0] == Qundef ? 1 : NUM2INT(opts[0]); + + GetContext(self, rw); + TypedData_Get_Struct(params, ruby_whisper_params, &ruby_whisper_params_type, rwp); + + if (!rb_respond_to(wave_file_path, id_to_s)) { + rb_raise(rb_eRuntimeError, "Expected file path to wave file"); + } + + if (rb_respond_to(wave_file_path, id_to_path)) { + wave_file_path = rb_funcall(wave_file_path, id_to_path, 0); + } + std::string fname_inp = StringValueCStr(wave_file_path); + + std::vector<float> pcmf32; // mono-channel F32 PCM + std::vector<std::vector<float>> pcmf32s; // stereo-channel F32 PCM + + if (!read_audio_data(fname_inp, pcmf32, pcmf32s, rwp->diarize)) { + fprintf(stderr, "error: failed to open '%s' as WAV file\n", fname_inp.c_str()); + return self; + } + + VALUE rb_result; + if (n_processors == 1) { + ruby_whisper_full_args args = { + &self, + ¶ms, + pcmf32.data(), + (int)pcmf32.size(), + }; + rb_result = full_body((VALUE)&args); + } else { + ruby_whisper_full_parallel_args parallel_args = { + &self, + ¶ms, + pcmf32.data(), + (int)pcmf32.size(), + n_processors, + }; + rb_result = full_parallel_body((VALUE)¶llel_args); + } + const int result = NUM2INT(rb_result); + if (result != 0) { + fprintf(stderr, "failed to process audio\n"); + return self; + } + if (NIL_P(blk)) { + return self; + } + const int n_segments = whisper_full_n_segments(rw->context); + VALUE output = rb_str_new2(""); + for (int i = 0; i < n_segments; ++i) { + const char * text = whisper_full_get_segment_text(rw->context, i); + output = rb_str_concat(output, rb_str_new2(text)); + } + rb_funcall(blk, id_call, 1, output); + return self; +} +#ifdef __cplusplus +} +#endif diff --git a/bindings/ruby/ext/ruby_whisper_vad_context.c b/bindings/ruby/ext/ruby_whisper_vad_context.c new file mode 100644 index 0000000..b98ed93 --- /dev/null +++ b/bindings/ruby/ext/ruby_whisper_vad_context.c @@ -0,0 +1,154 @@ +#include "ruby_whisper.h" + +extern ID id_to_s; + +extern VALUE cVADContext; + +extern const rb_data_type_t ruby_whisper_vad_params_type; +extern VALUE ruby_whisper_vad_detect(VALUE self, VALUE file_path, VALUE params); +extern VALUE ruby_whisper_normalize_model_path(VALUE model_path); +extern parsed_samples_t parse_samples(VALUE *samples, VALUE *n_samples); +extern VALUE release_samples(VALUE parsed); + +extern VALUE ruby_whisper_vad_segments_s_init(struct whisper_vad_segments *segments); + +static size_t +ruby_whisper_vad_context_memsize(const void *p) +{ + const ruby_whisper_vad_context *rwvc = p; + size_t size = sizeof(rwvc); + if (!rwvc) { + return 0; + } + if (rwvc->context) { + size += sizeof(rwvc->context); + } + return size; +} + +static void +ruby_whisper_vad_context_free(void *p) +{ + ruby_whisper_vad_context *rwvc = (ruby_whisper_vad_context *)p; + if (rwvc->context) { + whisper_vad_free(rwvc->context); + rwvc->context = NULL; + } + xfree(rwvc); +} + +const rb_data_type_t ruby_whisper_vad_context_type = { + "ruby_whisper_vad_context", + {0, ruby_whisper_vad_context_free, ruby_whisper_vad_context_memsize,}, + 0, 0, + 0 +}; + +static VALUE +ruby_whisper_vad_context_s_allocate(VALUE klass) +{ + ruby_whisper_vad_context *rwvc; + VALUE obj = TypedData_Make_Struct(klass, ruby_whisper_vad_context, &ruby_whisper_vad_context_type, rwvc); + rwvc->context = NULL; + return obj; +} + +static VALUE +ruby_whisper_vad_context_initialize(VALUE self, VALUE model_path) +{ + ruby_whisper_vad_context *rwvc; + struct whisper_vad_context *context; + + model_path = ruby_whisper_normalize_model_path(model_path); + context = whisper_vad_init_from_file_with_params(StringValueCStr(model_path), whisper_vad_default_context_params()); + if (context == NULL) { + rb_raise(rb_eRuntimeError, "Failed to initialize whisper VAD context"); + } + TypedData_Get_Struct(self, ruby_whisper_vad_context, &ruby_whisper_vad_context_type, rwvc); + rwvc->context = context; + + return Qnil; +} + +typedef struct { + struct whisper_vad_context *context; + struct whisper_vad_params *params; + float *samples; + int n_samples; + struct whisper_vad_segments *result; +} segments_from_samples_without_gvl_args; + +static void* +segments_from_samples_without_gvl(void *rb_args) +{ + segments_from_samples_without_gvl_args *args = (segments_from_samples_without_gvl_args *)rb_args; + args->result = whisper_vad_segments_from_samples(args->context, *args->params, args->samples, args->n_samples); + + return NULL; +} + +VALUE +segments_from_samples_body(VALUE rb_args) +{ + segments_from_samples_args *args = (segments_from_samples_args *)rb_args; + + ruby_whisper_vad_context *rwvc; + ruby_whisper_vad_params *rwvp; + GetVADContext(*args->context, rwvc); + GetVADParams(*args->params, rwvp); + + segments_from_samples_without_gvl_args segments_from_samples_without_gvl_args = { + rwvc->context, + &rwvp->params, + args->samples, + args->n_samples, + NULL + }; + rb_thread_call_without_gvl(segments_from_samples_without_gvl, (void *)&segments_from_samples_without_gvl_args, NULL, NULL); + if (!segments_from_samples_without_gvl_args.result) { + rb_raise(rb_eRuntimeError, "Failed to process audio"); + } + + return ruby_whisper_vad_segments_s_init(segments_from_samples_without_gvl_args.result); +} + +static VALUE +ruby_whisper_vad_segments_from_samples(int argc, VALUE *argv, VALUE self) +{ + if (argc < 2 || argc > 3) { + rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 2..3)", argc); + } + + VALUE n_samples = argc == 2 ? Qnil : argv[2]; + struct parsed_samples_t parsed = parse_samples(&argv[1], &n_samples); + segments_from_samples_args args = { + &self, + &argv[0], + parsed.samples, + parsed.n_samples, + }; + VALUE segments = rb_ensure(segments_from_samples_body, (VALUE)&args, release_samples, (VALUE)&parsed); + + return segments; +} + +static VALUE +ruby_whisper_vad_free(VALUE self) +{ + ruby_whisper_vad_context *rwvc; + GetVADContext(self, rwvc); + whisper_vad_free(rwvc->context); + rwvc->context = NULL; + + return Qnil; +} + +void init_ruby_whisper_vad_context(VALUE *mVAD) +{ + cVADContext = rb_define_class_under(*mVAD, "Context", rb_cObject); + rb_define_alloc_func(cVADContext, ruby_whisper_vad_context_s_allocate); + rb_define_method(cVADContext, "initialize", ruby_whisper_vad_context_initialize, 1); + rb_define_method(cVADContext, "segments_from_samples", ruby_whisper_vad_segments_from_samples, -1); + rb_define_method(cVADContext, "detect", ruby_whisper_vad_detect, 2); + rb_define_method(cVADContext, "free", ruby_whisper_vad_free, 0); +} diff --git a/bindings/ruby/ext/ruby_whisper_vad_context_detect.cpp b/bindings/ruby/ext/ruby_whisper_vad_context_detect.cpp new file mode 100644 index 0000000..862da81 --- /dev/null +++ b/bindings/ruby/ext/ruby_whisper_vad_context_detect.cpp @@ -0,0 +1,50 @@ +#include "ruby_whisper.h" +#include "common-whisper.h" +#include <string> +#include <vector> + +#ifdef __cplusplus +extern "C" { +#endif + +extern ID id_to_path; + +extern VALUE cVADSegments; + +extern const rb_data_type_t ruby_whisper_vad_context_type; +extern const rb_data_type_t ruby_whisper_vad_params_type; +extern const rb_data_type_t ruby_whisper_vad_segments_type; + +extern VALUE ruby_whisper_vad_segments_s_init(struct whisper_vad_segments *segments); +extern VALUE segments_from_samples_body(VALUE rb_args); + +VALUE +ruby_whisper_vad_detect(VALUE self, VALUE file_path, VALUE params) { + ruby_whisper_vad_context *rwvc; + std::string cpp_file_path; + std::vector<float> pcmf32; + std::vector<std::vector<float>> pcmf32s; + + GetVADContext(self, rwvc); + + if (rb_respond_to(file_path, id_to_path)) { + file_path = rb_funcall(file_path, id_to_path, 0); + } + cpp_file_path = StringValueCStr(file_path); + + if (!read_audio_data(cpp_file_path, pcmf32, pcmf32s, false)) { + rb_raise(rb_eRuntimeError, "Failed to open '%s' as WAV file\n", cpp_file_path.c_str()); + } + + segments_from_samples_args args = { + &self, + ¶ms, + pcmf32.data(), + (int)pcmf32.size(), + }; + return segments_from_samples_body((VALUE)&args); +} + +#ifdef __cplusplus +} +#endif diff --git a/bindings/ruby/ext/ruby_whisper_vad_params.c b/bindings/ruby/ext/ruby_whisper_vad_params.c new file mode 100644 index 0000000..edcc25d --- /dev/null +++ b/bindings/ruby/ext/ruby_whisper_vad_params.c @@ -0,0 +1,287 @@ +#include "ruby_whisper.h" + +#define DEFINE_PARAM(param_name, nth) \ + id_ ## param_name = rb_intern(#param_name); \ + param_names[nth] = id_ ## param_name; \ + rb_define_method(cVADParams, #param_name, ruby_whisper_vad_params_get_ ## param_name, 0); \ + rb_define_method(cVADParams, #param_name "=", ruby_whisper_vad_params_set_ ## param_name, 1); + +#define NUM_PARAMS 6 + +extern VALUE cVADParams; + +static size_t +ruby_whisper_vad_params_memsize(const void *p) +{ + const struct ruby_whisper_vad_params *params = p; + size_t size = sizeof(params); + if (!params) { + return 0; + } + return size; +} + +static ID param_names[NUM_PARAMS]; +static ID id_threshold; +static ID id_min_speech_duration_ms; +static ID id_min_silence_duration_ms; +static ID id_max_speech_duration_s; +static ID id_speech_pad_ms; +static ID id_samples_overlap; + +const rb_data_type_t ruby_whisper_vad_params_type = { + "ruby_whisper_vad_params", + {0, RUBY_DEFAULT_FREE, ruby_whisper_vad_params_memsize,}, + 0, 0, + 0 +}; + +static VALUE +ruby_whisper_vad_params_s_allocate(VALUE klass) +{ + ruby_whisper_vad_params *rwvp; + VALUE obj = TypedData_Make_Struct(klass, ruby_whisper_vad_params, &ruby_whisper_vad_params_type, rwvp); + rwvp->params = whisper_vad_default_params(); + return obj; +} + +/* + * Probability threshold to consider as speech. + * + * call-seq: + * threshold = th -> th + */ +static VALUE +ruby_whisper_vad_params_set_threshold(VALUE self, VALUE value) +{ + ruby_whisper_vad_params *rwvp; + TypedData_Get_Struct(self, ruby_whisper_vad_params, &ruby_whisper_vad_params_type, rwvp); + rwvp->params.threshold = NUM2DBL(value); + return value; +} + +static VALUE +ruby_whisper_vad_params_get_threshold(VALUE self) +{ + ruby_whisper_vad_params *rwvp; + TypedData_Get_Struct(self, ruby_whisper_vad_params, &ruby_whisper_vad_params_type, rwvp); + return DBL2NUM(rwvp->params.threshold); +} + +/* + * Min duration for a valid speech segment. + * + * call-seq: + * min_speech_duration_ms = duration_ms -> duration_ms + */ +static VALUE +ruby_whisper_vad_params_set_min_speech_duration_ms(VALUE self, VALUE value) +{ + ruby_whisper_vad_params *rwvp; + TypedData_Get_Struct(self, ruby_whisper_vad_params, &ruby_whisper_vad_params_type, rwvp); + rwvp->params.min_speech_duration_ms = NUM2INT(value); + return value; +} + +static VALUE +ruby_whisper_vad_params_get_min_speech_duration_ms(VALUE self) +{ + ruby_whisper_vad_params *rwvp; + TypedData_Get_Struct(self, ruby_whisper_vad_params, &ruby_whisper_vad_params_type, rwvp); + return INT2NUM(rwvp->params.min_speech_duration_ms); +} + +/* + * Min silence duration to consider speech as ended. + * + * call-seq: + * min_silence_duration_ms = duration_ms -> duration_ms + */ +static VALUE +ruby_whisper_vad_params_set_min_silence_duration_ms(VALUE self, VALUE value) +{ + ruby_whisper_vad_params *rwvp; + TypedData_Get_Struct(self, ruby_whisper_vad_params, &ruby_whisper_vad_params_type, rwvp); + rwvp->params.min_silence_duration_ms = NUM2INT(value); + return value; +} + +static VALUE +ruby_whisper_vad_params_get_min_silence_duration_ms(VALUE self) +{ + ruby_whisper_vad_params *rwvp; + TypedData_Get_Struct(self, ruby_whisper_vad_params, &ruby_whisper_vad_params_type, rwvp); + return INT2NUM(rwvp->params.min_silence_duration_ms); +} + +/* + * Max duration of a speech segment before forcing a new segment. + * + * call-seq: + * max_speech_duration_s = duration_s -> duration_s + */ +static VALUE +ruby_whisper_vad_params_set_max_speech_duration_s(VALUE self, VALUE value) +{ + ruby_whisper_vad_params *rwvp; + TypedData_Get_Struct(self, ruby_whisper_vad_params, &ruby_whisper_vad_params_type, rwvp); + rwvp->params.max_speech_duration_s = NUM2DBL(value); + return value; +} + +static VALUE +ruby_whisper_vad_params_get_max_speech_duration_s(VALUE self) +{ + ruby_whisper_vad_params *rwvp; + TypedData_Get_Struct(self, ruby_whisper_vad_params, &ruby_whisper_vad_params_type, rwvp); + return DBL2NUM(rwvp->params.max_speech_duration_s); +} + +/* + * Padding added before and after speech segments. + * + * call-seq: + * speech_pad_ms = pad_ms -> pad_ms + */ +static VALUE +ruby_whisper_vad_params_set_speech_pad_ms(VALUE self, VALUE value) +{ + ruby_whisper_vad_params *rwvp; + TypedData_Get_Struct(self, ruby_whisper_vad_params, &ruby_whisper_vad_params_type, rwvp); + rwvp->params.speech_pad_ms = NUM2INT(value); + return value; +} + +static VALUE +ruby_whisper_vad_params_get_speech_pad_ms(VALUE self) +{ + ruby_whisper_vad_params *rwvp; + TypedData_Get_Struct(self, ruby_whisper_vad_params, &ruby_whisper_vad_params_type, rwvp); + return INT2NUM(rwvp->params.speech_pad_ms); +} + +/* + * Overlap in seconds when copying audio samples from speech segment. + * + * call-seq: + * samples_overlap = overlap -> overlap + */ +static VALUE +ruby_whisper_vad_params_set_samples_overlap(VALUE self, VALUE value) +{ + ruby_whisper_vad_params *rwvp; + TypedData_Get_Struct(self, ruby_whisper_vad_params, &ruby_whisper_vad_params_type, rwvp); + rwvp->params.samples_overlap = NUM2DBL(value); + return value; +} + +static VALUE +ruby_whisper_vad_params_get_samples_overlap(VALUE self) +{ + ruby_whisper_vad_params *rwvp; + TypedData_Get_Struct(self, ruby_whisper_vad_params, &ruby_whisper_vad_params_type, rwvp); + return DBL2NUM(rwvp->params.samples_overlap); +} + +static VALUE +ruby_whisper_vad_params_equal(VALUE self, VALUE other) +{ + ruby_whisper_vad_params *rwvp1; + ruby_whisper_vad_params *rwvp2; + + if (self == other) { + return Qtrue; + } + + if (!rb_obj_is_kind_of(other, cVADParams)) { + return Qfalse; + } + + TypedData_Get_Struct(self, ruby_whisper_vad_params, &ruby_whisper_vad_params_type, rwvp1); + TypedData_Get_Struct(other, ruby_whisper_vad_params, &ruby_whisper_vad_params_type, rwvp2); + + if (rwvp1->params.threshold != rwvp2->params.threshold) { + return Qfalse; + } + if (rwvp1->params.min_speech_duration_ms != rwvp2->params.min_speech_duration_ms) { + return Qfalse; + } + if (rwvp1->params.min_silence_duration_ms != rwvp2->params.min_silence_duration_ms) { + return Qfalse; + } + if (rwvp1->params.max_speech_duration_s != rwvp2->params.max_speech_duration_s) { + return Qfalse; + } + if (rwvp1->params.speech_pad_ms != rwvp2->params.speech_pad_ms) { + return Qfalse; + } + if (rwvp1->params.samples_overlap != rwvp2->params.samples_overlap) { + return Qfalse; + } + + return Qtrue; +} + +#define SET_PARAM_IF_SAME(param_name) \ + if (id == id_ ## param_name) { \ + ruby_whisper_vad_params_set_ ## param_name(self, value); \ + continue; \ + } + +VALUE +ruby_whisper_vad_params_initialize(int argc, VALUE *argv, VALUE self) +{ + VALUE kw_hash; + VALUE values[NUM_PARAMS] = {Qundef}; + VALUE value; + ruby_whisper_vad_params *rwvp; + ID id; + int i; + + TypedData_Get_Struct(self, ruby_whisper_vad_params, &ruby_whisper_vad_params_type, rwvp); + + rb_scan_args_kw(RB_SCAN_ARGS_KEYWORDS, argc, argv, ":", &kw_hash); + if (NIL_P(kw_hash)) { + return self; + } + + rb_get_kwargs(kw_hash, param_names, 0, NUM_PARAMS, values); + + for (i = 0; i < NUM_PARAMS; i++) { + id = param_names[i]; + value = values[i]; + if (value == Qundef) { + continue; + } + SET_PARAM_IF_SAME(threshold) + SET_PARAM_IF_SAME(min_speech_duration_ms) + SET_PARAM_IF_SAME(min_silence_duration_ms) + SET_PARAM_IF_SAME(max_speech_duration_s) + SET_PARAM_IF_SAME(speech_pad_ms) + SET_PARAM_IF_SAME(samples_overlap) + } + + return self; +} + +#undef SET_PARAM_IF_SAME + +void +init_ruby_whisper_vad_params(VALUE *mVAD) +{ + cVADParams = rb_define_class_under(*mVAD, "Params", rb_cObject); + rb_define_alloc_func(cVADParams, ruby_whisper_vad_params_s_allocate); + rb_define_method(cVADParams, "initialize", ruby_whisper_vad_params_initialize, -1); + + DEFINE_PARAM(threshold, 0) + DEFINE_PARAM(min_speech_duration_ms, 1) + DEFINE_PARAM(min_silence_duration_ms, 2) + DEFINE_PARAM(max_speech_duration_s, 3) + DEFINE_PARAM(speech_pad_ms, 4) + DEFINE_PARAM(samples_overlap, 5) + + rb_define_method(cVADParams, "==", ruby_whisper_vad_params_equal, 1); +} + +#undef DEFINE_PARAM +#undef NUM_PARAMS diff --git a/bindings/ruby/ext/ruby_whisper_vad_segment.c b/bindings/ruby/ext/ruby_whisper_vad_segment.c new file mode 100644 index 0000000..0db6db6 --- /dev/null +++ b/bindings/ruby/ext/ruby_whisper_vad_segment.c @@ -0,0 +1,138 @@ +#include "ruby_whisper.h" + +#define N_KEY_NAMES 2 + +extern VALUE cVADSegment; + +extern const rb_data_type_t ruby_whisper_vad_segments_type; + +static VALUE sym_start_time; +static VALUE sym_end_time; + +static void +rb_whisper_vad_segment_mark(void *p) +{ + ruby_whisper_vad_segment *rwvs = (ruby_whisper_vad_segment *)p; + rb_gc_mark(rwvs->segments); +} + +static size_t +ruby_whisper_vad_segment_memsize(const void *p) +{ + const ruby_whisper_vad_segment *rwvs = p; + size_t size = sizeof(rwvs); + if (!rwvs) { + return 0; + } + if (rwvs->index) { + size += sizeof(rwvs->index); + } + return size; +} + +static const rb_data_type_t ruby_whisper_vad_segment_type = { + "ruby_whisper_vad_segment", + {rb_whisper_vad_segment_mark, RUBY_DEFAULT_FREE, ruby_whisper_vad_segment_memsize,}, + 0, 0, + 0 +}; + +static VALUE +ruby_whisper_vad_segment_s_allocate(VALUE klass) +{ + ruby_whisper_vad_segment *rwvs; + VALUE obj = TypedData_Make_Struct(klass, ruby_whisper_vad_segment, &ruby_whisper_vad_segment_type, rwvs); + rwvs->segments = Qnil; + rwvs->index = -1; + return obj; +} + +VALUE +rb_whisper_vad_segment_s_new(VALUE segments, int index) +{ + ruby_whisper_vad_segment *rwvs; + const VALUE segment = ruby_whisper_vad_segment_s_allocate(cVADSegment); + TypedData_Get_Struct(segment, ruby_whisper_vad_segment, &ruby_whisper_vad_segment_type, rwvs); + rwvs->segments = segments; + rwvs->index = index; + return segment; +} + +static VALUE +ruby_whisper_vad_segment_get_start_time(VALUE self) +{ + ruby_whisper_vad_segment *rwvs; + ruby_whisper_vad_segments *rwvss; + float t0; + + TypedData_Get_Struct(self, ruby_whisper_vad_segment, &ruby_whisper_vad_segment_type, rwvs); + TypedData_Get_Struct(rwvs->segments, ruby_whisper_vad_segments, &ruby_whisper_vad_segments_type, rwvss); + t0 = whisper_vad_segments_get_segment_t0(rwvss->segments, rwvs->index); + return LONG2NUM(t0 * 10); +} + +static VALUE +ruby_whisper_vad_segment_get_end_time(VALUE self) +{ + ruby_whisper_vad_segment *rwvs; + ruby_whisper_vad_segments *rwvss; + float t1; + + TypedData_Get_Struct(self, ruby_whisper_vad_segment, &ruby_whisper_vad_segment_type, rwvs); + TypedData_Get_Struct(rwvs->segments, ruby_whisper_vad_segments, &ruby_whisper_vad_segments_type, rwvss); + t1 = whisper_vad_segments_get_segment_t1(rwvss->segments, rwvs->index); + return LONG2NUM(t1 * 10); +} + +static VALUE +ruby_whisper_vad_segment_deconstruct_keys(VALUE self, VALUE keys) +{ + ruby_whisper_vad_segment *rwvs; + ruby_whisper_vad_segments *rwvss; + VALUE hash, key; + long n_keys; + int i; + + TypedData_Get_Struct(self, ruby_whisper_vad_segment, &ruby_whisper_vad_segment_type, rwvs); + TypedData_Get_Struct(rwvs->segments, ruby_whisper_vad_segments, &ruby_whisper_vad_segments_type, rwvss); + + hash = rb_hash_new(); + if (NIL_P(keys)) { + keys = rb_ary_new3( + N_KEY_NAMES, + sym_start_time, + sym_end_time + ); + n_keys = N_KEY_NAMES; + } else { + n_keys = RARRAY_LEN(keys); + if (n_keys > N_KEY_NAMES) { + return hash; + } + } + for (i = 0; i < n_keys; i++) { + key = rb_ary_entry(keys, i); + if (key == sym_start_time) { + rb_hash_aset(hash, key, ruby_whisper_vad_segment_get_start_time(self)); + } + if (key == sym_end_time) { + rb_hash_aset(hash, key, ruby_whisper_vad_segment_get_end_time(self)); + } + } + + return hash; +} + +void +init_ruby_whisper_vad_segment(VALUE *mVAD) +{ + cVADSegment = rb_define_class_under(*mVAD, "Segment", rb_cObject); + + sym_start_time = ID2SYM(rb_intern("start_time")); + sym_end_time = ID2SYM(rb_intern("end_time")); + + rb_define_alloc_func(cVADSegment, ruby_whisper_vad_segment_s_allocate); + rb_define_method(cVADSegment, "start_time", ruby_whisper_vad_segment_get_start_time, 0); + rb_define_method(cVADSegment, "end_time", ruby_whisper_vad_segment_get_end_time, 0); + rb_define_method(cVADSegment, "deconstruct_keys", ruby_whisper_vad_segment_deconstruct_keys, 1); +} diff --git a/bindings/ruby/ext/ruby_whisper_vad_segments.c b/bindings/ruby/ext/ruby_whisper_vad_segments.c new file mode 100644 index 0000000..db62fdb --- /dev/null +++ b/bindings/ruby/ext/ruby_whisper_vad_segments.c @@ -0,0 +1,105 @@ +#include "ruby_whisper.h" + +extern ID id___method__; +extern ID id_to_enum; + +extern VALUE cVADSegments; + +extern VALUE rb_whisper_vad_segment_s_new(VALUE segments, int index); + +static size_t +ruby_whisper_vad_segments_memsize(const void *p) +{ + const ruby_whisper_vad_segments *rwvss = p; + size_t size = sizeof(rwvss); + if (!rwvss) { + return 0; + } + if (rwvss->segments) { + size += sizeof(rwvss->segments); + } + return size; +} + +static void +ruby_whisper_vad_segments_free(void *p) +{ + ruby_whisper_vad_segments *rwvss = (ruby_whisper_vad_segments *)p; + if (rwvss->segments) { + whisper_vad_free_segments(rwvss->segments); + rwvss->segments = NULL; + } + xfree(rwvss); +} + +const rb_data_type_t ruby_whisper_vad_segments_type = { + "ruby_whisper_vad_segments", + {0, ruby_whisper_vad_segments_free, ruby_whisper_vad_segments_memsize,}, + 0, 0, + 0 +}; + +static VALUE +ruby_whisper_vad_segments_s_allocate(VALUE klass) +{ + ruby_whisper_vad_segments *rwvss; + VALUE obj = TypedData_Make_Struct(klass, ruby_whisper_vad_segments, &ruby_whisper_vad_segments_type, rwvss); + rwvss->segments = NULL; + return obj; +} + +VALUE +ruby_whisper_vad_segments_s_init(struct whisper_vad_segments *segments) +{ + VALUE rb_segments; + ruby_whisper_vad_segments *rwvss; + + rb_segments = ruby_whisper_vad_segments_s_allocate(cVADSegments); + TypedData_Get_Struct(rb_segments, ruby_whisper_vad_segments, &ruby_whisper_vad_segments_type, rwvss); + rwvss->segments = segments; + + return rb_segments; +} + +static VALUE +ruby_whisper_vad_segments_each(VALUE self) +{ + ruby_whisper_vad_segments *rwvss; + VALUE method_name; + int n_segments, i; + + if (!rb_block_given_p()) { + method_name = rb_funcall(self, id___method__, 0); + return rb_funcall(self, id_to_enum, 1, method_name); + } + + GetVADSegments(self, rwvss); + n_segments = whisper_vad_segments_n_segments(rwvss->segments); + for (i = 0; i < n_segments; ++i) { + rb_yield(rb_whisper_vad_segment_s_new(self, i)); + } + + return self; +} + +static VALUE +ruby_whisper_vad_segments_get_length(VALUE self) +{ + ruby_whisper_vad_segments *rwvss; + int n_segments; + + GetVADSegments(self, rwvss); + n_segments = whisper_vad_segments_n_segments(rwvss->segments); + + return INT2NUM(n_segments); +} + +void +init_ruby_whisper_vad_segments(VALUE *mVAD) +{ + cVADSegments = rb_define_class_under(*mVAD, "Segments", rb_cObject); + rb_define_alloc_func(cVADSegments, ruby_whisper_vad_segments_s_allocate); + rb_define_method(cVADSegments, "each", ruby_whisper_vad_segments_each, 0); + rb_define_method(cVADSegments, "length", ruby_whisper_vad_segments_get_length, 0); + rb_include_module(cVADSegments, rb_path2class("Enumerable")); +} diff --git a/bindings/ruby/ext/sources/CMakeGraphVizOptions.cmake b/bindings/ruby/ext/sources/CMakeGraphVizOptions.cmake new file mode 100644 index 0000000..746c14b --- /dev/null +++ b/bindings/ruby/ext/sources/CMakeGraphVizOptions.cmake @@ -0,0 +1,8 @@ +set(GRAPHVIZ_EXECUTABLES FALSE) +set(GRAPHVIZ_STATIC_LIBS TRUE) +set(GRAPHVIZ_SHARED_LIBS FALSE) +set(GRAPHVIZ_MODULE_LIBS FALSE) +set(GRAPHVIZ_INTERFACE_LIBS FALSE) +set(GRAPHVIZ_OBJECT_LIBS FALSE) +set(GRAPHVIZ_UNKNOWN_LIBS FALSE) +set(GRAPHVIZ_GENERATE_DEPENDERS FALSE) diff --git a/bindings/ruby/extsources.rb b/bindings/ruby/extsources.rb new file mode 100644 index 0000000..850ac98 --- /dev/null +++ b/bindings/ruby/extsources.rb @@ -0,0 +1,57 @@ +require "pathname" + +root = Pathname("..")/".." +ignored_dirs = %w[ + .devops + .github + ci + examples/addon.node + examples/bench.wasm + examples/command + examples/command.wasm + examples/lsp + examples/main + examples/python + examples/stream + examples/stream.wasm + examples/sycl + examples/talk-llama + examples/wchess + examples/whisper.android + examples/whisper.android.java + examples/whisper.nvim + examples/whisper.objc + examples/whisper.swiftui + examples/whisper.wasm + grammars + models + samples + scripts + tests +].collect {|dir| root/dir} +ignored_files = %w[ + AUTHORS + Makefile + .gitignore + .gitmodules + .dockerignore +] +ignored_exts = %w[ + .yml + .sh + .md + .py + .js + .nvim +] + +EXTSOURCES = + `git ls-files -z #{root}`.split("\x0") + .collect {|file| Pathname(file)} + .reject {|file| + ignored_exts.include?(file.extname) || + ignored_files.include?(file.basename.to_path) || + ignored_dirs.any? {|dir| file.descend.any? {|desc| desc == dir}} || + (file.descend.to_a[1] != root && file != Pathname("..")/"javascript"/"package-tmpl.json") + } + .collect(&:to_path) diff --git a/bindings/ruby/lib/whisper/log_settable.rb b/bindings/ruby/lib/whisper/log_settable.rb new file mode 100644 index 0000000..477aa1f --- /dev/null +++ b/bindings/ruby/lib/whisper/log_settable.rb @@ -0,0 +1,33 @@ +module Whisper + module LogSettable + private + + def start_log_callback_thread + return if @log_callback_thread&.alive? + + @log_callback_thread = Thread.new { + begin + while logs = drain_logs + begin + callback, user_data = synchronize {[@log_callback, @log_callback_user_data]} + next if callback.nil? + + logs.each do |(level, text)| + callback.call level, text, user_data + end + rescue => err + $stderr.puts err + end + end + rescue => err + $stderr.puts err + end + } + end + + def synchronize(&block) + @mutex ||= Thread::Mutex.new + @mutex.synchronize &block + end + end +end diff --git a/bindings/ruby/lib/whisper/model/uri.rb b/bindings/ruby/lib/whisper/model/uri.rb new file mode 100644 index 0000000..58db376 --- /dev/null +++ b/bindings/ruby/lib/whisper/model/uri.rb @@ -0,0 +1,237 @@ +require "uri" +require "net/http" +require "time" +require "pathname" +require "io/console/size" + +module Whisper + class Model + class URI + def initialize(uri) + @uri = URI(uri) + end + + def to_path + cache + cache_path.to_path + end + + def clear_cache + path = cache_path + path.delete if path.exist? + end + + private + + def cache_path + base_cache_dir/@uri.host/@uri.path[1..] + end + + def base_cache_dir + base = case RUBY_PLATFORM + when /mswin|mingw/ + ENV.key?("LOCALAPPDATA") ? Pathname(ENV["LOCALAPPDATA"]) : Pathname(Dir.home)/"AppData/Local" + when /darwin/ + Pathname(Dir.home)/"Library/Caches" + else + ENV.key?("XDG_CACHE_HOME") ? Pathname(ENV["XDG_CACHE_HOME"]) : Pathname(Dir.home)/".cache" + end + base/"whisper.cpp" + end + + def cache + path = cache_path + return path if cache_path.exist? + + headers = {} + request @uri, headers + path + end + + def request(uri, headers) + Net::HTTP.start uri.host, uri.port, use_ssl: uri.scheme == "https" do |http| + request = Net::HTTP::Get.new(uri, headers) + http.request request do |response| + case response + when Net::HTTPOK + download response + when Net::HTTPRedirection + request URI(response["location"]), headers + else + raise "#{response.code} #{response.message}\n#{response.body}" + end + end + end + rescue => err + if cache_path.exist? + warn err + # Use cache file + else + raise + end + end + + def download(response) + path = cache_path + path.dirname.mkpath unless path.dirname.exist? + downloading_path = Pathname("#{path}.downloading") + size = response.content_length + downloading_path.open "wb" do |file| + downloaded = 0 + response.read_body do |chunk| + file << chunk + downloaded += chunk.bytesize + show_progress downloaded, size + end + $stderr.puts + end + downloading_path.rename path + end + + def show_progress(current, size) + line_size = 47 + progress_rate_available = size && $stderr.tty? && $stderr.winsize[1] >= line_size + + unless @prev + @prev = Time.now + $stderr.puts "Downloading #{@uri} to #{cache_path}" + end + + now = Time.now + + if progress_rate_available + return if now - @prev < 1 && current < size + + progress_width = 20 + progress = current.to_f / size + arrow_length = progress * progress_width + arrow = "=" * (arrow_length - 1) + ">" + " " * (progress_width - arrow_length) + line = "[#{arrow}] (#{format_bytesize(current)} / #{format_bytesize(size)})" + padding = ' ' * ($stderr.winsize[1] - line.size) + $stderr.print "\r#{line}#{padding}" + else + return if now - @prev < 1 + + $stderr.print "." + end + @prev = now + end + + def format_bytesize(bytesize) + return "0.0 B" if bytesize.zero? + + units = %w[B KiB MiB GiB TiB] + exp = (Math.log(bytesize) / Math.log(1024)).to_i + format("%.1f %s", bytesize.to_f / 1024 ** exp, units[exp]) + end + end + + class ZipURI < URI + def cache + zip_path = super + dest = unzipped_path + return if dest.exist? && dest.mtime >= zip_path.mtime + escaping dest do + system "unzip", "-q", "-d", zip_path.dirname.to_path, zip_path.to_path, exception: true + end + zip_path + end + + def clear_cache + super + unzipped_path.rmtree if unzipped_path.exist? + end + + private + + def unzipped_path + cache_path.sub_ext("") + end + + def escaping(path) + escaped = Pathname("#{path}.removing") + if path.exist? + escaped.rmtree if escaped.exist? + path.rename escaped + end + yield + ensure + if path.exist? + escaped.rmtree if escaped.exist? + else + escaped.rename path if escaped.exist? + end + end + end + + @pre_converted_models = %w[ + tiny + tiny.en + tiny-q5_1 + tiny.en-q5_1 + tiny-q8_0 + base + base.en + base-q5_1 + base.en-q5_1 + base-q8_0 + small + small.en + small-q5_1 + small.en-q5_1 + small-q8_0 + medium + medium.en + medium-q5_0 + medium.en-q5_0 + medium-q8_0 + large-v1 + large-v2 + large-v2-q5_0 + large-v2-q8_0 + large-v3 + large-v3-q5_0 + large-v3-turbo + large-v3-turbo-q5_0 + large-v3-turbo-q8_0 + ].each_with_object({}) {|name, models| + models[name] = URI.new("https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-#{name}.bin") + } + + %w[ + small.en-tdrz + ].each do |name| + @pre_converted_models[name] = URI.new("https://huggingface.co/akashmjn/tinydiarize-whisper.cpp/resolve/main/ggml-#{name}.bin") + end + + %w[ + silero-v5.1.2 + silero-v6.2.0 + ].each do |name| + @pre_converted_models[name] = URI.new("https://huggingface.co/ggml-org/whisper-vad/resolve/main/ggml-#{name}.bin") + end + + %w[ + parakeet-tdt-0.6b-v3-f16 + parakeet-tdt-0.6b-v3-f32 + parakeet-tdt-0.6b-v3-q4_0 + parakeet-tdt-0.6b-v3-q4_k + parakeet-tdt-0.6b-v3-q8_0 + ].each do |name| + @pre_converted_models[name] = URI.new("https://huggingface.co/ggml-org/parakeet-GGUF/resolve/main/ggml-#{name}.bin") + end + + @coreml_compiled_models = @pre_converted_models.each_with_object({}) {|(name, uri), models| + next if name.end_with?("-tdrz") || name.start_with?("silero-") || name.start_with?("parakeet-") + + if matched = name.match(/\A(?<name>.*)-q\d_\d\z/) + name = matched[:name] + end + models[uri] = ZipURI.new("https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-#{name}-encoder.mlmodelc.zip") + } + + class << self + attr_reader :pre_converted_models, :coreml_compiled_models + end + end +end diff --git a/bindings/ruby/lib/whisper/output.rb b/bindings/ruby/lib/whisper/output.rb new file mode 100644 index 0000000..1781af1 --- /dev/null +++ b/bindings/ruby/lib/whisper/output.rb @@ -0,0 +1,74 @@ +module Whisper + module Output + module Context + def to_srt + each_segment.with_index.reduce("") {|srt, (segment, index)| + srt << "#{index + 1}\n#{segment.to_srt_cue}\n" + } + end + + def to_webvtt + each_segment.with_index.reduce("WEBVTT\n\n") {|webvtt, (segment, index)| + webvtt << "#{index + 1}\n#{segment.to_webvtt_cue}\n" + } + end + end + + module Segment + SRT_ESCAPES = { + "&" => "&", + "<" => "<", + ">" => ">", + } + SRT_ESCAPES_RE = Regexp.union(SRT_ESCAPES.keys) + private_constant :SRT_ESCAPES, :SRT_ESCAPES_RE + + def to_srt_cue + "#{srt_start_time} --> #{srt_end_time}\n#{srt_text}\n" + end + + def to_webvtt_cue + "#{webvtt_start_time} --> #{webvtt_end_time}\n#{webvtt_text}\n" + end + + private + + def time_to_a(time) + sec, decimal_part = time.divmod(1000) + min, sec = sec.divmod(60) + hour, min = min.divmod(60) + [hour, min, sec, decimal_part] + end + + def srt_time(time) + "%02d:%02d:%02d,%03d" % time_to_a(time) + end + + def srt_start_time + srt_time(start_time) + end + + def srt_end_time + srt_time(end_time) + end + + def srt_text + text.gsub(SRT_ESCAPES_RE, SRT_ESCAPES) + end + + def webvtt_time(time) + "%02d:%02d:%02d.%03d" % time_to_a(time) + end + + def webvtt_start_time + webvtt_time(start_time) + end + + def webvtt_end_time + webvtt_time(end_time) + end + + alias webvtt_text srt_text + end + end +end diff --git a/bindings/ruby/sig/whisper.rbs b/bindings/ruby/sig/whisper.rbs new file mode 100644 index 0000000..6000641 --- /dev/null +++ b/bindings/ruby/sig/whisper.rbs @@ -0,0 +1,1036 @@ +module Whisper + interface _Samples + def length: () -> Integer + def each: { (Float) -> void } -> void + end + + type log_callback = ^(Integer level, String message, Object user_data) -> void + type new_segment_callback = ^(Whisper::Context, untyped, Integer n_new, Object user_data) -> void + type progress_callback = ^(Whisper::Context, untyped, Integer progress, Object user_data) -> void + type encoder_begin_callback = ^(Whisper::Context, untyped, Object user_data) -> void + type abort_callback = ^(Whisper::Context, untyped, Object user_data) -> boolish + + VERSION: String + LOG_LEVEL_NONE: Integer + LOG_LEVEL_INFO: Integer + LOG_LEVEL_WARN: Integer + LOG_LEVEL_ERROR: Integer + LOG_LEVEL_DEBUG: Integer + LOG_LEVEL_CONT: Integer + AHEADS_NONE: Integer + AHEADS_N_TOP_MOST: Integer + AHEADS_CUSTOM: Integer + AHEADS_TINY_EN: Integer + AHEADS_TINY: Integer + AHEADS_BASE_EN: Integer + AHEADS_BASE: Integer + AHEADS_SMALL_EN: Integer + AHEADS_SMALL: Integer + AHEADS_MEDIUM_EN: Integer + AHEADS_MEDIUM: Integer + AHEADS_LARGE_V1: Integer + AHEADS_LARGE_V2: Integer + AHEADS_LARGE_V3: Integer + AHEADS_LARGE_V3_TURBO: Integer + + def self.lang_max_id: () -> Integer + def self.lang_id: (string name) -> Integer + def self.lang_str: (Integer id) -> String + def self.lang_str_full: (Integer id) -> String + def self.log_set: (log_callback?, Object? user_data) -> log_callback + def self.system_info_str: () -> String + + module Output + module Context + def to_srt: () -> String + def to_webvtt: () -> String + end + + module Segment + def to_srt_cue: () -> String + def to_webvtt_cue: () -> String + end + end + + class Context + include Output::Context + + def self.new: (String | path | ::URI::HTTP) -> instance + + # transcribe a single file + # can emit to a block results + # + # params = Whisper::Params.new + # params.duration = 60_000 + # whisper.transcribe "path/to/audio.wav", params do |text| + # puts text + # end + # + # If `n_processors` is greater than 1, you cannot set any callbacks including + # new_segment_callback, progress_callback, encoder_begin_callback, abort_callback, + # and log_callback set by Whisper.log_set + def transcribe: (path, Whisper::Params, ?n_processors: Integer) -> self + | (path, Whisper::Params, ?n_processors: Integer) { (String) -> void } -> self + + def model_n_vocab: () -> Integer + def model_n_audio_ctx: () -> Integer + def model_n_audio_state: () -> Integer + def model_n_text_head: () -> Integer + def model_n_text_layer: () -> Integer + def model_n_mels: () -> Integer + def model_ftype: () -> Integer + def model_type: () -> String + + # Yields each Whisper::Segment: + # + # whisper.transcribe("path/to/audio.wav", params) + # whisper.each_segment do |segment| + # puts segment.text + # end + # + # Returns an `Enumerator` if no block given: + # + # whisper.transcribe("path/to/audio.wav", params) + # enum = whisper.each_segment + # enum.to_a # => [#<Whisper::Segment>, ...] + # + def each_segment: { (Segment) -> void } -> void + | () -> Enumerator[Segment] + + def model: () -> Model + def full_get_segment: (Integer nth) -> Segment + def full_n_segments: () -> Integer + + # Language ID, which can be converted to string by Whisper.lang_str and Whisper.lang_str_full. + # + def full_lang_id: () -> Integer + + # Start time of a segment indexed by `segment_index` in centiseconds (10 times milliseconds). + # + # full_get_segment_t0(3) # => 1668 (16680 ms) + # + def full_get_segment_t0: (Integer) -> Integer + + # End time of a segment indexed by `segment_index` in centiseconds (10 times milliseconds). + # + # full_get_segment_t1(3) # => 1668 (16680 ms) + # + def full_get_segment_t1: (Integer) -> Integer + + # Whether the next segment indexed by `segment_index` is predicated as a speaker turn. + # + # full_get_segment_speacker_turn_next(3) # => true + # + def full_get_segment_speaker_turn_next: (Integer) -> (true | false) + + # Text of a segment indexed by `segment_index`. + # + # full_get_segment_text(3) # => "ask not what your country can do for you, ..." + # + def full_get_segment_text: (Integer) -> String + + def full_get_segment_no_speech_prob: (Integer) -> Float + + def full_n_vad_segments: () -> Integer + + def full_get_vad_segment_t0: (Integer) -> Integer + + def full_get_vad_segment_t1: (Integer) -> Integer + + # Run the entire model: PCM -> log mel spectrogram -> encoder -> decoder -> text + # Not thread safe for same context + # Uses the specified decoding strategy to obtain the text. + # + # The second argument `samples` must be an array of samples, respond to `:length`, or be a MemoryView of an array of float. It must be 32 bit float PCM audio data. + # + def full: (Whisper::Params, Array[Float] samples, ?Integer n_samples) -> self + | (Whisper::Params, _Samples, ?Integer n_samples) -> self + + # Split the input audio in chunks and process each chunk separately using `whisper_full_with_state()` + # Result is stored in the default state of the context + # Not thread safe if executed in parallel on the same context. + # It seems this approach can offer some speedup in some cases. + # However, the transcription accuracy can be worse at the beginning and end of each chunk. + # + # If `n_processors` is greater than 1, you cannot set any callbacks including + # new_segment_callback, progress_callback, encoder_begin_callback, abort_callback, + # and log_callback set by Whisper.log_set + def full_parallel: (Whisper::Params, Array[Float], ?Integer n_samples) -> self + | (Whisper::Params, _Samples, ?Integer n_samples) -> self + | (Whisper::Params, _Samples, ?Integer? n_samples, Integer n_processors) -> self + + # Free context and unload the model. + def free: () -> void + + class Params + def self.new: ( + ?use_gpu: boolish, + ?flash_attn: boolish, + ?gpu_device: Integer, + ?dtw_token_timestamps: boolish, + ?dtw_aheads_preset: Integer, + ?dtw_n_top: Integer | nil, + ) -> instance + + def use_gpu=: (boolish) -> boolish + def use_gpu: () -> (true | false) + def flash_attn=: (boolish) -> boolish + def flash_attn: () -> (true | false) + def gpu_device=: (Integer) -> Integer + def gpu_device: () -> Integer + def dtw_token_timestamps=: (boolish) -> boolish + def dtw_token_timestamps: () -> (true | false) + def dtw_aheads_preset=: (Integer) -> Integer + def dtw_aheads_preset: () -> Integer + def dtw_n_top=: (Integer | nil) -> (Integer | nil) + def dtw_n_top: () -> (Integer | nil) + end + end + + class Params + def self.new: ( + ?language: string, + ?translate: boolish, + ?no_context: boolish, + ?single_segment: boolish, + ?print_special: boolish, + ?print_progress: boolish, + ?print_realtime: boolish, + ?print_timestamps: boolish, + ?suppress_blank: boolish, + ?suppress_nst: boolish, + ?token_timestamps: boolish, + ?max_len: Integer, + ?split_on_word: boolish, + ?initial_prompt: string | nil, + ?carry_initial_prompt: boolish, + ?diarize: boolish, + ?offset: Integer, + ?duration: Integer, + ?max_text_tokens: Integer, + ?temperature: Float, + ?max_initial_ts: Float, + ?length_penalty: Float, + ?temperature_inc: Float, + ?entropy_thold: Float, + ?logprob_thold: Float, + ?no_speech_thold: Float, + ?new_segment_callback: new_segment_callback, + ?new_segment_callback_user_data: Object, + ?progress_callback: progress_callback, + ?progress_callback_user_data: Object, + ?encoder_begin_callback: encoder_begin_callback, + ?encoder_begin_callback_user_data: Object, + ?abort_callback: abort_callback, + ?abort_callback_user_data: Object, + ?vad: boolish, + ?vad_model_path: path | URI, + ?vad_params: Whisper::VAD::Params + ) -> instance + + # params.language = "auto" | "en", etc... + # + def language=: (String) -> String # TODO: Enumerate lang names + + def language: () -> String + def translate=: (boolish) -> boolish + def translate: () -> (true | false) + def no_context=: (boolish) -> boolish + + # If `true`, does not use past transcription (if any) as initial prompt for the decoder. + # + def no_context: () -> (true | false) + + def single_segment=: (boolish) -> boolish + + # If `true`, forces single segment output (useful for streaming). + # + def single_segment: () -> (true | false) + + def print_special=: (boolish) -> boolish + + # If `true`, prints special tokens (e.g. <SOT>, <EOT>, <BEG>, etc.). + # + def print_special: () -> (true | false) + + def print_progress=: (boolish) -> boolish + + # If `true`, prints progress information. + # + def print_progress: () -> (true | false) + + def print_realtime=: (boolish) -> boolish + + # If `true`, prints results from within whisper.cpp. (avoid it, use callback instead) + # + def print_realtime: () -> (true | false) + + # If `true`, prints timestamps for each text segment when printing realtime. + # + def print_timestamps=: (boolish) -> boolish + + def print_timestamps: () -> (true | false) + + def suppress_blank=: (boolish) -> boolish + + # If `true`, suppresses blank outputs. + # + def suppress_blank: () -> (true | false) + + def suppress_nst=: (boolish) -> boolish + + # If `true`, suppresses non-speech-tokens. + # + def suppress_nst: () -> (true | false) + + def token_timestamps=: (boolish) -> boolish + + # If `true`, enables token-level timestamps. + # + def token_timestamps: () -> (true | false) + + def max_len=: (Integer) -> Integer + + # max segment length in characters. + # + def max_len: () -> Integer + + def split_on_word=: (boolish) -> boolish + + # If `true`, split on word rather than on token (when used with max_len). + # + def split_on_word: () -> (true | false) + + def initial_prompt=: (_ToS) -> _ToS + def carry_initial_prompt=: (boolish) -> boolish + + # Tokens to provide to the whisper decoder as initial prompt + # these are prepended to any existing text context from a previous call + # use whisper_tokenize() to convert text to tokens. + # Maximum of whisper_n_text_ctx()/2 tokens are used (typically 224). + # + def initial_prompt: () -> (String | nil) + def carry_initial_prompt: () -> (true | false) + + def diarize=: (boolish) -> boolish + + # If `true`, enables diarization. + # + def diarize: () -> (true | false) + + def offset=: (Integer) -> Integer + + # Start offset in ms. + # + def offset: () -> Integer + + def duration=: (Integer) -> Integer + + # Audio duration to process in ms. + # + def duration: () -> Integer + + def max_text_tokens=: (Integer) -> Integer + + # Max tokens to use from past text as prompt for the decoder. + # + def max_text_tokens: () -> Integer + + def temperature=: (Float) -> Float + def temperature: () -> Float + def max_initial_ts=: (Float) -> Float + + # See https://github.com/openai/whisper/blob/f82bc59f5ea234d4b97fb2860842ed38519f7e65/whisper/decoding.py#L97 + # + def max_initial_ts: () -> Float + + def length_penalty=: (Float) -> Float + def length_penalty: () -> Float + def temperature_inc=: (Float) -> Float + def temperature_inc: () -> Float + def entropy_thold=: (Float) -> Float + + # Similar to OpenAI's "compression_ratio_threshold" + # + def entropy_thold: () -> Float + + def logprob_thold=: (Float) -> Float + def logprob_thold: () -> Float + def no_speech_thold=: (Float) -> Float + def no_speech_thold: () -> Float + + # Sets new segment callback, called for every newly generated text segment. + # + # params.new_segment_callback = ->(context, _, n_new, user_data) { + # # ... + # } + # + def new_segment_callback=: (new_segment_callback) -> new_segment_callback + def new_segment_callback: () -> (new_segment_callback | nil) + + # Sets user data passed to the last argument of new segment callback. + # + def new_segment_callback_user_data=: (Object) -> Object + + def new_segment_callback_user_data: () -> Object + + # Sets progress callback, called on each progress update. + # + # params.new_segment_callback = ->(context, _, progress, user_data) { + # # ... + # } + # + # +progress+ is an Integer between 0 and 100. + # + def progress_callback=: (progress_callback) -> progress_callback + + def progress_callback: () -> (progress_callback | nil) + + # Sets user data passed to the last argument of progress callback. + # + def progress_callback_user_data=: (Object) -> Object + + def progress_callback_user_data: () -> Object + + # Sets encoder begin callback, called when the encoder starts. + # + def encoder_begin_callback=: (encoder_begin_callback) -> encoder_begin_callback + + def encoder_begin_callback: () -> (encoder_begin_callback | nil) + + # Sets user data passed to the last argument of encoder begin callback. + # + def encoder_begin_callback_user_data=: (Object) -> Object + + def encoder_begin_callback_user_data: () -> Object + + # Sets abort callback, called to check if the process should be aborted. + # + # params.abort_callback = ->(user_data) { + # # ... + # } + # + # + def abort_callback=: (abort_callback) -> abort_callback + + def abort_callback: () -> (abort_callback | nil) + + # Sets user data passed to the last argument of abort callback. + # + def abort_callback_user_data=: (Object) -> Object + + def abort_callback_user_data: () -> Object + + # Enable VAD + # + def vad=: (boolish) -> boolish + + def vad: () -> (true | false) + + # Path to the VAD model + def vad_model_path=: (path | URI | nil) -> (path | URI | nil) + + def vad_model_path: () -> (String | nil) + + def vad_params=: (Whisper::VAD::Params) -> Whisper::VAD::Params + def vad_params: () -> (Whisper::VAD::Params) + + # Hook called on new segment. Yields each Whisper::Segment. + # + # whisper.on_new_segment do |segment| + # # ... + # end + # + def on_new_segment: { (Segment) -> void } -> void + + # Hook called on progress update. Yields each progress `Integer` between 0 and 100. + # + def on_progress: { (Integer progress) -> void } -> void + + # Hook called on encoder starts. + # + def on_encoder_begin: { () -> void } -> void + + # Call block to determine whether abort or not. Return `true` when you want to abort. + # + # params.abort_on do + # if some_condition + # true # abort + # else + # false # continue + # end + # end + # + def abort_on: { (Object user_data) -> boolish } -> void + end + + module LogSettable + end + + class Model + def self.pre_converted_models: () -> Hash[String, Model::URI] + def self.coreml_compiled_models: () -> Hash[Model::URI, Model::ZipURI] + def self.new: () -> instance + def n_vocab: () -> Integer + def n_audio_ctx: () -> Integer + def n_audio_state: () -> Integer + def n_audio_head: () -> Integer + def n_audio_layer: () -> Integer + def n_text_ctx: () -> Integer + def n_text_state: () -> Integer + def n_text_head: () -> Integer + def n_text_layer: () -> Integer + def n_mels: () -> Integer + def ftype: () -> Integer + def type: () -> String + + class URI + def self.new: (string | ::URI::HTTP) -> instance + def to_path: -> String + def clear_cache: -> void + end + + class ZipURI < URI + def cache: () -> Pathname + def clear_cache: () -> void + end + end + + class Segment + include Output::Segment + + type deconstructed_keys = { + start_time: (Integer | nil), + end_time: (Integer | nil), + text: (String | nil), + no_speech_prob: (Float | nil), + speaker_turn_next: (true | false | nil), + n_tokens: (Integer | nil) + } + + # Start time in milliseconds. + # + def start_time: () -> Integer + + # End time in milliseconds. + # + def end_time: () -> Integer + + # Whether the next segment is predicted as a speaker turn. + # + def speaker_turn_next?: () -> (true | false) + + def text: () -> String + def no_speech_prob: () -> Float + + # Get number of tokens in the segment + # + def n_tokens: () -> Integer + + # Yields each Whisper::Token: + # + # whisper.each_segment.first.each_token do |token| + # p token + # end + # + # Returns an `Enumerator` if no block is given: + # + # whisper.each_segment.first.each_token.to_a # => [#<Whisper::Token>, ...] + # + def each_token: { (Token) -> void } -> void + | () -> Enumerator[Token] + + # Possible keys: `:start_time`, `:end_time`, `:text`, `:no_speech_prob`, `:speaker_turn_next` + # + # whisper.each_segment do |segment| + # segment => {start_time:, end_time:, text:, no_speech_prob:, speaker_turn_next:} + # + # puts "[#{start_time} --> #{end_time}] #{text} (no speech prob: #{no_speech_prob}#{speaker_turn_next ? ', speaker turns next' : ''})" + # end + def deconstruct_keys: (Array[:start_time | :end_time | :text | :no_speech_prob | :speaker_turn_next | :n_tokens] | nil) -> deconstructed_keys + end + + class Token + type deconstructed_keys = { + id: (Integer | nil), + tid: (Integer | nil), + probability: (Float | nil), + log_probability: (Float | nil), + pt: (Float | nil), + ptsum: (Float | nil), + t_dtw: (Integer | nil), + voice_length: (Float | nil), + text: (String | nil), + start_time: (Integer | nil), + end_time: (Integer | nil), + } + + # Token ID. + # + def id: () -> Integer + + # Forced timestamp token ID. + # + def tid: () -> Integer + + # Probability of the token. + # + def probability: () -> Float + + # Log probability of the token. + # + def log_probability: () -> Float + + # Probability of the timestamp token. + # + def pt: () -> Float + + # Sum of probability of all timestamp tokens. + # + def ptsum: () -> Float + + # [EXPERIMENTAL] Token-level timestamps with DTW + # + # Do not use if you haven't computed token-level timestamps with dtw. + # Roughly corresponds to the moment in audio in which the token was output. + # + def t_dtw: () -> Integer + + # Voice length of the token. + # + def voice_length: () -> Float + + # Start time of the token. + # + # Token-level timestamp data. + # Do not use if you haven't computed token-level timestamps. + # + def start_time: () -> Integer + + # End time of the token. + # + # Token-level timestamp data. + # Do not use if you haven't computed token-level timestamps. + # + def end_time: () -> Integer + + # Get the token text of the token. + # + def text: () -> String + def deconstruct_keys: (Array[:id | :tid | :probability | :log_probability | :pt | :ptsum | :t_dtw | :voice_length | :start_time | :end_time | :text] | nil) -> deconstructed_keys + end + + module Parakeet + extend LogSettable + + VERSION: String + + # Control logging output. The default behavior is to print to stderr. + # + def self.log_set: (nil, Object? user_data) -> nil + | (^(Integer level, String message, Object user_data) -> void, Object? user_data) -> nil + def self.system_info_str: () -> String + + class Context + include Output::Context + + # Load a Parakeet model from the given file path. + # + def self.new: (String | path | ::URI::HTTP, ?Params) -> instance + + # Transcribe a single audio file. + # + def transcribe: (path audio_file_path, Whisper::Parakeet::Params) -> self + + # Run the entire model: PCM -> log mel spectrogram -> encoder -> decoder -> text. + # Not thread safe for the same context. + # + # The second argument `samples` must be an array of samples, respond to `:length`, + # or be a MemoryView of an array of float. It must be 32 bit float PCM audio data. + # + def full: (Whisper::Parakeet::Params, Array[Float] samples, ?Integer n_samples) -> self + | (Whisper::Parakeet::Params, _Samples, ?Integer n_samples) -> self + + # Number of generated text segments. + # + def full_n_segments: () -> Integer + + # Start time of a segment indexed by `segment_index` in centiseconds (10 times milliseconds). + # + # full_get_segment_t0(3) # => 1668 (16680 ms) + # + def full_get_segment_t0: (Integer segment_index) -> Integer + + # End time of a segment indexed by `segment_index` in centiseconds (10 times milliseconds). + # + # full_get_segment_t1(3) # => 1668 (16680 ms) + # + def full_get_segment_t1: (Integer segment_index) -> Integer + + # Text of a segment indexed by `segment_index`. + # + # full_get_segment_text(3) # => "ask not what your country can do for you, ..." + # + def full_get_segment_text: (Integer segment_index) -> String + + # Number of tokens in the segment indexed by `segment_index`. + # + def full_n_tokens: (Integer segment_index) -> Integer + + # Text of the token indexed by `token_index` in the segment indexed by `segment_index`. + # + def full_get_token_text: (Integer segment_index, Integer token_index) -> String + + # Token id of the token indexed by `token_index` in the segment indexed by `segment_index`. + # + def full_get_token_id: (Integer segment_index, Integer token_index) -> Integer + + # Probability of the token indexed by `token_index` in the segment indexed by `segment_index`. + # + def full_get_token_p: (Integer segment_index, Integer token_index) -> Float + + # Token data of the token indexed by `token_index` in the segment indexed by `segment_index`. + # + def full_get_token_data: (Integer segment_index, Integer token_index) -> Token + + def model: () -> Model + + # Yields each Whisper::Parakeet::Segment: + # + # parakeet.transcribe("path/to/audio.wav", params) + # parakeet.each_segment do |segment| + # puts segment.text + # end + # + # Returns an `Enumerator` if no block given: + # + # parakeet.transcribe("path/to/audio.wav", params) + # enum = parakeet.each_segment + # enum.to_a # => [#<Whisper::Parakeet::Segment>, ...] + # + def each_segment: { (Segment) -> void } -> void + | () -> Enumerator[Segment] + + def free: () -> void + + class Params + def self.new: (?use_gpu: boolish, ?gpu_device: Integer) -> instance + def use_gpu: () -> boolish + def use_gpu=: (boolish) -> boolish + def gpu_device: () -> Integer + def gpu_device=: (Integer) -> Integer + end + end + + class Params + def self.new: ( + ?n_threads: Integer, + ?offset_ms: Integer, + ?duration_ms: Integer, + ?no_context: boolish, + ?audio_ctx: Integer, + ?new_segment_callback: ^(Whisper::Parakeet::Context, untyped, Integer n_new, Object user_data) -> void, + ?new_segment_callback_user_data: Object, + ?new_token_callback: ^(Whisper::Parakeet::Context, untyped, Whisper::Parakeet::Token, Object user_data) -> void, + ?new_token_callback_user_data: Object, + ?progress_callback: ^(Whisper::Parakeet::Context, untyped, Integer progress, Object user_data) -> void, + ?progress_callback_user_data: Object, + ?encoder_begin_callback: ^(Whisper::Parakeet::Context, untyped, Object user_data) -> boolish, + ?encoder_begin_callback_user_data: Object, + ?abort_callback: ^(Object user_data) -> boolish, + ?abort_callback_user_data: Object + ) -> instance + + # Number of threads to use. + # + def n_threads=: (Integer) -> Integer + def n_threads: () -> Integer + + # Start offset in ms. + # + def offset_ms=: (Integer) -> Integer + def offset_ms: () -> Integer + + # Audio duration to process in ms. + # + def duration_ms=: (Integer) -> Integer + def duration_ms: () -> Integer + + # If `true`, does not use past transcription (if any) as context. + # + def no_context=: (boolish) -> boolish + def no_context: () -> (true | false) + + # Overwrite the audio context size. `0` uses the default value. + # + def audio_ctx=: (Integer) -> Integer + def audio_ctx: () -> Integer + + # Sets new segment callback, called for every newly generated text segment. + # + # params.new_segment_callback = ->(context, _, n_new, user_data) { + # # ... + # } + # + def new_segment_callback=: (^(Whisper::Parakeet::Context, untyped, Integer n_new, Object user_data) -> void) -> (^(Whisper::Parakeet::Context, untyped, Integer n_new, Object user_data) -> void) + def new_segment_callback: () -> ((^(Whisper::Parakeet::Context, untyped, Integer n_new, Object user_data) -> void) | nil) + + # Sets user data passed to the last argument of new segment callback. + # + def new_segment_callback_user_data=: (Object?) -> Object? + def new_segment_callback_user_data: () -> Object? + + # Sets token callback, called for every newly predicted token. + # + def new_token_callback=: (^(Whisper::Parakeet::Context, untyped, Whisper::Parakeet::Token, Object user_data) -> void) -> (^(Whisper::Parakeet::Context, untyped, Whisper::Parakeet::Token, Object user_data) -> void) + def new_token_callback: () -> ((^(Whisper::Parakeet::Context, untyped, Whisper::Parakeet::Token, Object user_data) -> void) | nil) + + # Sets user data passed to the last argument of token callback. + # + def new_token_callback_user_data=: (Object?) -> Object? + def new_token_callback_user_data: () -> Object? + + # Sets progress callback, called on each progress update. + # + # +progress+ is an Integer between 0 and 100. + # + def progress_callback=: (^(Whisper::Parakeet::Context, untyped, Integer progress, Object user_data) -> void) -> (^(Whisper::Parakeet::Context, untyped, Integer progress, Object user_data) -> void) + def progress_callback: () -> ((^(Whisper::Parakeet::Context, untyped, Integer progress, Object user_data) -> void) | nil) + + # Sets user data passed to the last argument of progress callback. + # + def progress_callback_user_data=: (Object?) -> Object? + def progress_callback_user_data: () -> Object? + + # Sets encoder begin callback, called each time before the encoder starts. + # + # If it returns `false`, the computation is aborted. + # + def encoder_begin_callback=: (^(Whisper::Parakeet::Context, untyped, Object user_data) -> boolish) -> (^(Whisper::Parakeet::Context, untyped, Object user_data) -> boolish) + def encoder_begin_callback: () -> ((^(Whisper::Parakeet::Context, untyped, Object user_data) -> boolish) | nil) + + # Sets user data passed to the last argument of encoder begin callback. + # + def encoder_begin_callback_user_data=: (Object?) -> Object? + def encoder_begin_callback_user_data: () -> Object? + + # Sets abort callback, called each time before ggml computation starts. + # + def abort_callback=: (^(Object user_data) -> boolish) -> (^(Object user_data) -> boolish) + def abort_callback: () -> ((^(Object user_data) -> boolish) | nil) + + # Sets user data passed to the last argument of abort callback. + # + def abort_callback_user_data=: (Object?) -> Object? + def abort_callback_user_data: () -> Object? + + # Hook called on new segment. Yields each Whisper::Parakeet::Segment. + # + def on_new_segment: { (Segment) -> void } -> void + + # Hook called on new token. Yields each Whisper::Parakeet::Token. + # + def on_new_token: { (Token) -> void } -> void + + # Hook called on progress update. Yields each progress `Integer` between 0 and 100. + # + def on_progress: { (Integer progress) -> void } -> void + + # Hook called each time before the encoder starts. + # + def on_encoder_begin: { () -> boolish } -> void + + # Call block to determine whether abort or not. Return `true` when you want to abort. + # + def abort_on: { () -> boolish } -> void + end + + class Segment + include Output::Segment + + type deconstructed_keys = { + start_time: (Integer | nil), + end_time: (Integer | nil), + text: (String | nil) + } + + # Start time in milliseconds. + # + def start_time: () -> Integer + + # End time in milliseconds. + # + def end_time: () -> Integer + + # Text of the segment. + # + def text: () -> String + + # Yields each Whisper::Parakeet::Token: + # + # parakeet.each_segment.first.each_token do |token| + # p token + # end + # + # Returns an `Enumerator` if no block is given: + # + # parakeet.each_segment.first.each_token.to_a # => [#<Whisper::Parakeet::Token>, ...] + # + def each_token: { (Token) -> void } -> void + | () -> Enumerator[Token] + + # Possible keys: `:start_time`, `:end_time`, `:text` + # + def deconstruct_keys: (Array[:start_time | :end_time | :text] | nil) -> deconstructed_keys + end + + class Token + type deconstructed_keys = { + id: (Integer | nil), + duration_idx: (Integer | nil), + duration_value: (Integer | nil), + frame_index: (Integer | nil), + probability: (Float | nil), + log_probability: (Float | nil), + start_time: (Integer | nil), + end_time: (Integer | nil), + word_start: ((true | false) | nil), + text: (String | nil), + } + + # Token ID. + # + def id: () -> Integer + + # Index into the model's durations array. + # + def duration_idx: () -> Integer + + # Actual duration value. + # + def duration_value: () -> Integer + + # Frame index of the token. + # + def frame_index: () -> Integer + + # Probability of the token. + # + def probability: () -> Float + + # Log probability of the token. + # + def log_probability: () -> Float + + # Start time of the token in milliseconds. + # + def start_time: () -> Integer + + # End time of the token in milliseconds. + # + def end_time: () -> Integer + + # Whether this token is the start of a word. + # + def word_start?: () -> (true | false) + + # Get the token text of the token. + # + def text: () -> String + + def deconstruct_keys: (Array[:id | :duration_idx | :duration_value | :frame_index | :probability | :log_probability | :start_time | :end_time | :word_start | :text] | nil) -> deconstructed_keys + end + + class Model + def n_vocab: () -> Integer + def n_audio_ctx: () -> Integer + def n_audio_state: () -> Integer + def n_audio_head: () -> Integer + def n_audio_layer: () -> Integer + def n_mels: () -> Integer + def ftype: () -> Integer + end + end + + module VAD + class Params + def self.new: ( + ?threshold: Float, + ?min_speech_duration_ms: Integer, + ?min_silence_duration_ms: Integer, + ?max_speech_duration_s: Float, + ?speech_pad_ms: Integer, + ?samples_overlap: Float + ) -> instance + + # Probability threshold to consider as speech. + # + def threshold=: (Float) -> Float + + def threshold: () -> Float + + # Min duration for a valid speech segment. + # + def min_speech_duration_ms=: (Integer) -> Integer + + def min_speech_duration_ms: () -> Integer + + # Min silence duration to consider speech as ended. + # + def min_silence_duration_ms=: (Integer) -> Integer + + def min_silence_duration_ms: () -> Integer + + # Max duration of a speech segment before forcing a new segment. + def max_speech_duration_s=: (Float) -> Float + + def max_speech_duration_s: () -> Float + + # Padding added before and after speech segments. + # + def speech_pad_ms=: (Integer) -> Integer + + def speech_pad_ms: () -> Integer + + # Overlap in seconds when copying audio samples from speech segment. + # + def samples_overlap=: (Float) -> Float + + def samples_overlap: () -> Float + def ==: (Params) -> (true | false) + end + + class Context + def self.new: (String | path | ::URI::HTTP model_name_or_path) -> instance + def segments_from_samples: (Params, Array[Float] samples, ?Integer n_samples) -> Segments + | (Params, _Samples, ?Integer n_samples) -> Segments + def detect: (path wav_file_path, Params) -> Segments + def free: () -> void + end + + class Segments + include Enumerable[Segment] + + def each: { (Segment) -> void } -> void + | () -> Enumerator[Segment] + def length: -> Integer + end + + class Segment + type deconstructed_keys = { + start_time: (Integer | nil), + end_time: (Integer | nil), + } + + def start_time: () -> Integer + def end_time: () -> Integer + def deconstruct_keys: (Array[:start_time | :end_time] | nil) -> deconstructed_keys + end + end + + class Error < StandardError + attr_reader code: Integer + + def self.new: (Integer code) -> instance + end +end diff --git a/bindings/ruby/test/helper.rb b/bindings/ruby/test/helper.rb new file mode 100644 index 0000000..5e37ad9 --- /dev/null +++ b/bindings/ruby/test/helper.rb @@ -0,0 +1,26 @@ +require "test/unit" +require "whisper" +require_relative "jfk_reader/jfk_reader" + +class TestBase < Test::Unit::TestCase + AUDIO = File.join(__dir__, "fixtures", "jfk.wav") + + Parakeet = Whisper::Parakeet + + class << self + def whisper + return @whisper if @whisper + + @whisper = Whisper::Context.new("base.en") + params = Whisper::Params.new + params.print_timestamps = false + @whisper.transcribe(TestBase::AUDIO, params) + end + end + + private + + def whisper + self.class.whisper + end +end diff --git a/bindings/ruby/test/jfk_reader/.gitignore b/bindings/ruby/test/jfk_reader/.gitignore new file mode 100644 index 0000000..656da8d --- /dev/null +++ b/bindings/ruby/test/jfk_reader/.gitignore @@ -0,0 +1,5 @@ +Makefile +jfk_reader.o +jfk_reader.so +jfk_reader.bundle +jfk_reader.dll diff --git a/bindings/ruby/test/jfk_reader/extconf.rb b/bindings/ruby/test/jfk_reader/extconf.rb new file mode 100644 index 0000000..0d842d0 --- /dev/null +++ b/bindings/ruby/test/jfk_reader/extconf.rb @@ -0,0 +1,3 @@ +require "mkmf" + +create_makefile("jfk_reader") diff --git a/bindings/ruby/test/jfk_reader/jfk_reader.c b/bindings/ruby/test/jfk_reader/jfk_reader.c new file mode 100644 index 0000000..7c95f72 --- /dev/null +++ b/bindings/ruby/test/jfk_reader/jfk_reader.c @@ -0,0 +1,111 @@ +#include <ruby.h> +#include <ruby/memory_view.h> +#include <ruby/encoding.h> + +typedef struct { + VALUE audio_path; + int n_samples; + const char *audio_path_str; + float *data; + short *samples; +} jfk_alloc_args; + +static VALUE +jfk_reader_alloc_resources(VALUE arg) +{ + jfk_alloc_args *a = (jfk_alloc_args *)arg; + a->audio_path_str = StringValueCStr(a->audio_path); + a->data = ALLOC_N(float, a->n_samples); + a->samples = ALLOC_N(short, a->n_samples); + return Qnil; +} + +static VALUE +jfk_reader_initialize(VALUE self, VALUE audio_path) +{ + rb_iv_set(self, "audio_path", audio_path); + return Qnil; +} + +static bool +jfk_reader_get_memory_view(const VALUE obj, rb_memory_view_t *view, int flags) +{ + VALUE audio_path = rb_iv_get(obj, "audio_path"); + // n_samples is a fixed constant (not derived from user input). + const int n_samples = 176000; + + jfk_alloc_args args = { + .audio_path = audio_path, + .n_samples = n_samples, + .audio_path_str = NULL, + .data = NULL, + .samples = NULL, + }; + + int state; + rb_protect(jfk_reader_alloc_resources, (VALUE)&args, &state); + if (state) { + if (args.samples) xfree(args.samples); + if (args.data) xfree(args.data); + return false; + } + + FILE *file = fopen(args.audio_path_str, "rb"); + if (file == NULL) { + xfree(args.samples); + xfree(args.data); + return false; + } + + fseek(file, 78, SEEK_SET); + fread(args.samples, sizeof(short), n_samples, file); + fclose(file); + for (int i = 0; i < n_samples; i++) { + args.data[i] = args.samples[i] / 32768.0; + } + xfree(args.samples); + + view->obj = obj; + view->data = (void *)args.data; + view->byte_size = sizeof(float) * n_samples; + view->readonly = true; + view->format = "e"; + view->item_size = sizeof(float); + view->item_desc.components = NULL; + view->item_desc.length = 0; + view->ndim = 1; + view->shape = NULL; + view->sub_offsets = NULL; + view->private_data = NULL; + + return true; +} + +static bool +jfk_reader_release_memory_view(const VALUE obj, rb_memory_view_t *view) +{ + if (view->data) { + xfree(view->data); + view->data = NULL; + } + return true; +} + +static bool +jfk_reader_memory_view_available_p(const VALUE obj) +{ + return true; +} + +static const rb_memory_view_entry_t jfk_reader_view_entry = { + jfk_reader_get_memory_view, + jfk_reader_release_memory_view, + jfk_reader_memory_view_available_p +}; + +void Init_jfk_reader(void) +{ + VALUE cJFKReader = rb_define_class("JFKReader", rb_cObject); + rb_memory_view_register(cJFKReader, &jfk_reader_view_entry); + rb_define_method(cJFKReader, "initialize", jfk_reader_initialize, 1); +} diff --git a/bindings/ruby/test/test_callback.rb b/bindings/ruby/test/test_callback.rb new file mode 100644 index 0000000..6490c8a --- /dev/null +++ b/bindings/ruby/test/test_callback.rb @@ -0,0 +1,203 @@ +require_relative "helper" + +class TestCallback < TestBase + def setup + GC.start + @params = Whisper::Params.new + @whisper = Whisper::Context.new("base.en") + @audio = File.join(AUDIO) + end + + def test_new_segment_callback + @params.new_segment_callback = ->(context, state, n_new, user_data) { + assert_kind_of Integer, n_new + assert n_new > 0 + assert_same @whisper, context + + n_segments = context.full_n_segments + n_new.times do |i| + i_segment = n_segments - 1 + i + start_time = context.full_get_segment_t0(i_segment) * 10 + end_time = context.full_get_segment_t1(i_segment) * 10 + text = context.full_get_segment_text(i_segment) + + assert_kind_of Integer, start_time + assert start_time >= 0 + assert_kind_of Integer, end_time + assert end_time > 0 + assert_match(/ask not what your country can do for you, ask what you can do for your country/, text) if i_segment == 0 + end + } + + @whisper.transcribe(@audio, @params) + end + + def test_new_segment_callback_closure + search_word = "what" + @params.new_segment_callback = ->(context, state, n_new, user_data) { + n_segments = context.full_n_segments + n_new.times do |i| + i_segment = n_segments - 1 + i + text = context.full_get_segment_text(i_segment) + if text.include?(search_word) + t0 = context.full_get_segment_t0(i_segment) + t1 = context.full_get_segment_t1(i_segment) + raise "search word '#{search_word}' found at between #{t0} and #{t1}" + end + end + } + + assert_raise RuntimeError do + @whisper.transcribe(@audio, @params) + end + end + + def test_new_segment_callback_user_data + udata = Object.new + @params.new_segment_callback_user_data = udata + @params.new_segment_callback = ->(context, state, n_new, user_data) { + assert_same udata, user_data + } + + @whisper.transcribe(@audio, @params) + end + + def test_new_segment_callback_user_data_gc + @params.new_segment_callback_user_data = "My user data" + @params.new_segment_callback = ->(context, state, n_new, user_data) { + assert_equal "My user data", user_data + } + GC.start + + assert_same @whisper, @whisper.transcribe(@audio, @params) + end + + def test_progress_callback + first = nil + last = nil + @params.progress_callback = ->(context, state, progress, user_data) { + assert_kind_of Integer, progress + assert 0 <= progress && progress <= 100 + assert_same @whisper, context + first = progress if first.nil? + last = progress + } + @whisper.transcribe(@audio, @params) + assert_equal 0, first + assert_equal 100, last + end + + def test_progress_callback_user_data + udata = Object.new + @params.progress_callback_user_data = udata + @params.progress_callback = ->(context, state, n_new, user_data) { + assert_same udata, user_data + } + + @whisper.transcribe(@audio, @params) + end + + def test_on_progress + first = nil + last = nil + @params.on_progress do |progress| + assert_kind_of Integer, progress + assert 0 <= progress && progress <= 100 + first = progress if first.nil? + last = progress + end + @whisper.transcribe(@audio, @params) + assert_equal 0, first + assert_equal 100, last + end + + def test_encoder_begin_callback + i = 0 + @params.encoder_begin_callback = ->(context, state, user_data) { + i += 1 + } + @whisper.transcribe(@audio, @params) + assert i > 0 + end + + def test_encoder_begin_callback_abort + logs = [] + Whisper.log_set -> (level, buffer, user_data) { + logs << buffer if level == Whisper::LOG_LEVEL_ERROR + }, logs + @params.encoder_begin_callback = ->(context, state, user_data) { + return false + } + @whisper.transcribe(@audio, @params) + sleep 0.5 # wait for logs dequeued + assert_match(/encoder_begin_callback returned false - aborting/, logs.join) + Whisper.log_set ->(level, buffer, user_data) {}, nil + end + + def test_encoder_begin_callback_user_data + udata = Object.new + @params.encoder_begin_callback_user_data = udata + yielded = nil + @params.encoder_begin_callback = ->(context, state, user_data) { + yielded = user_data + } + @whisper.transcribe(@audio, @params) + assert_same udata, yielded + end + + def test_on_encoder_begin + i = 0 + @params.on_encoder_begin do + i += 1 + end + @whisper.transcribe(@audio, @params) + assert i > 0 + end + + def test_abort_callback + i = 0 + @params.abort_callback = ->(user_data) { + assert_nil user_data + i += 1 + return false + } + @whisper.transcribe(@audio, @params) + assert i > 0 + end + + def test_abort_callback_abort + i = 0 + @params.abort_callback = ->(user_data) { + i += 1 + return i == 3 + } + @whisper.transcribe(@audio, @params) + assert_equal 3, i + end + + def test_abort_callback_user_data + udata = Object.new + @params.abort_callback_user_data = udata + yielded = nil + @params.abort_callback = ->(user_data) { + yielded = user_data + } + @whisper.transcribe(@audio, @params) + assert_same udata, yielded + end + + def test_abort_on + do_abort = false + _aborted_from_callback = false + @params.on_new_segment do |segment| + do_abort = true if segment.text.match?(/ask/) + end + i = 0 + @params.abort_on do + i += 1 + do_abort + end + @whisper.transcribe(@audio, @params) + assert i > 0 + end +end diff --git a/bindings/ruby/test/test_context_params.rb b/bindings/ruby/test/test_context_params.rb new file mode 100644 index 0000000..8d19fdc --- /dev/null +++ b/bindings/ruby/test/test_context_params.rb @@ -0,0 +1,82 @@ +require_relative "helper" + +class TestContextParams < TestBase + PARAM_NAMES = [ + :use_gpu, + :flash_attn, + :gpu_device, + :dtw_token_timestamps, + :dtw_aheads_preset, + :dtw_n_top + ] + + def test_new + params = Whisper::Context::Params.new + assert_instance_of Whisper::Context::Params, params + end + + def test_attributes + params = Whisper::Context::Params.new + + assert_true params.use_gpu + params.use_gpu = false + assert_false params.use_gpu + + assert_true params.flash_attn + params.flash_attn = false + assert_false params.flash_attn + + assert_equal 0, params.gpu_device + params.gpu_device = 1 + assert_equal 1, params.gpu_device + + assert_false params.dtw_token_timestamps + params.dtw_token_timestamps = true + assert_true params.dtw_token_timestamps + + assert_equal Whisper::AHEADS_NONE, params.dtw_aheads_preset + params.dtw_aheads_preset =Whisper::AHEADS_BASE + assert_equal Whisper::AHEADS_BASE, params.dtw_aheads_preset + + assert_nil params.dtw_n_top + params.dtw_n_top = 6 + assert_equal 6, params.dtw_n_top + params.dtw_n_top = nil + assert_nil params.dtw_n_top + end + + def test_new_with_kw_args + params = Whisper::Context::Params.new(use_gpu: false) + assert_false params.use_gpu + end + + def test_new_with_kw_wargs_non_existent + assert_raise ArgumentError do + Whisper::Context::Params.new(non_existent: "value") + end + end + + data(PARAM_NAMES.collect {|param| [param, param]}.to_h) + def test_new_with_kw_args_default_values(param) + default_params = Whisper::Context::Params.new + default_value = default_params.send(param) + value = if param == :dtw_n_top + 6 + else + case default_value + in true | false + !default_value + in Integer + default_value + 1 + end + end + params = Whisper::Context::Params.new(param => value) + assert_equal value, params.send(param) + + PARAM_NAMES.reject {|name| name == param}.each do |name| + expected = default_params.send(name) + actual = params.send(name) + assert_equal expected, actual + end + end +end diff --git a/bindings/ruby/test/test_error.rb b/bindings/ruby/test/test_error.rb new file mode 100644 index 0000000..2f28849 --- /dev/null +++ b/bindings/ruby/test/test_error.rb @@ -0,0 +1,20 @@ +require_relative "helper" + +class TestError < TestBase + def test_error + error = Whisper::Error.new(-2) + assert_equal "failed to compute log mel spectrogram", error.message + assert_equal(-2, error.code) + end + + def test_unknown_error + error = Whisper::Error.new(-20) + assert_equal "unknown error", error.message + end + + def test_non_int_code + assert_raise TypeError do + _error = Whisper::Error.new("non int") + end + end +end diff --git a/bindings/ruby/test/test_model.rb b/bindings/ruby/test/test_model.rb new file mode 100644 index 0000000..5648fc3 --- /dev/null +++ b/bindings/ruby/test/test_model.rb @@ -0,0 +1,118 @@ +require_relative "helper" +require "pathname" + +class TestModel < TestBase + def test_model + whisper = Whisper::Context.new("base.en") + assert_instance_of Whisper::Model, whisper.model + end + + def test_attributes + whisper = Whisper::Context.new("base.en") + model = whisper.model + + assert_equal 51864, model.n_vocab + assert_equal 1500, model.n_audio_ctx + assert_equal 512, model.n_audio_state + assert_equal 8, model.n_audio_head + assert_equal 6, model.n_audio_layer + assert_equal 448, model.n_text_ctx + assert_equal 512, model.n_text_state + assert_equal 8, model.n_text_head + assert_equal 6, model.n_text_layer + assert_equal 80, model.n_mels + assert_equal 1, model.ftype + assert_equal "base", model.type + end + + def test_gc + model = Whisper::Context.new("base.en").model + GC.start + + assert_equal 51864, model.n_vocab + assert_equal 1500, model.n_audio_ctx + assert_equal 512, model.n_audio_state + assert_equal 8, model.n_audio_head + assert_equal 6, model.n_audio_layer + assert_equal 448, model.n_text_ctx + assert_equal 512, model.n_text_state + assert_equal 8, model.n_text_head + assert_equal 6, model.n_text_layer + assert_equal 80, model.n_mels + assert_equal 1, model.ftype + assert_equal "base", model.type + end + + def test_pathname + path = Pathname(Whisper::Model.pre_converted_models["base.en"].to_path) + whisper = Whisper::Context.new(path) + model = whisper.model + + assert_equal 51864, model.n_vocab + assert_equal 1500, model.n_audio_ctx + assert_equal 512, model.n_audio_state + assert_equal 8, model.n_audio_head + assert_equal 6, model.n_audio_layer + assert_equal 448, model.n_text_ctx + assert_equal 512, model.n_text_state + assert_equal 8, model.n_text_head + assert_equal 6, model.n_text_layer + assert_equal 80, model.n_mels + assert_equal 1, model.ftype + assert_equal "base", model.type + end + + def test_auto_download + path = Whisper::Model.pre_converted_models["base.en"].to_path + + assert_path_exist path + assert_equal 147964211, File.size(path) + end + + def test_uri_string + path = "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.en.bin" + whisper = Whisper::Context.new(path) + model = whisper.model + + assert_equal 51864, model.n_vocab + assert_equal 1500, model.n_audio_ctx + assert_equal 512, model.n_audio_state + assert_equal 8, model.n_audio_head + assert_equal 6, model.n_audio_layer + assert_equal 448, model.n_text_ctx + assert_equal 512, model.n_text_state + assert_equal 8, model.n_text_head + assert_equal 6, model.n_text_layer + assert_equal 80, model.n_mels + assert_equal 1, model.ftype + assert_equal "base", model.type + end + + def test_uri + path = URI("https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.en.bin") + whisper = Whisper::Context.new(path) + model = whisper.model + + assert_equal 51864, model.n_vocab + assert_equal 1500, model.n_audio_ctx + assert_equal 512, model.n_audio_state + assert_equal 8, model.n_audio_head + assert_equal 6, model.n_audio_layer + assert_equal 448, model.n_text_ctx + assert_equal 512, model.n_text_state + assert_equal 8, model.n_text_head + assert_equal 6, model.n_text_layer + assert_equal 80, model.n_mels + assert_equal 1, model.ftype + assert_equal "base", model.type + end + + def test_coreml_model_auto_download + uri = Whisper::Model.coreml_compiled_models[Whisper::Model.pre_converted_models["tiny"]] + model_path = Pathname(uri.to_path).sub_ext("") + model_path.rmtree if model_path.exist? + + uri.cache + assert_path_exist model_path + end +end diff --git a/bindings/ruby/test/test_package.rb b/bindings/ruby/test/test_package.rb new file mode 100644 index 0000000..f99012c --- /dev/null +++ b/bindings/ruby/test/test_package.rb @@ -0,0 +1,52 @@ +require_relative "helper" +require 'tempfile' +require 'tmpdir' +require 'open3' + +class TestPackage < TestBase + def test_build + Tempfile.create do |file| + assert system("gem", "build", "whispercpp.gemspec", "--output", file.to_path, exception: true) + assert file.size > 0 + assert_path_exist file.to_path + end + end + + sub_test_case "Building binary on installation" do + def setup + system "rake", "build", exception: true + end + + def test_install + gemspec = Gem::Specification.load("whispercpp.gemspec") + Dir.mktmpdir do |dir| + system "gem", "install", "--install-dir", dir, "--no-document", File.join("pkg", gemspec.file_name), exception: true + assert_installed dir, gemspec.version + end + end + + def test_install_with_coreml + omit_unless RUBY_PLATFORM.match?(/darwin/) do + gemspec = Gem::Specification.load("whispercpp.gemspec") + Dir.mktmpdir do |dir| + system "gem", "install", "--install-dir", dir, "--no-document", File.join("pkg", gemspec.file_name), "--", "--enable-whisper-coreml", exception: true + assert_installed dir, gemspec.version + libdir = File.join(dir, "gems", "#{gemspec.name}-#{gemspec.version}", "lib") + assert_nothing_raised do + system "ruby", "-I", libdir, "-r", "whisper", "-e", "Whisper::Context.new('tiny')", exception: true + end + output, status = Open3.capture2("ruby", "-I", libdir, "-r", "whisper", "-e", "puts Whisper.system_info_str") + assert_match /COREML = 1/, output + end + end + end + + private + + def assert_installed(dir, version) + assert_path_exist File.join(dir, "gems/whispercpp-#{version}/lib", "whisper.#{RbConfig::CONFIG["DLEXT"]}") + assert_path_exist File.join(dir, "gems/whispercpp-#{version}/LICENSE") + assert_path_not_exist File.join(dir, "gems/whispercpp-#{version}/ext/build") + end + end +end diff --git a/bindings/ruby/test/test_parakeet.rb b/bindings/ruby/test/test_parakeet.rb new file mode 100644 index 0000000..bfd5707 --- /dev/null +++ b/bindings/ruby/test/test_parakeet.rb @@ -0,0 +1,28 @@ +require_relative "helper" +require "stringio" + +class TestParakeet < TestBase + def test_log_set + log_callback = Parakeet.instance_variable_get("@log_callback") + user_data = Parakeet.instance_variable_get("@log_callback_user_data") + + $stdout = StringIO.new + Parakeet.log_set proc {|level, message, _| puts [level, message].join(": ")}, nil + Parakeet::Context.new("test/fixtures/for-tests-ggml-parakeet-tdt.bin") + sleep 0.1 + $stdout.rewind + logs = $stdout.string + assert_match /loading model from/, logs + ensure + $stdout = STDOUT + Parakeet.log_set log_callback, user_data + end + + def test_system_info_str + assert_match /\APARAKEET : /, Parakeet.system_info_str + end + + def test_version + assert_instance_of String, Parakeet::VERSION + end +end diff --git a/bindings/ruby/test/test_parakeet_callback.rb b/bindings/ruby/test/test_parakeet_callback.rb new file mode 100644 index 0000000..1209e96 --- /dev/null +++ b/bindings/ruby/test/test_parakeet_callback.rb @@ -0,0 +1,107 @@ +require_relative "helper" + +class TestParakeetCallback < TestBase + def setup + omit "Skip not to download large model" if ENV["CI"] + + Whisper.instance_variable_set "@whisper", nil + GC.start + @params = Parakeet::Params.new + @parakeet = Parakeet::Context.new("parakeet-tdt-0.6b-v3-q4_0") + end + + def test_new_segment_callback + @params.new_segment_callback = ->(context, state, n_new, user_data) { + assert_kind_of Integer, n_new + assert n_new > 0 + assert_same @parakeet, context + + n_segments = context.full_n_segments + n_new.times do |i| + i_segment = n_segments - 1 + i + start_time = context.full_get_segment_t0(i_segment) * 10 + end_time = context.full_get_segment_t1(i_segment) * 10 + text = context.full_get_segment_text(i_segment) + + assert_kind_of Integer, start_time + assert start_time >= 0 + assert_kind_of Integer, end_time + assert end_time > 0 + assert_match(/ask not what your country can do for you, ask what you can do for your/, text) if i_segment == 0 + end + } + + @parakeet.transcribe AUDIO, @params + end + + def test_on_new_segment + seg = nil + index = 0 + @params.on_new_segment do |segment| + assert_instance_of Parakeet::Segment, segment + if index == 0 + seg = segment + assert_equal 0, segment.start_time + assert_match(/ask not what your country can do for you, ask what you can do for your/, segment.text) + end + index += 1 + end + @parakeet.transcribe AUDIO, @params + assert_equal 0, seg.start_time + assert_match /ask not what your country can do for you, ask what you can do for your/, seg.text + end + + def test_on_new_token + index = 0 + @params.on_new_token do |token| + assert_instance_of Parakeet::Token, token + if index == 0 + assert_instance_of Integer, token.start_time + assert_match "▁And", token.text + end + index += 1 + end + + @parakeet.transcribe AUDIO, @params + end + + def test_on_progress + first = nil + @params.on_progress do |progress| + assert_kind_of Integer, progress + assert 0 <= progress && progress <= 100 + first = progress if first.nil? + end + + @parakeet.transcribe AUDIO, @params + + assert_equal 0, first + end + + def test_on_encoder_begin + i = 0 + @params.on_encoder_begin do + i += 1 + end + + @parakeet.transcribe AUDIO, @params + + assert i > 0 + end + + def test_abort_on + do_abort = false + @params.on_new_segment do |segment| + do_abort = true if segment.text.match?(/ask/) + end + i = 0 + @params.abort_on do + i += 1 + do_abort + end + + @parakeet.transcribe(AUDIO, @params) rescue nil + + assert i > 0 + end +end diff --git a/bindings/ruby/test/test_parakeet_context.rb b/bindings/ruby/test/test_parakeet_context.rb new file mode 100644 index 0000000..cef6b14 --- /dev/null +++ b/bindings/ruby/test/test_parakeet_context.rb @@ -0,0 +1,123 @@ +require_relative "helper" +require "stringio" + +class TestParakeetContext < TestBase + def setup + omit "Skip not to download large model" if ENV["CI"] + + Whisper.instance_variable_set "@whisper", nil + GC.start + + @parakeet = Parakeet::Context.new("parakeet-tdt-0.6b-v3-q4_0") + @params = Parakeet::Params.new + end + + def test_new + assert_instance_of Parakeet::Context, @parakeet + end + + def test_new_with_params + log_callback = Parakeet.instance_variable_get(:@log_callback) + user_data = Parakeet.instance_variable_get(:@log_callback_user_data) + begin + logs = "" + Parakeet.log_set proc {|level, message| logs << message}, nil + params = Parakeet::Context::Params.new(use_gpu: false) + parakeet = Parakeet::Context.new("parakeet-tdt-0.6b-v3-q4_0", params) + assert_instance_of Parakeet::Context, parakeet + assert_match /use gpu\s+=\s+0/, logs + ensure + Parakeet.log_set log_callback, user_data + end + end + + sub_test_case "full" do + def setup + super + @samples = File.read(AUDIO, nil, 78).unpack("s<*").collect {|i| i.to_f / 2**15} + end + + def test_full + @parakeet.full @params, @samples, @samples.length + + segments = @parakeet.each_segment.to_a + assert_equal 1, segments.length + assert_match /ask not what your country can do for you, ask what you can do for your/, segments.first.text + end + + def test_full_without_length + @parakeet.full(@params, @samples) + + segments = @parakeet.each_segment.to_a + assert_equal 1, segments.length + assert_match /ask not what your country can do for you, ask what you can do for your/, @parakeet.each_segment.first.text + end + + def test_full_enumerator + samples = @samples.each + @parakeet.full @params, samples, @samples.length + + segments = @parakeet.each_segment.to_a + assert_equal 1, segments.length + assert_match /ask not what your country can do for you, ask what you can do for your/, @parakeet.each_segment.first.text + end + + def test_full_enumerator_without_length + samples = @samples.each + assert_raise ArgumentError do + @parakeet.full @params, samples + end + end + + def test_full_enumerator_with_too_large_length + samples = @samples.each.take(10).to_enum + assert_raise StopIteration do + @parakeet.full @params, samples, 11 + end + end + + def test_full_with_memory_view + samples = JFKReader.new(AUDIO) + @parakeet.full @params, samples + + segments = @parakeet.each_segment.to_a + assert_equal 1, segments.length + assert_match /ask not what your country can do for you, ask what you can do for your/, @parakeet.each_segment.first.text + end + + def test_full_with_memroy_view_gc + samples = JFKReader.new(AUDIO) + @parakeet.full(@params, samples) + GC.start + require "fiddle" + Fiddle::MemoryView.export samples do |view| + assert_equal 176000, view.to_s.unpack("#{view.format}*").length + end + end + end + + def test_transcribe + assert_nothing_raised do + @parakeet.transcribe AUDIO, @params + end + end + + def test_transcribe_with_pathname + assert_nothing_raised do + @parakeet.transcribe Pathname(AUDIO), @params + end + end + + def test_transcribe_with_nothing + assert_raise_message(/open/) do + @parakeet.transcribe "nothing", @params + end + end + + def test_free + @parakeet.free + assert_raise RuntimeError do + @parakeet.transcribe AUDIO, @params + end + end +end diff --git a/bindings/ruby/test/test_parakeet_context_params.rb b/bindings/ruby/test/test_parakeet_context_params.rb new file mode 100644 index 0000000..fcd0f24 --- /dev/null +++ b/bindings/ruby/test/test_parakeet_context_params.rb @@ -0,0 +1,24 @@ +require_relative "helper" + +class TestParakeetContextParams < TestBase + def setup + @params = Parakeet::Context::Params.new + end + + def test_new + assert_instance_of Parakeet::Context::Params, @params + end + + def test_attributes + assert_true @params.use_gpu + assert_instance_of Integer, @params.gpu_device + end + + def test_attribute_writer + @params.use_gpu = false + assert_false @params.use_gpu + + @params.gpu_device = 2 + assert_equal 2, @params.gpu_device + end +end diff --git a/bindings/ruby/test/test_parakeet_model.rb b/bindings/ruby/test/test_parakeet_model.rb new file mode 100644 index 0000000..5343b35 --- /dev/null +++ b/bindings/ruby/test/test_parakeet_model.rb @@ -0,0 +1,21 @@ +require_relative "helper" + +class TestParakeetModel < TestBase + def test_model + parakeet = Parakeet::Context.new("test/fixtures/for-tests-ggml-parakeet-tdt.bin") + assert_instance_of Parakeet::Model, parakeet.model + end + + def test_attributes + parakeet = Parakeet::Context.new("test/fixtures/for-tests-ggml-parakeet-tdt.bin") + model = parakeet.model + + assert_equal 10, model.n_vocab + assert_equal 3200, model.n_audio_ctx + assert_equal 8, model.n_audio_state + assert_equal 2, model.n_audio_head + assert_equal 1, model.n_audio_layer + assert_equal 16, model.n_mels + assert_equal 0, model.ftype + end +end diff --git a/bindings/ruby/test/test_parakeet_params.rb b/bindings/ruby/test/test_parakeet_params.rb new file mode 100644 index 0000000..dc651f7 --- /dev/null +++ b/bindings/ruby/test/test_parakeet_params.rb @@ -0,0 +1,78 @@ +require_relative "helper" +require "etc" + +class TestParakeetParams < TestBase + PARAM_NAMES = [ + :n_threads, + :offset_ms, + :duration_ms, + :no_context, + :audio_ctx + ] + + def setup + @params = Parakeet::Params.new + end + + def test_new + assert_instance_of Parakeet::Params, @params + end + + def test_n_threads + assert_equal [4, Etc.nprocessors].min, @params.n_threads + + @params.n_threads = 1 + assert_equal 1, @params.n_threads + end + + def test_offset_ms + assert_equal 0, @params.offset_ms + + @params.offset_ms = 10_000 + assert_equal 10_000, @params.offset_ms + end + + def test_duration_ms + assert_equal 0, @params.duration_ms + + @params.duration_ms = 60_000 + assert_equal 60_000, @params.duration_ms + end + + def test_no_context + assert_equal true, @params.no_context + + @params.no_context = false + assert_equal false, @params.no_context + end + + def test_audio_ctx + assert_equal 0, @params.audio_ctx + + @params.audio_ctx = 1 + assert_equal 1, @params.audio_ctx + end + + def test_new_with_kw_args + params = Parakeet::Params.new(n_threads: 1) + assert_equal 1, params.n_threads + assert_equal 0, params.offset_ms + end + + data(PARAM_NAMES.collect {|param| [param, param]}.to_h) + def test_new_with_kw_args_default_values(param) + default_value = @params.send(param) + value = case [param, default_value] + in [*, true | false] + !default_value + in [*, Integer] + default_value + 1 + end + params = Parakeet::Params.new(param => value) + assert_equal value, params.send(param) + + PARAM_NAMES.reject {|name| name == param}.each do |name| + assert_equal @params.send(name), params.send(name) + end + end +end diff --git a/bindings/ruby/test/test_parakeet_segment.rb b/bindings/ruby/test/test_parakeet_segment.rb new file mode 100644 index 0000000..d5b99bd --- /dev/null +++ b/bindings/ruby/test/test_parakeet_segment.rb @@ -0,0 +1,42 @@ +require_relative "helper" + +class TestParakeetSegment < TestBase + def setup + omit "Skip not to download large model" if ENV["CI"] + + @parakeet = Parakeet::Context.new("parakeet-tdt-0.6b-v3-q4_0") + @parakeet.transcribe AUDIO, Parakeet::Params.new + end + + def test_segment + whole_text = "" + @parakeet.each_segment do |segment| + assert_instance_of Parakeet::Segment, segment + assert_kind_of Integer, segment.start_time + assert segment.end_time >= segment.start_time + assert_kind_of String, segment.text + whole_text << segment.text + end + assert_match(/ask not what your country can do for you, ask what you can do for your country/, whole_text) + end + + def test_deconstruct_keys + segment = @parakeet.each_segment.first + expected = { + start_time: segment.start_time, + end_time: segment.end_time, + text: segment.text + } + assert_equal expected, segment.deconstruct_keys([:start_time, :end_time, :text]) + end + + def test_deconstruct_keys_with_nil + segment = @parakeet.each_segment.first + expected = { + start_time: segment.start_time, + end_time: segment.end_time, + text: segment.text + } + assert_equal expected, segment.deconstruct_keys(nil) + end +end diff --git a/bindings/ruby/test/test_parakeet_token.rb b/bindings/ruby/test/test_parakeet_token.rb new file mode 100644 index 0000000..6f0b8b5 --- /dev/null +++ b/bindings/ruby/test/test_parakeet_token.rb @@ -0,0 +1,73 @@ +require_relative "helper" + +class TestParakeetToken < TestBase + ATTRS = %i[ + id + duration_idx + duration_value + frame_index + probability + log_probability + start_time + end_time + word_start? + text + ] + + def setup + omit "Skip not to download large model" if ENV["CI"] + + Whisper.instance_variable_set "@whisper", nil + GC.start + + parakeet = Parakeet::Context.new("parakeet-tdt-0.6b-v3-q4_0") + params = Parakeet::Params.new + parakeet.transcribe AUDIO, params + @segment = parakeet.each_segment.first + end + + def test_each_token + i = 0 + @segment.each_token do |token| + i += 1 + assert_instance_of Parakeet::Token, token + end + assert_equal 38, i + end + + def test_each_token_without_block + assert_instance_of Enumerator, @segment.each_token + end + + def test_token + token = @segment.each_token.first + + assert_instance_of Parakeet::Token, token + assert_instance_of Integer, token.id + assert_instance_of Integer, token.duration_idx + assert_instance_of Integer, token.duration_value + assert_instance_of Integer, token.frame_index + assert_instance_of Float, token.probability + assert_instance_of Float, token.log_probability + assert_instance_of Integer, token.start_time + assert_instance_of Integer, token.end_time + assert_instance_of String, token.text + end + + def test_text + assert_equal ["▁And", "▁so", ",", "▁my", "▁f", "ell", "ow", "▁Amer", "ic", "ans", ",", "▁a", "sk", "▁not", "▁what", "▁your", "▁co", "un", "tr", "y", "▁can", "▁do", "▁for", "▁you", ",", "▁a", "sk", "▁what", "▁you", "▁can", "▁do", "▁for", "▁your", "▁co", "un", "tr", "y", "."], + @segment.each_token.collect(&:text) + end + + def test_deconstruct_keys_with_nil + token = @segment.each_token.first + expected = ATTRS.collect {|attr| [attr.to_s.sub(/\?\z/, "").intern, token.send(attr)]}.to_h + assert_equal expected, token.deconstruct_keys(nil) + end + + def test_deconstruct_keys_with_keys + token = @segment.each_token.first + expected = ATTRS.collect {|attr| [attr.to_s.sub(/\?\z/, "").intern, token.send(attr)]}.to_h + assert_equal expected, token.deconstruct_keys(expected.keys) + end +end diff --git a/bindings/ruby/test/test_params.rb b/bindings/ruby/test/test_params.rb new file mode 100644 index 0000000..ff5c28e --- /dev/null +++ b/bindings/ruby/test/test_params.rb @@ -0,0 +1,315 @@ +require_relative "helper" + +class TestParams < TestBase + PARAM_NAMES = [ + :language, + :translate, + :no_context, + :single_segment, + :print_special, + :print_progress, + :print_realtime, + :print_timestamps, + :suppress_blank, + :suppress_nst, + :token_timestamps, + :max_len, + :split_on_word, + :initial_prompt, + :carry_initial_prompt, + :diarize, + :offset, + :duration, + :max_text_tokens, + :temperature, + :max_initial_ts, + :length_penalty, + :temperature_inc, + :entropy_thold, + :logprob_thold, + :no_speech_thold, + :new_segment_callback, + :new_segment_callback_user_data, + :progress_callback, + :progress_callback_user_data, + :abort_callback, + :abort_callback_user_data, + :vad, + :vad_model_path, + :vad_params, + ] + + def setup + @params = Whisper::Params.new + end + + def test_language + @params.language = "en" + assert_equal @params.language, "en" + GC.compact + assert_equal @params.language, "en" + @params.language = "auto" + assert_equal @params.language, "auto" + end + + def test_offset + @params.offset = 10_000 + assert_equal @params.offset, 10_000 + @params.offset = 0 + assert_equal @params.offset, 0 + end + + def test_duration + @params.duration = 60_000 + assert_equal @params.duration, 60_000 + @params.duration = 0 + assert_equal @params.duration, 0 + end + + def test_max_text_tokens + @params.max_text_tokens = 300 + assert_equal @params.max_text_tokens, 300 + @params.max_text_tokens = 0 + assert_equal @params.max_text_tokens, 0 + end + + def test_translate + @params.translate = true + assert @params.translate + @params.translate = false + assert !@params.translate + end + + def test_no_context + @params.no_context = true + assert @params.no_context + @params.no_context = false + assert !@params.no_context + end + + def test_single_segment + @params.single_segment = true + assert @params.single_segment + @params.single_segment = false + assert !@params.single_segment + end + + def test_print_special + @params.print_special = true + assert @params.print_special + @params.print_special = false + assert !@params.print_special + end + + def test_print_progress + @params.print_progress = true + assert @params.print_progress + @params.print_progress = false + assert !@params.print_progress + end + + def test_print_realtime + @params.print_realtime = true + assert @params.print_realtime + @params.print_realtime = false + assert !@params.print_realtime + end + + def test_print_timestamps + @params.print_timestamps = true + assert @params.print_timestamps + @params.print_timestamps = false + assert !@params.print_timestamps + end + + def test_carry_initial_prompt + @params.carry_initial_prompt = true + assert @params.carry_initial_prompt + @params.carry_initial_prompt = false + assert !@params.carry_initial_prompt + end + + def test_suppress_blank + @params.suppress_blank = true + assert @params.suppress_blank + @params.suppress_blank = false + assert !@params.suppress_blank + end + + def test_suppress_nst + @params.suppress_nst = true + assert @params.suppress_nst + @params.suppress_nst = false + assert !@params.suppress_nst + end + + def test_token_timestamps + @params.token_timestamps = true + assert @params.token_timestamps + @params.token_timestamps = false + assert !@params.token_timestamps + end + + def test_max_len + @params.max_len = 42 + assert_equal @params.max_len, 42 + @params.max_len = 0 + assert_equal @params.max_len, 0 + end + + def test_split_on_word + @params.split_on_word = true + assert @params.split_on_word + @params.split_on_word = false + assert !@params.split_on_word + end + + def test_initial_prompt + assert_nil @params.initial_prompt + @params.initial_prompt = "You are a polite person." + assert_equal "You are a polite person.", @params.initial_prompt + end + + def test_temperature + assert_equal 0.0, @params.temperature + @params.temperature = 0.5 + assert_equal 0.5, @params.temperature + end + + def test_max_initial_ts + assert_equal 1.0, @params.max_initial_ts + @params.max_initial_ts = 600.0 + assert_equal 600.0, @params.max_initial_ts + end + + def test_length_penalty + assert_equal(-1.0, @params.length_penalty) + @params.length_penalty = 0.5 + assert_equal 0.5, @params.length_penalty + end + + def test_temperature_inc + assert_in_delta 0.2, @params.temperature_inc + @params.temperature_inc = 0.5 + assert_in_delta 0.5, @params.temperature_inc + end + + def test_entropy_thold + assert_in_delta 2.4, @params.entropy_thold + @params.entropy_thold = 3.0 + assert_in_delta 3.0, @params.entropy_thold + end + + def test_logprob_thold + assert_in_delta(-1.0, @params.logprob_thold) + @params.logprob_thold = -0.5 + assert_in_delta(-0.5, @params.logprob_thold) + end + + def test_no_speech_thold + assert_in_delta 0.6, @params.no_speech_thold + @params.no_speech_thold = 0.2 + assert_in_delta 0.2, @params.no_speech_thold + end + + def test_vad + assert_false @params.vad + @params.vad = true + assert_true @params.vad + end + + def test_vad_model_path + assert_nil @params.vad_model_path + @params.vad_model_path = "silero-v6.2.0" + assert_equal Whisper::Model.pre_converted_models["silero-v6.2.0"].to_path, @params.vad_model_path + end + + def test_vad_model_path_with_nil + @params.vad_model_path = "silero-v6.2.0" + @params.vad_model_path = nil + assert_nil @params.vad_model_path + end + + def test_vad_model_path_with_invalid + assert_raise TypeError do + @params.vad_model_path = Object.new + end + end + + def test_vad_model_path_with_URI_string + @params.vad_model_path = "https://huggingface.co/ggml-org/whisper-vad/resolve/main/ggml-silero-v6.2.0.bin" + assert_equal @params.vad_model_path, Whisper::Model.pre_converted_models["silero-v6.2.0"].to_path + end + + def test_vad_model_path_with_URI + @params.vad_model_path = URI("https://huggingface.co/ggml-org/whisper-vad/resolve/main/ggml-silero-v6.2.0.bin") + assert_equal @params.vad_model_path, Whisper::Model.pre_converted_models["silero-v6.2.0"].to_path + end + + def test_vad_params + assert_kind_of Whisper::VAD::Params, @params.vad_params + default_params = @params.vad_params + assert_same default_params, @params.vad_params + assert_equal 0.5, default_params.threshold + new_params = Whisper::VAD::Params.new + @params.vad_params = new_params + assert_same new_params, @params.vad_params + end + + def test_new_with_kw_args + params = Whisper::Params.new(language: "es") + assert_equal "es", params.language + assert_equal 1.0, params.max_initial_ts + end + + def test_new_with_kw_args_non_existent + assert_raise ArgumentError do + Whisper::Params.new(non_existent: "value") + end + end + + def test_new_with_kw_args_wrong_type + assert_raise TypeError do + Whisper::Params.new(language: 3) + end + end + + data(PARAM_NAMES.collect {|param| [param, param]}.to_h) + def test_new_with_kw_args_default_values(param) + default_value = @params.send(param) + value = case [param, default_value] + in [*, true | false] + !default_value + in [*, Integer | Float] + default_value + 1 + in [:language, *] + "es" + in [:initial_prompt, *] + "Initial prompt" + in [/_callback\Z/, *] + proc {} + in [/_user_data\Z/, *] + Object.new + in [:vad_model_path, *] + Whisper::Model.pre_converted_models["silero-v6.2.0"].to_path + in [:vad_params, *] + Whisper::VAD::Params.new + end + params = Whisper::Params.new(param => value) + if Float === value + assert_in_delta value, params.send(param) + else + assert_equal value, params.send(param) + end + + PARAM_NAMES.reject {|name| name == param}.each do |name| + expected = @params.send(name) + actual = params.send(name) + if Float === expected + assert_in_delta expected, actual + else + assert_equal expected, actual + end + end + end +end diff --git a/bindings/ruby/test/test_segment.rb b/bindings/ruby/test/test_segment.rb new file mode 100644 index 0000000..08a037c --- /dev/null +++ b/bindings/ruby/test/test_segment.rb @@ -0,0 +1,145 @@ +require_relative "helper" + +class TestSegment < TestBase + def test_iteration + whisper.each_segment do |segment| + assert_instance_of Whisper::Segment, segment + end + end + + def test_enumerator + enum = whisper.each_segment + assert_instance_of Enumerator, enum + enum.to_a.each_with_index do |segment, index| + assert_instance_of Whisper::Segment, segment + assert_kind_of Integer, index + end + end + + def test_start_time + i = 0 + whisper.each_segment do |segment| + assert_equal 0, segment.start_time if i == 0 + i += 1 + end + end + + def test_end_time + i = 0 + whisper.each_segment do |segment| + assert_equal whisper.full_get_segment_t1(i) * 10, segment.end_time + i += 1 + end + end + + def test_no_speech_prob + no_speech_prob = nil + whisper.each_segment do |segment| + no_speech_prob = segment.no_speech_prob + end + assert no_speech_prob > 0.0 + end + + def test_on_new_segment + params = Whisper::Params.new + seg = nil + index = 0 + params.on_new_segment do |segment| + assert_instance_of Whisper::Segment, segment + if index == 0 + seg = segment + assert_equal 0, segment.start_time + assert_match(/ask not what your country can do for you, ask what you can do for your country/, segment.text) + end + index += 1 + end + whisper.transcribe(AUDIO, params) + assert_equal 0, seg.start_time + assert_match(/ask not what your country can do for you, ask what you can do for your country/, seg.text) + end + + def test_on_new_segment_twice + params = Whisper::Params.new + seg = nil + params.on_new_segment do |segment| + seg = segment + return + end + params.on_new_segment do |segment| + assert_same seg, segment + return + end + whisper.transcribe(AUDIO, params) + end + + def test_transcription_after_segment_retrieved + segment = whisper.each_segment.first + assert_match(/ask not what your country can do for you, ask what you can do for your country/, segment.text) + + whisper.transcribe(AUDIO, Whisper::Params.new(offset: 5000)) + assert_not_match(/ask not what your country can do for you, ask what you can do for your country/, segment.text) + assert_match(/what you can do for your country/i, segment.text) + end + + def test_pattern_matching + segment = whisper.each_segment.first + segment => {start_time:, end_time:, text:, no_speech_prob:, speaker_turn_next:} + + assert_equal segment.start_time, start_time + assert_equal segment.end_time, end_time + assert_equal segment.text, text + assert_equal segment.no_speech_prob, no_speech_prob + assert_equal segment.speaker_turn_next?, speaker_turn_next + end + + def test_pattern_matching_partial + segment = whisper.each_segment.first + segment => {start_time:, end_time:, text:} + + assert_equal segment.start_time, start_time + assert_equal segment.end_time, end_time + assert_equal segment.text, text + end + + def test_deconstruct_keys + segment = whisper.each_segment.first + expected = { + start_time: segment.start_time, + end_time: segment.end_time, + text: segment.text, + no_speech_prob: segment.no_speech_prob, + speaker_turn_next: segment.speaker_turn_next? + } + assert_equal expected, segment.deconstruct_keys([:start_time, :end_time, :text, :no_speech_prob, :speaker_turn_next]) + end + + def test_deconstruct_keys_non_existent + omit "Undefined behavior" + + segment = whisper.each_segment.first + + assert_equal({}, segment.deconstruct_keys([:non_existent])) + end + + def test_deconstruct_keys_too_many_keys + omit "Undefined behavior" + + segment = whisper.each_segment.first + + assert_equal({}, segment.deconstruct_keys([:start_time, :end_time, :text, :no_speech_prob, :speaker_turn_next, :extra_key])) + end + + def test_deconstruct_keys_includes_non_existent_keys_not_too_many + omit "Undefined behavior" + + segment = whisper.each_segment.first + + expected = { + start_time: segment.start_time, + end_time: segment.end_time, + text: segment.text, + no_speech_prob: segment.no_speech_prob + } + assert_equal(expected, segment.deconstruct_keys([:start_time, :end_time, :text, :no_speech_prob, :non_existent])) + end +end diff --git a/bindings/ruby/test/test_token.rb b/bindings/ruby/test/test_token.rb new file mode 100644 index 0000000..a23f681 --- /dev/null +++ b/bindings/ruby/test/test_token.rb @@ -0,0 +1,81 @@ +require_relative "helper" + +class TestToken < TestBase + def setup + @segment = whisper.each_segment.first + @token = @segment.each_token.first + end + + def test_n_tokens + assert_equal 27, @segment.n_tokens + end + + def test_allocate + token = Whisper::Token.allocate + assert_raise do + token.id + end + end + + def test_each_token + i = 0 + @segment.each_token do |token| + i += 1 + assert_instance_of Whisper::Token, token + end + assert_equal 27, i + end + + def test_each_token_without_block + assert_instance_of Enumerator, @segment.each_token + end + + def test_token + assert_instance_of Whisper::Token, @token + + assert_instance_of Integer, @token.id + assert_instance_of Float, @token.probability + assert_instance_of Float, @token.log_probability + + assert_instance_of Integer, @token.tid + assert_instance_of Float, @token.pt + assert_instance_of Float, @token.ptsum + + assert_instance_of Integer, @token.start_time + assert_instance_of Integer, @token.end_time + + assert_instance_of Integer, @token.t_dtw + + assert_instance_of Float, @token.voice_length + + assert_instance_of String, @token.text + end + + def test_text + assert_equal ["[_BEG_]", " And", " so", " my", " fellow", " Americans", ",", " ask", " not", " what", " your", " country", " can", " do", " for", " you", ",", " ask", " what", " you", " can", " do", " for", " your", " country", ".", "[_TT_550]"], + @segment.each_token.collect(&:text) + end + + def test_token_timestamps + params = Whisper::Params.new(token_timestamps: true) + whisper.transcribe(TestBase::AUDIO, params) + prev = -1 + whisper.each_segment.first.each_token do |token| + assert token.start_time >= prev + assert token.end_time >= token.start_time + prev = token.end_time + end + end + + def test_deconstruct_keys_with_nil + keys = %i[id tid probability log_probability pt ptsum t_dtw voice_length start_time end_time text] + expected = keys.collect {|key| [key, @token.send(key)] }.to_h + assert_equal(expected, @token.deconstruct_keys(nil)) + end + + def test_deconstruct_keys_with_keys + keys = %i[id tid probability log_probability pt ptsum t_dtw voice_length start_time end_time text] + expected = keys.collect {|key| [key, @token.send(key)] }.to_h + assert_equal expected, @token.deconstruct_keys(keys) + end +end diff --git a/bindings/ruby/test/test_vad.rb b/bindings/ruby/test/test_vad.rb new file mode 100644 index 0000000..8d3d94d --- /dev/null +++ b/bindings/ruby/test/test_vad.rb @@ -0,0 +1,28 @@ +require_relative "helper" + +class TestVAD < TestBase + def setup + @whisper = Whisper::Context.new("base.en") + vad_params = Whisper::VAD::Params.new + @params = Whisper::Params.new( + vad: true, + vad_model_path: "silero-v6.2.0", + vad_params: + ) + end + + def test_transcribe + @whisper.transcribe(TestBase::AUDIO, @params) do |text| + assert_match(/ask not what your country can do for you[,.] ask what you can do for your country/i, text) + end + end + + def test_vad_segments_api + @whisper.transcribe TestBase::AUDIO, @params + + assert_equal 4, @whisper.full_n_vad_segments + @whisper.full_n_vad_segments.times do |i| + assert @whisper.full_get_vad_segment_t0(i) < @whisper.full_get_vad_segment_t1(i) + end + end +end diff --git a/bindings/ruby/test/test_vad_context.rb b/bindings/ruby/test/test_vad_context.rb new file mode 100644 index 0000000..9cc5016 --- /dev/null +++ b/bindings/ruby/test/test_vad_context.rb @@ -0,0 +1,108 @@ +require_relative "helper" + +class TestVADContext < TestBase + def test_initialize + context = Whisper::VAD::Context.new("silero-v6.2.0") + assert_instance_of Whisper::VAD::Context, context + end + + def test_detect + context = Whisper::VAD::Context.new("silero-v6.2.0") + segments = context.detect(AUDIO, Whisper::VAD::Params.new) + assert_segments segments + end + + def test_invalid_model_type + assert_raise TypeError do + Whisper::VAD::Context.new(Object.new) + end + end + + def test_allocate + vad = Whisper::VAD::Context.allocate + assert_raise do + vad.detect(AUDIO, Whisper::VAD::Params.new) + end + end + + def test_free + vad = Whisper::VAD::Context.new("silero-v6.2.0") + vad.free + assert_raise RuntimeError do + vad.detect(AUDIO, Whisper::VAD::Params.new) + end + end + + private + + def assert_segments(segments) + assert_instance_of Whisper::VAD::Segments, segments + + i = 0 + segments.each do |segment| + i += 1 + assert_instance_of Whisper::VAD::Segment, segment + end + assert i > 0 + + segments.each_with_index do |segment, index| + assert_instance_of Integer, index + end + + assert_instance_of Enumerator, segments.each + + segment = segments.each.first + assert_instance_of Integer, segment.start_time + assert_instance_of Integer, segment.end_time + + segment => {start_time:, end_time:} + assert_equal segment.start_time, start_time + assert_equal segment.end_time, end_time + + assert_equal 4, segments.length + end + + sub_test_case "from samples" do + def setup + super + @vad = Whisper::VAD::Context.new("silero-v6.2.0") + @samples = File.read(AUDIO, nil, 78).unpack("s<*").collect {|i| i.to_f / 2**15} + end + + def test_segments_from_samples + segments = @vad.segments_from_samples(Whisper::VAD::Params.new, @samples, @samples.length) + assert_segments segments + end + + def test_segments_from_samples_without_length + segments = @vad.segments_from_samples(Whisper::VAD::Params.new, @samples) + assert_segments segments + end + + def test_segments_from_samples_enumerator + samples = @samples.each + segments = @vad.segments_from_samples(Whisper::VAD::Params.new, samples, @samples.length) + assert_segments segments + end + + def test_segments_from_samples_enumerator_without_length + samples = @samples.each + assert_raise ArgumentError do + @vad.segments_from_samples(Whisper::VAD::Params.new, samples) + end + end + + def test_segments_from_samples_enumerator_with_too_large_length + samples = @samples.each.take(10).to_enum + assert_raise StopIteration do + @vad.segments_from_samples(Whisper::VAD::Params.new, samples, 11) + end + end + + def test_segments_from_samples_with_memory_view + samples = JFKReader.new(AUDIO) + segments = @vad.segments_from_samples(Whisper::VAD::Params.new, samples) + assert_segments segments + end + end +end diff --git a/bindings/ruby/test/test_vad_params.rb b/bindings/ruby/test/test_vad_params.rb new file mode 100644 index 0000000..add4899 --- /dev/null +++ b/bindings/ruby/test/test_vad_params.rb @@ -0,0 +1,103 @@ +require_relative "helper" + +class TestVADParams < TestBase + PARAM_NAMES = [ + :threshold, + :min_speech_duration_ms, + :min_silence_duration_ms, + :max_speech_duration_s, + :speech_pad_ms, + :samples_overlap + ] + + def setup + @params = Whisper::VAD::Params.new + end + + def test_new + params = Whisper::VAD::Params.new + assert_kind_of Whisper::VAD::Params, params + end + + def test_threshold + assert_in_delta @params.threshold, 0.5 + @params.threshold = 0.7 + assert_in_delta @params.threshold, 0.7 + end + + def test_min_speech_duration + pend + end + + def test_min_speech_duration_ms + assert_equal 250, @params.min_speech_duration_ms + @params.min_speech_duration_ms = 500 + assert_equal 500, @params.min_speech_duration_ms + end + + def test_min_silence_duration_ms + assert_equal 100, @params.min_silence_duration_ms + @params.min_silence_duration_ms = 200 + assert_equal 200, @params.min_silence_duration_ms + end + + def test_max_speech_duration + pend + end + + def test_max_speech_duration_s + assert @params.max_speech_duration_s >= 10e37 # Defaults to FLT_MAX + @params.max_speech_duration_s = 60.0 + assert_equal 60.0, @params.max_speech_duration_s + end + + def test_speech_pad_ms + assert_equal 30, @params.speech_pad_ms + @params.speech_pad_ms = 50 + assert_equal 50, @params.speech_pad_ms + end + + def test_samples_overlap + assert_in_delta @params.samples_overlap, 0.1 + @params.samples_overlap = 0.5 + assert_in_delta @params.samples_overlap, 0.5 + end + + def test_equal + assert_equal @params, Whisper::VAD::Params.new + end + + def test_new_with_kw_args + params = Whisper::VAD::Params.new(threshold: 0.7) + assert_in_delta params.threshold, 0.7 + assert_equal 250, params.min_speech_duration_ms + end + + def test_new_with_kw_args_non_existent + assert_raise ArgumentError do + Whisper::VAD::Params.new(non_existent: "value") + end + end + + data(PARAM_NAMES.collect {|param| [param, param]}.to_h) + def test_new_with_kw_args_default_values(param) + default_value = @params.send(param) + value = default_value + 1 + params = Whisper::VAD::Params.new(param => value) + if Float === value + assert_in_delta value, params.send(param) + else + assert_equal value, params.send(param) + end + + PARAM_NAMES.reject {|name| name == param}.each do |name| + expected = @params.send(name) + actual = params.send(name) + if Float === expected + assert_in_delta expected, actual + else + assert_equal expected, actual + end + end + end +end diff --git a/bindings/ruby/test/test_vad_segment.rb b/bindings/ruby/test/test_vad_segment.rb new file mode 100644 index 0000000..6d66c27 --- /dev/null +++ b/bindings/ruby/test/test_vad_segment.rb @@ -0,0 +1,19 @@ +require_relative "helper" + +class TestVADSegment < TestBase + def test_initialize + segment = Whisper::VAD::Segment.new + + assert_raise do + segment.start_time + end + + assert_raise do + segment.end_time + end + + assert_raise do + segment => {start_time:, end_time:} + end + end +end diff --git a/bindings/ruby/test/test_vad_segments.rb b/bindings/ruby/test/test_vad_segments.rb new file mode 100644 index 0000000..855dc48 --- /dev/null +++ b/bindings/ruby/test/test_vad_segments.rb @@ -0,0 +1,16 @@ +require_relative "helper" + +class TestVADSegments < TestBase + def test_initialize + segments = Whisper::VAD::Segments.new + + assert_raise do + segments.each do |segment| + end + end + + assert_raise do + segments.length + end + end +end diff --git a/bindings/ruby/test/test_whisper.rb b/bindings/ruby/test/test_whisper.rb new file mode 100644 index 0000000..ba5dbe7 --- /dev/null +++ b/bindings/ruby/test/test_whisper.rb @@ -0,0 +1,350 @@ +require_relative "helper" +require "stringio" +require "etc" +require "pathname" + +# Exists to detect memory-related bug +Whisper.log_set ->(level, buffer, user_data) {}, nil + +class TestWhisper < TestBase + def setup + @params = Whisper::Params.new + end + + def test_whisper + @whisper = Whisper::Context.new("base.en") + params = Whisper::Params.new + params.print_timestamps = false + + @whisper.transcribe(AUDIO, params) {|text| + assert_match(/ask not what your country can do for you, ask what you can do for your country/, text) + } + end + + def test_whisper_pathname + @whisper = Whisper::Context.new("base.en") + params = Whisper::Params.new + + @whisper.transcribe(Pathname(AUDIO), params) {|text| + assert_match(/ask not what your country can do for you, ask what you can do for your country/, text) + } + end + + def test_transcribe_non_parallel + @whisper = Whisper::Context.new("base.en") + params = Whisper::Params.new + + @whisper.transcribe(AUDIO, params, n_processors: 1) {|text| + assert_match(/ask not what your country can do for you, ask what you can do for your country/, text) + } + end + + def test_transcribe_n_processors + @whisper = Whisper::Context.new("base.en") + params = Whisper::Params.new + + without_log_callback do + @whisper.transcribe(AUDIO, params, n_processors: 4) {|text| + assert_match(/what you can do for your country/i, text) + } + end + end + + def test_free + whisper = Whisper::Context.new("base.en") + whisper.free + + assert_raise RuntimeError do + whisper.model_type + end + end + + private + + def without_log_callback + Whisper.log_set nil, nil + yield + ensure + Whisper.log_set ->(level, buffer, user_data) {}, nil + end + + sub_test_case "After transcription" do + def test_full_n_segments + assert_equal 1, whisper.full_n_segments + end + + def test_full_lang_id + assert_equal 0, whisper.full_lang_id + end + + def test_full_get_segment + segment = whisper.full_get_segment(0) + assert_equal 0, segment.start_time + assert_match(/ask not what your country can do for you, ask what you can do for your country/, segment.text) + end + + def test_full_get_segment_t0 + assert_equal 0, whisper.full_get_segment_t0(0) + assert_raise IndexError do + whisper.full_get_segment_t0(whisper.full_n_segments) + end + assert_raise IndexError do + whisper.full_get_segment_t0(-1) + end + end + + def test_full_get_segment_t1 + t1 = whisper.full_get_segment_t1(0) + assert_kind_of Integer, t1 + assert t1 > 0 + assert_raise IndexError do + whisper.full_get_segment_t1(whisper.full_n_segments) + end + end + + def test_full_get_segment_speaker_turn_next + assert_false whisper.full_get_segment_speaker_turn_next(0) + end + + def test_full_get_segment_text + assert_match(/ask not what your country can do for you, ask what you can do for your country/, whisper.full_get_segment_text(0)) + end + + def test_full_get_segment_no_speech_prob + prob = whisper.full_get_segment_no_speech_prob(0) + assert prob > 0.0 + assert prob < 1.0 + end + end + + def test_lang_max_id + assert_kind_of Integer, Whisper.lang_max_id + end + + def test_lang_id + assert_equal 0, Whisper.lang_id("en") + assert_raise ArgumentError do + Whisper.lang_id("non existing language") + end + end + + def test_lang_str + assert_equal "en", Whisper.lang_str(0) + assert_raise IndexError do + Whisper.lang_str(Whisper.lang_max_id + 1) + end + end + + def test_lang_str_full + assert_equal "english", Whisper.lang_str_full(0) + assert_raise IndexError do + Whisper.lang_str_full(Whisper.lang_max_id + 1) + end + end + + def test_system_info_str + assert_match(/\AWHISPER : COREML = \d | OPENVINO = \d |/, Whisper.system_info_str) + end + + def test_version + assert_kind_of String, Whisper::VERSION + end + + def test_log_set + user_data = Object.new + logs = [] + log_callback = ->(level, buffer, udata) { + logs << [level, buffer, udata] + } + Whisper.log_set log_callback, user_data + Whisper::Context.new("base.en") + sleep 0.1 # wait for logs dequeued + + assert logs.length > 30 + logs.each do |log| + assert_include [Whisper::LOG_LEVEL_DEBUG, Whisper::LOG_LEVEL_INFO, Whisper::LOG_LEVEL_WARN], log[0] + assert_same user_data, log[2] + end + end + + def test_log_suppress + stderr = $stderr + Whisper.log_set ->(level, buffer, user_data) { + # do nothing + }, nil + dev = StringIO.new("") + $stderr = dev + Whisper::Context.new("base.en") + assert_empty dev.string + ensure + $stderr = stderr + end + + def test_access_attribute_without_initialization + whisper = Whisper::Context.allocate + assert_raise do + whisper.model_type + end + end + + sub_test_case "full" do + def setup + super + @whisper = Whisper::Context.new("base.en") + @samples = File.read(AUDIO, nil, 78).unpack("s<*").collect {|i| i.to_f / 2**15} + end + + def test_full + @whisper.full(@params, @samples, @samples.length) + + assert_equal 1, @whisper.full_n_segments + assert_match(/ask not what your country can do for you, ask what you can do for your country/, @whisper.each_segment.first.text) + end + + def test_full_without_length + @whisper.full(@params, @samples) + + assert_equal 1, @whisper.full_n_segments + assert_match(/ask not what your country can do for you, ask what you can do for your country/, @whisper.each_segment.first.text) + end + + def test_full_enumerator + samples = @samples.each + @whisper.full(@params, samples, @samples.length) + + assert_equal 1, @whisper.full_n_segments + assert_match(/ask not what your country can do for you, ask what you can do for your country/, @whisper.each_segment.first.text) + end + + def test_full_enumerator_without_length + samples = @samples.each + assert_raise ArgumentError do + @whisper.full(@params, samples) + end + end + + def test_full_enumerator_with_too_large_length + samples = @samples.each.take(10).to_enum + assert_raise StopIteration do + @whisper.full(@params, samples, 11) + end + end + + def test_full_with_memory_view + samples = JFKReader.new(AUDIO) + @whisper.full(@params, samples) + + assert_equal 1, @whisper.full_n_segments + assert_match(/ask not what your country can do for you, ask what you can do for your country/, @whisper.each_segment.first.text) + end + + def test_full_with_memroy_view_gc + samples = JFKReader.new(AUDIO) + @whisper.full(@params, samples) + GC.start + require "fiddle" + Fiddle::MemoryView.export samples do |view| + assert_equal 176000, view.to_s.unpack("#{view.format}*").length + end + end + + def test_full_parallel + nprocessors = 2 + without_log_callback do + @whisper.full_parallel(@params, @samples, @samples.length, nprocessors) + end + + assert_equal nprocessors, @whisper.full_n_segments + text = @whisper.each_segment.collect(&:text).join + assert_match(/ask what you can do/i, text) + assert_match(/for your country/i, text) + end + + def test_full_parallel_with_memory_view + nprocessors = 2 + samples = JFKReader.new(AUDIO) + without_log_callback do + @whisper.full_parallel(@params, samples, nil, nprocessors) + end + + assert_equal nprocessors, @whisper.full_n_segments + text = @whisper.each_segment.collect(&:text).join + assert_match(/ask what you can do/i, text) + assert_match(/for your country/i, text) + end + + def test_full_parallel_without_length_and_n_processors + @whisper.full_parallel(@params, @samples) + + assert_equal 1, @whisper.full_n_segments + text = @whisper.each_segment.collect(&:text).join + assert_match(/ask what you can do/i, text) + assert_match(/for your country/i, text) + end + + def test_full_parallel_without_length + nprocessors = 2 + without_log_callback do + @whisper.full_parallel(@params, @samples, nil, nprocessors) + end + + assert_equal nprocessors, @whisper.full_n_segments + text = @whisper.each_segment.collect(&:text).join + assert_match(/ask what you can do/i, text) + assert_match(/for your country/i, text) + end + + def test_full_parallel_without_n_processors + @whisper.full_parallel(@params, @samples, @samples.length) + + assert_equal 1, @whisper.full_n_segments + text = @whisper.each_segment.collect(&:text).join + assert_match(/ask what you can do/i, text) + assert_match(/for your country/i, text) + end + end + + def test_to_srt + whisper = Whisper::Context.new("base.en") + whisper.transcribe AUDIO, @params + + lines = whisper.to_srt.lines + assert_match(/\A\d+\n/, lines[0]) + assert_match(/\d{2}:\d{2}:\d{2},\d{3} --> \d{2}:\d{2}:\d{2},\d{3}\n/, lines[1]) + assert_match(/ask not what your country can do for you, ask what you can do for your country/, lines[2]) + end + + def test_to_webvtt + whisper = Whisper::Context.new("base.en") + whisper.transcribe AUDIO, @params + + lines = whisper.to_webvtt.lines + assert_equal "WEBVTT\n", lines[0] + assert_equal "\n", lines[1] + assert_match(/\A\d+\n/, lines[2]) + assert_match(/\d{2}:\d{2}:\d{2}\.\d{3} --> \d{2}:\d{2}:\d{2}\.\d{3}\n/, lines[3]) + assert_match(/ask not what your country can do for you, ask what you can do for your country/, lines[4]) + end + + sub_test_case "Format needs escape" do + def setup + @whisper = Whisper::Context.new("base.en") + @whisper.transcribe AUDIO, Whisper::Params.new + segment = @whisper.each_segment.first + segment.define_singleton_method :text do + "& so my fellow Americans --> ask not what your country can do for you <-- ask what you can do for your country." + end + @whisper.define_singleton_method :each_segment do + Enumerator.new(3) {|yielder| 3.times {yielder << segment}} + end + end + + def test_to_srt_escape + assert_equal "& so my fellow Americans --> ask not what your country can do for you <-- ask what you can do for your country.\n", @whisper.to_srt.lines[2] + end + + def test_to_webvtt_escape + assert_equal "& so my fellow Americans --> ask not what your country can do for you <-- ask what you can do for your country.\n", @whisper.to_webvtt.lines[4] + end + end +end diff --git a/bindings/ruby/whispercpp.gemspec b/bindings/ruby/whispercpp.gemspec new file mode 100644 index 0000000..e10521c --- /dev/null +++ b/bindings/ruby/whispercpp.gemspec @@ -0,0 +1,36 @@ +require_relative "extsources" + +Gem::Specification.new do |s| + s.name = "whispercpp" + s.authors = ["Georgi Gerganov", "Todd A. Fisher"] + s.version = '1.3.9' + s.description = %q{High-performance inference of OpenAI's Whisper automatic speech recognition (ASR) model via Ruby} + s.email = 'todd.fisher@gmail.com' + s.extra_rdoc_files = ['LICENSE', 'README.md'] + + s.files = `git ls-files . -z`.split("\x0") + + EXTSOURCES.collect {|file| + basename = File.basename(file) + if s.extra_rdoc_files.include?(basename) + basename + else + file.sub("../..", "ext/sources") + .sub("../javascript", "ext/sources/bindings/javascript") + end + } + + s.summary = %q{Ruby whisper.cpp bindings} + s.test_files = s.files.select {|file| file.start_with? "test/"} + + s.extensions << 'ext/extconf.rb' + s.required_ruby_version = '>= 3.3.0' + + #### Documentation and testing. + s.homepage = 'https://github.com/ggml-org/whisper.cpp' + s.rdoc_options = ['--main', 'README.md'] + + + s.platform = Gem::Platform::RUBY + + s.licenses = ['MIT'] +end diff --git a/build-xcframework.sh b/build-xcframework.sh new file mode 100755 index 0000000..e49de71 --- /dev/null +++ b/build-xcframework.sh @@ -0,0 +1,574 @@ +#!/bin/bash +# +# Options +IOS_MIN_OS_VERSION=16.4 +MACOS_MIN_OS_VERSION=13.3 +VISIONOS_MIN_OS_VERSION=1.0 +TVOS_MIN_OS_VERSION=16.4 + +BUILD_SHARED_LIBS=OFF +WHISPER_BUILD_EXAMPLES=OFF +WHISPER_BUILD_TESTS=OFF +WHISPER_BUILD_SERVER=OFF +GGML_METAL=ON +GGML_METAL_EMBED_LIBRARY=ON +GGML_BLAS_DEFAULT=ON +GGML_METAL_USE_BF16=ON +GGML_OPENMP=OFF +BUILD_STATIC_XCFRAMEWORK=${BUILD_STATIC_XCFRAMEWORK:-OFF} + +COMMON_C_FLAGS="-Wno-macro-redefined -Wno-shorten-64-to-32 -Wno-unused-command-line-argument -g" +COMMON_CXX_FLAGS="-Wno-macro-redefined -Wno-shorten-64-to-32 -Wno-unused-command-line-argument -g" + +# Common options for all builds +COMMON_CMAKE_ARGS=( + -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=NO + -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY="" + -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=NO + -DCMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT="dwarf-with-dsym" + -DCMAKE_XCODE_ATTRIBUTE_GCC_GENERATE_DEBUGGING_SYMBOLS=YES + -DCMAKE_XCODE_ATTRIBUTE_COPY_PHASE_STRIP=NO + -DCMAKE_XCODE_ATTRIBUTE_STRIP_INSTALLED_PRODUCT=NO + -DCMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM=ggml + -DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS} + -DWHISPER_BUILD_EXAMPLES=${WHISPER_BUILD_EXAMPLES} + -DWHISPER_BUILD_TESTS=${WHISPER_BUILD_TESTS} + -DWHISPER_BUILD_SERVER=${WHISPER_BUILD_SERVER} + -DGGML_METAL_EMBED_LIBRARY=${GGML_METAL_EMBED_LIBRARY} + -DGGML_BLAS_DEFAULT=${GGML_BLAS_DEFAULT} + -DGGML_METAL=${GGML_METAL} + -DGGML_METAL_USE_BF16=${GGML_METAL_USE_BF16} + -DGGML_NATIVE=OFF + -DGGML_OPENMP=${GGML_OPENMP} +) + +XCODE_VERSION=$(xcodebuild -version 2>/dev/null | head -n1 | awk '{ print $2 }') +MAJOR_VERSION=$(echo $XCODE_VERSION | cut -d. -f1) +MINOR_VERSION=$(echo $XCODE_VERSION | cut -d. -f2) +echo "Detected Xcode version: $XCODE_VERSION" + +check_required_tool() { + local tool=$1 + local install_message=$2 + + if ! command -v $tool &> /dev/null; then + echo "Error: $tool is required but not found." + echo "$install_message" + exit 1 + fi +} +echo "Checking for required tools..." +check_required_tool "cmake" "Please install CMake 3.28.0 or later (brew install cmake)" +check_required_tool "xcodebuild" "Please install Xcode and Xcode Command Line Tools (xcode-select --install)" +check_required_tool "libtool" "Please install libtool which should be available with Xcode Command Line Tools (CLT). Make sure Xcode CLT is installed (xcode-select --install)" +check_required_tool "dsymutil" "Please install Xcode and Xcode Command Line Tools (xcode-select --install)" + +set -e + +## Clean up previous builds +rm -rf build-apple +rm -rf build-ios-sim +rm -rf build-ios-device +rm -rf build-macos +rm -rf build-visionos +rm -rf build-visionos-sim +rm -rf build-tvos-sim +rm -rf build-tvos-device + +# Setup the xcframework build directory structure +setup_framework_structure() { + local build_dir=$1 + local min_os_version=$2 + local platform=$3 # "ios", "macos", "visionos", or "tvos" + local framework_name="whisper" + + echo "Creating ${platform}-style framework structure for ${build_dir}" + + if [[ "$platform" == "macos" ]]; then + # macOS versioned structure uses versioned directories + mkdir -p ${build_dir}/framework/${framework_name}.framework/Versions/A/Headers + mkdir -p ${build_dir}/framework/${framework_name}.framework/Versions/A/Modules + mkdir -p ${build_dir}/framework/${framework_name}.framework/Versions/A/Resources + + # Create symbolic links + ln -sf A ${build_dir}/framework/${framework_name}.framework/Versions/Current + ln -sf Versions/Current/Headers ${build_dir}/framework/${framework_name}.framework/Headers + ln -sf Versions/Current/Modules ${build_dir}/framework/${framework_name}.framework/Modules + ln -sf Versions/Current/Resources ${build_dir}/framework/${framework_name}.framework/Resources + ln -sf Versions/Current/${framework_name} ${build_dir}/framework/${framework_name}.framework/${framework_name} + + # Set header and module paths + local header_path=${build_dir}/framework/${framework_name}.framework/Versions/A/Headers/ + local module_path=${build_dir}/framework/${framework_name}.framework/Versions/A/Modules/ + else + # iOS/VisionOS/tvOS use a flat structure + mkdir -p ${build_dir}/framework/${framework_name}.framework/Headers + mkdir -p ${build_dir}/framework/${framework_name}.framework/Modules + + # Remove any existing structure to ensure clean build + rm -rf ${build_dir}/framework/${framework_name}.framework/Versions + + # Set header and module paths + local header_path=${build_dir}/framework/${framework_name}.framework/Headers/ + local module_path=${build_dir}/framework/${framework_name}.framework/Modules/ + fi + + # Copy all required headers (common for all platforms) + cp include/whisper.h ${header_path} + cp include/parakeet.h ${header_path} + cp ggml/include/ggml.h ${header_path} + cp ggml/include/ggml-alloc.h ${header_path} + cp ggml/include/ggml-backend.h ${header_path} + cp ggml/include/ggml-metal.h ${header_path} + cp ggml/include/ggml-cpu.h ${header_path} + cp ggml/include/ggml-blas.h ${header_path} + cp ggml/include/gguf.h ${header_path} + + # Create module map (common for all platforms) + cat > ${module_path}module.modulemap << EOF +framework module whisper { + header "whisper.h" + header "parakeet.h" + header "ggml.h" + header "ggml-alloc.h" + header "ggml-backend.h" + header "ggml-metal.h" + header "ggml-cpu.h" + header "ggml-blas.h" + header "gguf.h" + + link "c++" + link framework "Accelerate" + link framework "Metal" + link framework "Foundation" + + export * +} +EOF + + # Platform-specific settings for Info.plist + local platform_name="" + local sdk_name="" + local supported_platform="" + + case "$platform" in + "ios") + platform_name="iphoneos" + sdk_name="iphoneos${min_os_version}" + supported_platform="iPhoneOS" + local plist_path="${build_dir}/framework/${framework_name}.framework/Info.plist" + local device_family=' <key>UIDeviceFamily</key> + <array> + <integer>1</integer> + <integer>2</integer> + </array>' + ;; + "macos") + platform_name="macosx" + sdk_name="macosx${min_os_version}" + supported_platform="MacOSX" + local plist_path="${build_dir}/framework/${framework_name}.framework/Versions/A/Resources/Info.plist" + local device_family="" + ;; + "visionos") + platform_name="xros" + sdk_name="xros${min_os_version}" + supported_platform="XRPlatform" + local plist_path="${build_dir}/framework/${framework_name}.framework/Info.plist" + local device_family="" + ;; + "tvos") + platform_name="appletvos" + sdk_name="appletvos${min_os_version}" + supported_platform="AppleTVOS" + local plist_path="${build_dir}/framework/${framework_name}.framework/Info.plist" + local device_family=' <key>UIDeviceFamily</key> + <array> + <integer>3</integer> + </array>' + ;; + esac + + # Create Info.plist + cat > ${plist_path} << EOF +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>en</string> + <key>CFBundleExecutable</key> + <string>whisper</string> + <key>CFBundleIdentifier</key> + <string>org.ggml.whisper</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>whisper</string> + <key>CFBundlePackageType</key> + <string>FMWK</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleVersion</key> + <string>1</string> + <key>MinimumOSVersion</key> + <string>${min_os_version}</string> + <key>CFBundleSupportedPlatforms</key> + <array> + <string>${supported_platform}</string> + </array>${device_family} + <key>DTPlatformName</key> + <string>${platform_name}</string> + <key>DTSDKName</key> + <string>${sdk_name}</string> +</dict> +</plist> +EOF +} + +# Create dynamic libraries from static libraries. +combine_static_libraries() { + local build_dir="$1" + local release_dir="$2" + local platform="$3" # "ios", "macos", "visionos", or "tvos" + local is_simulator="$4" + local base_dir="$(pwd)" + local framework_name="whisper" + + # Determine output path based on platform + local output_lib="" + if [[ "$platform" == "macos" ]]; then + # macOS uses versioned structure + output_lib="${build_dir}/framework/${framework_name}.framework/Versions/A/${framework_name}" + else + # iOS, visionOS, and tvOS use a directory flat structure + output_lib="${build_dir}/framework/${framework_name}.framework/${framework_name}" + fi + + local libs=( + "${base_dir}/${build_dir}/src/${release_dir}/libwhisper.a" + "${base_dir}/${build_dir}/src/${release_dir}/libparakeet.a" + "${base_dir}/${build_dir}/ggml/src/${release_dir}/libggml.a" + "${base_dir}/${build_dir}/ggml/src/${release_dir}/libggml-base.a" + "${base_dir}/${build_dir}/ggml/src/${release_dir}/libggml-cpu.a" + "${base_dir}/${build_dir}/ggml/src/ggml-metal/${release_dir}/libggml-metal.a" + "${base_dir}/${build_dir}/ggml/src/ggml-blas/${release_dir}/libggml-blas.a" + ) + if [[ "$platform" == "macos" || "$platform" == "ios" ]]; then + echo "Adding libwhisper.coreml library to the build." + libs+=( + "${base_dir}/${build_dir}/src/${release_dir}/libwhisper.coreml.a" + ) + fi + + # Create temporary directory for processing + local temp_dir="${base_dir}/${build_dir}/temp" + echo "Creating temporary directory: ${temp_dir}" + mkdir -p "${temp_dir}" + + # Since we have multiple architectures libtool will find object files that do not + # match the target architecture. We suppress these warnings. + libtool -static -o "${temp_dir}/combined.a" "${libs[@]}" 2> /dev/null + + # Determine SDK, architectures, and install_name based on platform and simulator flag. + local sdk="" + local archs="" + local min_version_flag="" + local install_name="" + local frameworks="-framework Foundation -framework Metal -framework Accelerate" + + case "$platform" in + "ios") + if [[ "$is_simulator" == "true" ]]; then + sdk="iphonesimulator" + archs="arm64 x86_64" + min_version_flag="-mios-simulator-version-min=${IOS_MIN_OS_VERSION}" + else + sdk="iphoneos" + archs="arm64" + min_version_flag="-mios-version-min=${IOS_MIN_OS_VERSION}" + fi + install_name="@rpath/whisper.framework/whisper" + frameworks+=" -framework CoreML" + ;; + "macos") + sdk="macosx" + archs="arm64 x86_64" + min_version_flag="-mmacosx-version-min=${MACOS_MIN_OS_VERSION}" + install_name="@rpath/whisper.framework/Versions/Current/whisper" + frameworks+=" -framework CoreML" + ;; + "visionos") + if [[ "$is_simulator" == "true" ]]; then + sdk="xrsimulator" + archs="arm64 x86_64" + min_version_flag="-mtargetos=xros${VISIONOS_MIN_OS_VERSION}-simulator" + else + sdk="xros" + archs="arm64" + min_version_flag="-mtargetos=xros${VISIONOS_MIN_OS_VERSION}" + fi + # Use flat structure for visionOS, same as iOS + install_name="@rpath/whisper.framework/whisper" + ;; + "tvos") + if [[ "$is_simulator" == "true" ]]; then + sdk="appletvsimulator" + archs="arm64 x86_64" + min_version_flag="-mtvos-simulator-version-min=${TVOS_MIN_OS_VERSION}" + else + sdk="appletvos" + archs="arm64" + min_version_flag="-mtvos-version-min=${TVOS_MIN_OS_VERSION}" + fi + install_name="@rpath/whisper.framework/whisper" + ;; + esac + + # Build architecture flags + local arch_flags="" + for arch in $archs; do + arch_flags+=" -arch $arch" + done + + + if [[ "${BUILD_STATIC_XCFRAMEWORK}" == "ON" ]]; then + echo "Packaging static framework for ${platform}." + mkdir -p "$(dirname "${base_dir}/${output_lib}")" + cp "${temp_dir}/combined.a" "${base_dir}/${output_lib}" + rm -rf "${temp_dir}" + return + fi + + # Create dynamic library + echo "Creating dynamic library for ${platform}." + xcrun -sdk $sdk clang++ -dynamiclib \ + -isysroot $(xcrun --sdk $sdk --show-sdk-path) \ + $arch_flags \ + $min_version_flag \ + -Wl,-force_load,"${temp_dir}/combined.a" \ + $frameworks \ + -install_name "$install_name" \ + -o "${base_dir}/${output_lib}" + + # Platform-specific post-processing for device builds + if [[ "$is_simulator" == "false" ]]; then + if command -v xcrun vtool &>/dev/null; then + case "$platform" in + "ios") + echo "Marking binary as a framework binary for iOS..." + xcrun vtool -set-build-version ios ${IOS_MIN_OS_VERSION} ${IOS_MIN_OS_VERSION} -replace \ + -output "${base_dir}/${output_lib}" "${base_dir}/${output_lib}" + ;; + "visionos") + echo "Marking binary as a framework binary for visionOS..." + if [[ "$MAJOR_VERSION" -gt 16 ]] || [[ "$MAJOR_VERSION" -eq 16 && "$MINOR_VERSION" -gt 2 ]]; then + echo "Xcode version greater than 16.2, using visionOS." + VISION_OS_BUILD_VERSION="visionos" + else + echo "Xcode version less than or equal to 16.2, using xros." + VISION_OS_BUILD_VERSION="xros" + fi + xcrun vtool -set-build-version ${VISION_OS_BUILD_VERSION} ${VISIONOS_MIN_OS_VERSION} ${VISIONOS_MIN_OS_VERSION} -replace \ + -output "${base_dir}/${output_lib}" "${base_dir}/${output_lib}" + ;; + "tvos") + echo "Marking binary as a framework binary for tvOS..." + xcrun vtool -set-build-version tvos ${TVOS_MIN_OS_VERSION} ${TVOS_MIN_OS_VERSION} -replace \ + -output "${base_dir}/${output_lib}" "${base_dir}/${output_lib}" + ;; + esac + else + echo "Warning: vtool not found. Binary may not pass App Store validation." + fi + fi + + echo "Creating properly formatted dSYM..." + # Create a separate directory for dSYMs for all platforms + mkdir -p "${base_dir}/${build_dir}/dSYMs" + + # iOS and visionOS style dSYM (flat structure) + if [[ "$platform" == "ios" || "$platform" == "visionos" || "$platform" == "tvos" ]]; then + # Generate dSYM in the dSYMs directory + xcrun dsymutil "${base_dir}/${output_lib}" -o "${base_dir}/${build_dir}/dSYMs/whisper.dSYM" + + # Create a copy of the binary that will be stripped + cp "${base_dir}/${output_lib}" "${temp_dir}/binary_to_strip" + + # Strip debug symbols from the copy + xcrun strip -S "${temp_dir}/binary_to_strip" -o "${temp_dir}/stripped_lib" + + # Replace the original with the stripped version + mv "${temp_dir}/stripped_lib" "${base_dir}/${output_lib}" + else + # macOS style dSYM + # First strip debug info to a separate file + xcrun strip -S "${base_dir}/${output_lib}" -o "${temp_dir}/stripped_lib" + + # Generate dSYM in the dSYMs directory + xcrun dsymutil "${base_dir}/${output_lib}" -o "${base_dir}/${build_dir}/dSYMs/whisper.dSYM" + + # Replace original binary with stripped version + mv "${temp_dir}/stripped_lib" "${base_dir}/${output_lib}" + fi + + # Remove any automatically generated dSYM files in the framework structure as they will + # otherwise case Invalid Bundle Structure validation errors. + if [ -d "${base_dir}/${output_lib}.dSYM" ]; then + echo "Removing generated dSYM file in framework structure: ${base_dir}/${output_lib}.dSYM" + rm -rf "${base_dir}/${output_lib}.dSYM" + fi + + # Clean up + rm -rf "${temp_dir}" +} + +echo "Building for iOS simulator..." +cmake -B build-ios-sim -G Xcode \ + "${COMMON_CMAKE_ARGS[@]}" \ + -DCMAKE_OSX_DEPLOYMENT_TARGET=${IOS_MIN_OS_VERSION} \ + -DIOS=ON \ + -DCMAKE_SYSTEM_NAME=iOS \ + -DCMAKE_OSX_SYSROOT=iphonesimulator \ + -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ + -DCMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS=iphonesimulator \ + -DCMAKE_C_FLAGS="${COMMON_C_FLAGS}" \ + -DCMAKE_CXX_FLAGS="${COMMON_CXX_FLAGS}" \ + -DWHISPER_COREML="ON" \ + -DWHISPER_COREML_ALLOW_FALLBACK="ON" \ + -S . +cmake --build build-ios-sim --config Release -- -quiet + +echo "Building for iOS devices..." +cmake -B build-ios-device -G Xcode \ + "${COMMON_CMAKE_ARGS[@]}" \ + -DCMAKE_OSX_DEPLOYMENT_TARGET=${IOS_MIN_OS_VERSION} \ + -DCMAKE_OSX_SYSROOT=iphoneos \ + -DCMAKE_OSX_ARCHITECTURES="arm64" \ + -DCMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS=iphoneos \ + -DCMAKE_C_FLAGS="${COMMON_C_FLAGS}" \ + -DCMAKE_CXX_FLAGS="${COMMON_CXX_FLAGS}" \ + -DWHISPER_COREML="ON" \ + -DWHISPER_COREML_ALLOW_FALLBACK="ON" \ + -S . +cmake --build build-ios-device --config Release -- -quiet + +echo "Building for macOS..." +cmake -B build-macos -G Xcode \ + "${COMMON_CMAKE_ARGS[@]}" \ + -DCMAKE_OSX_DEPLOYMENT_TARGET=${MACOS_MIN_OS_VERSION} \ + -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ + -DCMAKE_C_FLAGS="${COMMON_C_FLAGS}" \ + -DCMAKE_CXX_FLAGS="${COMMON_CXX_FLAGS}" \ + -DWHISPER_COREML="ON" \ + -DWHISPER_COREML_ALLOW_FALLBACK="ON" \ + -S . +cmake --build build-macos --config Release -- -quiet + +echo "Building for visionOS..." +cmake -B build-visionos -G Xcode \ + "${COMMON_CMAKE_ARGS[@]}" \ + -DCMAKE_OSX_DEPLOYMENT_TARGET=${VISIONOS_MIN_OS_VERSION} \ + -DCMAKE_OSX_ARCHITECTURES="arm64" \ + -DCMAKE_SYSTEM_NAME=visionOS \ + -DCMAKE_OSX_SYSROOT=xros \ + -DCMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS=xros \ + -DCMAKE_C_FLAGS="-D_XOPEN_SOURCE=700 ${COMMON_C_FLAGS}" \ + -DCMAKE_CXX_FLAGS="-D_XOPEN_SOURCE=700 ${COMMON_CXX_FLAGS}" \ + -S . +cmake --build build-visionos --config Release -- -quiet + +echo "Building for visionOS simulator..." +cmake -B build-visionos-sim -G Xcode \ + "${COMMON_CMAKE_ARGS[@]}" \ + -DCMAKE_OSX_DEPLOYMENT_TARGET=${VISIONOS_MIN_OS_VERSION} \ + -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ + -DCMAKE_SYSTEM_NAME=visionOS \ + -DCMAKE_OSX_SYSROOT=xrsimulator \ + -DCMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS=xrsimulator \ + -DCMAKE_C_FLAGS="-D_XOPEN_SOURCE=700 ${COMMON_C_FLAGS}" \ + -DCMAKE_CXX_FLAGS="-D_XOPEN_SOURCE=700 ${COMMON_CXX_FLAGS}" \ + -S . +cmake --build build-visionos-sim --config Release -- -quiet + +# Add tvOS builds (might need the same u_int definitions as watchOS and visionOS) +echo "Building for tvOS simulator..." +cmake -B build-tvos-sim -G Xcode \ + "${COMMON_CMAKE_ARGS[@]}" \ + -DCMAKE_OSX_DEPLOYMENT_TARGET=${TVOS_MIN_OS_VERSION} \ + -DCMAKE_SYSTEM_NAME=tvOS \ + -DCMAKE_OSX_SYSROOT=appletvsimulator \ + -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ + -DGGML_METAL=ON \ + -DCMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS=appletvsimulator \ + -DCMAKE_C_FLAGS="${COMMON_C_FLAGS}" \ + -DCMAKE_CXX_FLAGS="${COMMON_CXX_FLAGS}" \ + -S . +cmake --build build-tvos-sim --config Release -- -quiet + +echo "Building for tvOS devices..." +cmake -B build-tvos-device -G Xcode \ + "${COMMON_CMAKE_ARGS[@]}" \ + -DCMAKE_OSX_DEPLOYMENT_TARGET=${TVOS_MIN_OS_VERSION} \ + -DCMAKE_SYSTEM_NAME=tvOS \ + -DCMAKE_OSX_SYSROOT=appletvos \ + -DCMAKE_OSX_ARCHITECTURES="arm64" \ + -DGGML_METAL=ON \ + -DCMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS=appletvos \ + -DCMAKE_C_FLAGS="${COMMON_C_FLAGS}" \ + -DCMAKE_CXX_FLAGS="${COMMON_CXX_FLAGS}" \ + -S . +cmake --build build-tvos-device --config Release -- -quiet + +# Setup frameworks and copy binaries and headers +echo "Setting up framework structures..." +setup_framework_structure "build-ios-sim" ${IOS_MIN_OS_VERSION} "ios" +setup_framework_structure "build-ios-device" ${IOS_MIN_OS_VERSION} "ios" +setup_framework_structure "build-macos" ${MACOS_MIN_OS_VERSION} "macos" +setup_framework_structure "build-visionos" ${VISIONOS_MIN_OS_VERSION} "visionos" +setup_framework_structure "build-visionos-sim" ${VISIONOS_MIN_OS_VERSION} "visionos" +setup_framework_structure "build-tvos-sim" ${TVOS_MIN_OS_VERSION} "tvos" +setup_framework_structure "build-tvos-device" ${TVOS_MIN_OS_VERSION} "tvos" + +# Create dynamic libraries from static libraries +echo "Creating dynamic libraries from static libraries..." +combine_static_libraries "build-ios-sim" "Release-iphonesimulator" "ios" "true" +combine_static_libraries "build-ios-device" "Release-iphoneos" "ios" "false" +combine_static_libraries "build-macos" "Release" "macos" "false" +combine_static_libraries "build-visionos" "Release-xros" "visionos" "false" +combine_static_libraries "build-visionos-sim" "Release-xrsimulator" "visionos" "true" +combine_static_libraries "build-tvos-sim" "Release-appletvsimulator" "tvos" "true" +combine_static_libraries "build-tvos-device" "Release-appletvos" "tvos" "false" + +# Create XCFramework with correct debug symbols paths +echo "Creating XCFramework..." + +if [[ "${BUILD_STATIC_XCFRAMEWORK}" == "ON" ]]; then + xcodebuild -create-xcframework \ + -framework $(pwd)/build-ios-sim/framework/whisper.framework \ + -framework $(pwd)/build-ios-device/framework/whisper.framework \ + -framework $(pwd)/build-macos/framework/whisper.framework \ + -framework $(pwd)/build-visionos/framework/whisper.framework \ + -framework $(pwd)/build-visionos-sim/framework/whisper.framework \ + -framework $(pwd)/build-tvos-device/framework/whisper.framework \ + -framework $(pwd)/build-tvos-sim/framework/whisper.framework \ + -output $(pwd)/build-apple/whisper.xcframework + exit 0 +fi + +xcodebuild -create-xcframework \ + -framework $(pwd)/build-ios-sim/framework/whisper.framework \ + -debug-symbols $(pwd)/build-ios-sim/dSYMs/whisper.dSYM \ + -framework $(pwd)/build-ios-device/framework/whisper.framework \ + -debug-symbols $(pwd)/build-ios-device/dSYMs/whisper.dSYM \ + -framework $(pwd)/build-macos/framework/whisper.framework \ + -debug-symbols $(pwd)/build-macos/dSYMs/whisper.dSYM \ + -framework $(pwd)/build-visionos/framework/whisper.framework \ + -debug-symbols $(pwd)/build-visionos/dSYMs/whisper.dSYM \ + -framework $(pwd)/build-visionos-sim/framework/whisper.framework \ + -debug-symbols $(pwd)/build-visionos-sim/dSYMs/whisper.dSYM \ + -framework $(pwd)/build-tvos-device/framework/whisper.framework \ + -debug-symbols $(pwd)/build-tvos-device/dSYMs/whisper.dSYM \ + -framework $(pwd)/build-tvos-sim/framework/whisper.framework \ + -debug-symbols $(pwd)/build-tvos-sim/dSYMs/whisper.dSYM \ + -output $(pwd)/build-apple/whisper.xcframework diff --git a/ci/README.md b/ci/README.md new file mode 100644 index 0000000..63df4aa --- /dev/null +++ b/ci/README.md @@ -0,0 +1,41 @@ +# CI + +In addition to [Github Actions](https://github.com/ggerganov/whisper.cpp/actions) `whisper.cpp` uses a custom CI framework: + +https://github.com/ggml-org/ci + +It monitors the `master` branch for new commits and runs the +[ci/run.sh](https://github.com/ggerganov/whisper.cpp/blob/master/ci/run.sh) script on dedicated cloud instances. This allows us +to execute heavier workloads compared to just using Github Actions. Also with time, the cloud instances will be scaled +to cover various hardware architectures, including GPU and Apple Silicon instances. + +Collaborators can optionally trigger the CI run by adding the `ggml-ci` keyword to their commit message. +Only the branches of this repo are monitored for this keyword. + +It is a good practice, before publishing changes to execute the full CI locally on your machine: + +```bash +mkdir tmp + +# CPU-only build +bash ./ci/run.sh ./tmp/results ./tmp/mnt + +# with CUDA support +GG_BUILD_CUDA=1 bash ./ci/run.sh ./tmp/results ./tmp/mnt +``` + +## Environment Variables + +The CI script supports several environment variables to control the build: + +| Variable | Description | +|----------|-------------| +| `GG_BUILD_CUDA` | Enable NVIDIA CUDA GPU acceleration | +| `GG_BUILD_SYCL` | Enable Intel SYCL acceleration | +| `GG_BUILD_VULKAN` | Enable Vulkan GPU acceleration | +| `GG_BUILD_METAL` | Enable Metal acceleration on Apple Silicon | +| `GG_BUILD_BLAS` | Enable BLAS CPU acceleration | +| `GG_BUILD_OPENVINO` | Enable OpenVINO support | +| `GG_BUILD_COREML` | Enable Core ML support for Apple Neural Engine | +| `GG_BUILD_LOW_PERF` | Limit tests for low-performance hardware | +| `GG_BUILD_TEST_MODELS` | Comma-separated list of models to test (e.g. "tiny.en,tiny,base,medium", defaults to all models unless `GG_BUILD_LOW_PERF` is set) | diff --git a/ci/ruleset-nightly.json b/ci/ruleset-nightly.json new file mode 100644 index 0000000..87e68b0 --- /dev/null +++ b/ci/ruleset-nightly.json @@ -0,0 +1,26 @@ +{ + "name": "releases-nightly", + "target": "tag", + "enforcement": "active", + "bypass_actors": [ + { + "actor_id": null, + "actor_type": "DeployKey", + "bypass_mode": "always" + } + ], + "conditions": { + "ref_name": { + "include": [ + "refs/tags/b*" + ], + "exclude": [] + } + }, + "rules": [ + { "type": "deletion" }, + { "type": "non_fast_forward" }, + { "type": "update" }, + { "type": "creation" } + ] +} diff --git a/ci/ruleset-official.json b/ci/ruleset-official.json new file mode 100644 index 0000000..b94280a --- /dev/null +++ b/ci/ruleset-official.json @@ -0,0 +1,39 @@ +{ + "name": "releases-official", + "target": "tag", + "enforcement": "active", + "bypass_actors": [ + { + "actor_id": null, + "actor_type": "DeployKey", + "bypass_mode": "always" + } + ], + "conditions": { + "ref_name": { + "include": [ + "refs/tags/v*" + ], + "exclude": [] + } + }, + "rules": [ + { "type": "deletion" }, + { "type": "non_fast_forward" }, + { + "type": "required_status_checks", + "parameters": { + "strict_required_status_checks_policy": false, + "do_not_enforce_on_create": false, + "required_status_checks": [ + { + "context": "release", + "integration_id": 15368 + } + ] + } + }, + { "type": "update" }, + { "type": "creation" } + ] +} diff --git a/ci/run.sh b/ci/run.sh new file mode 100644 index 0000000..dca4476 --- /dev/null +++ b/ci/run.sh @@ -0,0 +1,390 @@ +#!/bin/bash +# +# sample usage: +# +# mkdir tmp +# +# # CPU-only build +# bash ./ci/run.sh ./tmp/results ./tmp/mnt +# +# # with CUDA support +# GG_BUILD_CUDA=1 bash ./ci/run.sh ./tmp/results ./tmp/mnt +# +# # with SYCL support +# GG_BUILD_SYCL=1 bash ./ci/run.sh ./tmp/results ./tmp/mnt + +if [ -z "$2" ]; then + echo "usage: $0 <output-dir> <mnt-dir>" + exit 1 +fi + +mkdir -p "$1" +mkdir -p "$2" + +OUT=$(realpath "$1") +MNT=$(realpath "$2") + +rm -vf $OUT/*.log +rm -vf $OUT/*.exit +rm -vf $OUT/*.md + +sd=`dirname $0` +cd $sd/../ +SRC=`pwd` + +ALL_MODELS=( "tiny.en" "tiny" "base.en" "base" "small.en" "small" "medium.en" "medium" "large-v1" "large-v2" "large-v3" "large-v3-turbo" ) +BENCH_N_THREADS=4 +BENCH_ENCODER_ONLY=0 +BENCH_FLASH_ATTN=0 + +# check for user-specified models first. if not specified, use fast models +if [ ! -z ${GG_BUILD_TEST_MODELS} ]; then + IFS=',' read -r -a MODELS <<< "${GG_BUILD_TEST_MODELS}" +else + if [ ! -z ${GG_BUILD_LOW_PERF} ]; then + MODELS=( "tiny" "base" "small" ) + else + MODELS=("${ALL_MODELS[@]}") + fi +fi + +CMAKE_EXTRA="-DWHISPER_FATAL_WARNINGS=ON" + +if [[ "$(uname -m)" == "x86_64" ]]; then + CMAKE_EXTRA="${CMAKE_EXTRA} -DGGML_NATIVE=OFF" +fi + +if [ ! -z ${GG_BUILD_METAL} ]; then + CMAKE_EXTRA="${CMAKE_EXTRA} -DGGML_METAL=ON" +fi + +if [ ! -z ${GG_BUILD_CUDA} ]; then + CMAKE_EXTRA="${CMAKE_EXTRA} -DGGML_CUDA=ON" + + if command -v nvidia-smi >/dev/null 2>&1; then + CUDA_ARCH=$(nvidia-smi --query-gpu=compute_cap --format=csv,noheader,nounits 2>/dev/null | head -1 | tr -d '.') + if [[ -n "$CUDA_ARCH" && "$CUDA_ARCH" =~ ^[0-9]+$ ]]; then + CMAKE_EXTRA="${CMAKE_EXTRA} -DCMAKE_CUDA_ARCHITECTURES=${CUDA_ARCH}" + else + echo "Warning: Using fallback CUDA architectures" + CMAKE_EXTRA="${CMAKE_EXTRA} -DCMAKE_CUDA_ARCHITECTURES=61;70;75;80;86;89" + fi + else + echo "Error: nvidia-smi not found, cannot build with CUDA" + exit 1 + fi +fi + +if [ ! -z ${GG_BUILD_ROCM} ]; then + CMAKE_EXTRA="${CMAKE_EXTRA} -DGGML_HIP=ON" + if [ -z ${GG_BUILD_AMDGPU_TARGETS} ]; then + echo "Missing GG_BUILD_AMDGPU_TARGETS, please set it to your GPU architecture (e.g. gfx90a, gfx1100, etc.)" + exit 1 + fi + + CMAKE_EXTRA="${CMAKE_EXTRA} -DAMDGPU_TARGETS=${GG_BUILD_AMDGPU_TARGETS}" +fi + +if [ ! -z ${GG_BUILD_SYCL} ]; then + if [ -z ${ONEAPI_ROOT} ]; then + echo "Not detected ONEAPI_ROOT, please install oneAPI base toolkit and enable it by:" + echo "source /opt/intel/oneapi/setvars.sh" + exit 1 + fi + # Use only main GPU + export ONEAPI_DEVICE_SELECTOR="level_zero:0" + # Enable sysman for correct memory reporting + export ZES_ENABLE_SYSMAN=1 + # to circumvent precision issues on CPY operations + export SYCL_PROGRAM_COMPILE_OPTIONS="-cl-fp32-correctly-rounded-divide-sqrt" + CMAKE_EXTRA="${CMAKE_EXTRA} -DGGML_SYCL=1 -DCMAKE_C_COMPILER=icx -DCMAKE_CXX_COMPILER=icpx -DGGML_SYCL_F16=ON" +fi + +if [ ! -z ${GG_BUILD_VULKAN} ]; then + CMAKE_EXTRA="${CMAKE_EXTRA} -DGGML_VULKAN=1" + + # if on Mac, disable METAL + if [[ "$OSTYPE" == "darwin"* ]]; then + CMAKE_EXTRA="${CMAKE_EXTRA} -DGGML_METAL=OFF -DGGML_BLAS=OFF" + fi + +fi + +if [ ! -z ${GG_BUILD_WEBGPU} ]; then + CMAKE_EXTRA="${CMAKE_EXTRA} -DGGML_WEBGPU=1" +fi + +if [ ! -z ${GG_BUILD_MUSA} ]; then + # Use qy1 by default (MTT S80) + MUSA_ARCH=${MUSA_ARCH:-21} + CMAKE_EXTRA="${CMAKE_EXTRA} -DGGML_MUSA=ON -DMUSA_ARCHITECTURES=${MUSA_ARCH}" +fi + +if [ ! -z ${GG_BUILD_NO_SVE} ]; then + # arm 9 and newer enables sve by default, adjust these flags depending on the cpu used + CMAKE_EXTRA="${CMAKE_EXTRA} -DGGML_NATIVE=OFF -DGGML_CPU_ARM_ARCH=armv8.5-a+fp16+i8mm" +fi + +## helpers + +# download a file if it does not exist or if it is outdated +function gg_wget { + local out=$1 + local url=$2 + + local cwd=`pwd` + + mkdir -p $out + cd $out + + # should not re-download if file is the same + wget -nv -N $url + + cd $cwd +} + +function gg_download_model { + local model_name=$1 + local model_file="$MNT/models/ggml-${model_name}.bin" + + if [ ! -f ${model_file} ]; then + local cwd=`pwd` + mkdir -p "$MNT/models" + cd "$MNT/models" + set -x + bash "$cwd/models/download-ggml-model.sh" ${model_name} . + local download_status=$? + set +x + cd "$cwd" + if [ $download_status -ne 0 ]; then + echo "Error: failed to download model ${model_name}" + ret=1 + fi + fi +} + +function gg_printf { + printf -- "$@" >> $OUT/README.md +} + +# Helper function to check command exit status +function gg_check_last_command_status { + local exit_file=$1 + local command_name=$2 + + local exit_status=$? + echo "$exit_status" > "$exit_file" + + if [ $exit_status -ne 0 ]; then + echo "Error: Command $command_name failed with exit status $exit_status" + return 1 + fi + + return 0 +} + +# Usage: gg_run <test_name> [additional_args...] +# +# Parameters: +# test_name - Name of the test to run (calls gg_run_<test_name>) +# additional_args - Any additional arguments to pass to the test function (first argument is appended to the log filename) +function gg_run { + ci=$1 + + if [ $# -gt 1 ]; then + ci="${ci}_${2}" + fi + + set -o pipefail + set -x + + gg_run_$1 "$@" | tee $OUT/$ci.log + cur=$? + echo "$cur" > $OUT/$ci.exit + + set +x + set +o pipefail + + gg_sum_$1 "$@" + + ret=$((ret | cur)) +} + +function gg_check_build_requirements { + if ! command -v cmake &> /dev/null; then + gg_printf 'cmake not found, please install' + fi + + if ! command -v make &> /dev/null; then + gg_printf 'make not found, please install' + fi +} + +## ci + +function gg_run_ctest { + mode=$2 + + cd ${SRC} + + rm -rf build-ci-${mode} && mkdir build-ci-${mode} && cd build-ci-${mode} + + set -e + + gg_check_build_requirements + + (time cmake -DCMAKE_BUILD_TYPE=${mode} ${CMAKE_EXTRA} .. ) 2>&1 | tee -a $OUT/${ci}-cmake.log + (time make -j$(nproc) ) 2>&1 | tee -a $OUT/${ci}-make.log + + (time ctest --output-on-failure -L main -E test-opt ) 2>&1 | tee -a $OUT/${ci}-ctest.log + + set +e +} + +function gg_sum_ctest { + mode=$2 + + gg_printf '### %s\n\n' "${ci}" + + gg_printf 'Runs ctest in '${mode}' mode\n' + gg_printf '- status: %s\n' "$(cat $OUT/${ci}.exit)" + gg_printf '```\n' + gg_printf '%s\n' "$(cat $OUT/${ci}-ctest.log)" + gg_printf '```\n' +} + +function gg_run_bench { + cd ${SRC} + + # set flash attention flag if enabled + fattn="-nfa" + if [ "$BENCH_FLASH_ATTN" -eq 1 ]; then + fattn="-fa" + fi + + # run memcpy benchmark if not encoder-only mode + if [ "$BENCH_ENCODER_ONLY" -eq 0 ]; then + echo "Running memcpy benchmark" + (time ./build-ci-release/bin/whisper-bench -w 1 -t $BENCH_N_THREADS 2>&1) | tee -a $OUT/${ci}-memcpy.log + gg_check_last_command_status "$OUT/${ci}-memcpy.exit" "memcpy benchmark" + + echo "Running ggml_mul_mat benchmark with $BENCH_N_THREADS threads" + (time ./build-ci-release/bin/whisper-bench -w 2 -t $BENCH_N_THREADS 2>&1) | tee -a $OUT/${ci}-mul_mat.log + gg_check_last_command_status "$OUT/${ci}-mul_mat.exit" "ggml_mul_mat benchmark" + fi + + echo "Running benchmark for all models" + + # generate header for the benchmark table + { + printf "| %16s | %13s | %3s | %3s | %7s | %7s | %7s | %7s | %7s |\n" "Config" "Model" "Th" "FA" "Enc." "Dec." "Bch5" "PP" "Commit" + printf "| %16s | %13s | %3s | %3s | %7s | %7s | %7s | %7s | %7s |\n" "---" "---" "---" "---" "---" "---" "---" "---" "---" + } | tee -a $OUT/${ci}-models-table.log + + res=0 + + # run benchmark for each model + for model in "${MODELS[@]}"; do + echo "Benchmarking model: $model" + + # run the benchmark and capture output + output=$(./build-ci-release/bin/whisper-bench -m $MNT/models/ggml-$model.bin -t $BENCH_N_THREADS $fattn 2>&1) + ret=$? + + # save the raw output + echo "$output" > $OUT/${ci}-bench-$model.log + + if [ $ret -eq 0 ]; then + # parse the benchmark results + encode_time=$(echo "$output" | grep "encode time" | awk '{print $11}') + decode_time=$(echo "$output" | grep "decode time" | awk '{print $11}') + batchd_time=$(echo "$output" | grep "batchd time" | awk '{print $11}') + prompt_time=$(echo "$output" | grep "prompt time" | awk '{print $11}') + system_info=$(echo "$output" | grep "system_info") + actual_threads=$(echo "$output" | grep "system_info" | awk '{print $4}') + + # determine configuration + config="" + if [[ $system_info == *"AVX2 = 1"* ]]; then + config="$config AVX2" + fi + if [[ $system_info == *"NEON = 1"* ]]; then + config="$config NEON" + fi + if [[ $system_info == *"BLAS = 1"* ]]; then + config="$config BLAS" + fi + if [[ $system_info == *"COREML = 1"* ]]; then + config="$config COREML" + fi + if [[ $system_info == *"CUDA = 1"* ]]; then + config="$config CUDA" + fi + if [[ $system_info == *"METAL = 1"* ]]; then + config="$config METAL" + fi + + # get commit hash + commit=$(git rev-parse --short HEAD) + + # add row to benchmark table + printf "| %16s | %13s | %3s | %3s | %7s | %7s | %7s | %7s | %7s |\n" \ + "$config" "$model" "$actual_threads" "$BENCH_FLASH_ATTN" "$encode_time" "$decode_time" "$batchd_time" "$prompt_time" "$commit" \ + | tee -a $OUT/${ci}-models-table.log + else + echo "Benchmark failed for model: $model" | tee -a $OUT/${ci}-bench-errors.log + res=1 + fi + done + + return $res +} + +function gg_sum_bench { + gg_printf '### %s\n\n' "${ci}" + + gg_printf 'Whisper Benchmark Results\n' + gg_printf '- status: %s\n' "$(cat $OUT/${ci}.exit)" + + # show memcpy and ggml_mul_mat benchmark results if available + if [ "$BENCH_ENCODER_ONLY" -eq 0 ]; then + if [ -f "$OUT/${ci}-memcpy.log" ]; then + gg_printf '#### memcpy Benchmark\n\n' + gg_printf '```\n%s\n```\n\n' "$(cat $OUT/${ci}-memcpy.log)" + fi + + if [ -f "$OUT/${ci}-mul_mat.log" ]; then + gg_printf '#### ggml_mul_mat Benchmark\n\n' + gg_printf '```\n%s\n```\n\n' "$(cat $OUT/${ci}-mul_mat.log)" + fi + fi + + # show model benchmark results + gg_printf '#### Model Benchmarks\n\n' + if [ -f "$OUT/${ci}-models-table.log" ]; then + gg_printf '%s\n\n' "$(cat $OUT/${ci}-models-table.log)" + else + gg_printf 'No model benchmark results available.\n\n' + fi + + # show any errors that occurred + if [ -f "$OUT/${ci}-bench-errors.log" ]; then + gg_printf '#### Benchmark Errors\n\n' + gg_printf '```\n%s\n```\n\n' "$(cat $OUT/${ci}-bench-errors.log)" + fi +} + +ret=0 + +for model in "${MODELS[@]}"; do + test $ret -eq 0 && gg_download_model ${model} +done + +test $ret -eq 0 && gg_run ctest debug +test $ret -eq 0 && gg_run ctest release + +test $ret -eq 0 && gg_run bench + +cat $OUT/README.md + +exit $ret diff --git a/cmake/DefaultTargetOptions.cmake b/cmake/DefaultTargetOptions.cmake new file mode 100644 index 0000000..0cfbb34 --- /dev/null +++ b/cmake/DefaultTargetOptions.cmake @@ -0,0 +1,16 @@ +# Set the default compile features and properties for a target. + +if (NOT TARGET) + message(FATAL_ERROR "TARGET not set before including DefaultTargetOptions") +endif() + +target_compile_features(${TARGET} + PRIVATE + cxx_std_11 + ) + +set_target_properties(${TARGET} + PROPERTIES + EXPORT_COMPILE_COMMANDS ON + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" +) diff --git a/cmake/FindFFmpeg.cmake b/cmake/FindFFmpeg.cmake new file mode 100644 index 0000000..bb6aff6 --- /dev/null +++ b/cmake/FindFFmpeg.cmake @@ -0,0 +1,163 @@ +# From +# https://github.com/snikulov/cmake-modules/blob/master/FindFFmpeg.cmake +# +# vim: ts=2 sw=2 +# - Try to find the required ffmpeg components(default: AVFORMAT, AVUTIL, AVCODEC) +# +# Once done this will define +# FFMPEG_FOUND - System has the all required components. +# FFMPEG_INCLUDE_DIRS - Include directory necessary for using the required components headers. +# FFMPEG_LIBRARIES - Link these to use the required ffmpeg components. +# FFMPEG_DEFINITIONS - Compiler switches required for using the required ffmpeg components. +# +# For each of the components it will additionally set. +# - AVCODEC +# - AVDEVICE +# - AVFORMAT +# - AVFILTER +# - AVUTIL +# - POSTPROC +# - SWSCALE +# the following variables will be defined +# <component>_FOUND - System has <component> +# <component>_INCLUDE_DIRS - Include directory necessary for using the <component> headers +# <component>_LIBRARIES - Link these to use <component> +# <component>_DEFINITIONS - Compiler switches required for using <component> +# <component>_VERSION - The components version +# +# Copyright (c) 2006, Matthias Kretz, <kretz@kde.org> +# Copyright (c) 2008, Alexander Neundorf, <neundorf@kde.org> +# Copyright (c) 2011, Michael Jansen, <kde@michael-jansen.biz> +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +include(FindPackageHandleStandardArgs) + +# The default components were taken from a survey over other FindFFMPEG.cmake files +if (NOT FFmpeg_FIND_COMPONENTS) + set(FFmpeg_FIND_COMPONENTS AVFORMAT AVCODEC AVUTIL SWRESAMPLE) +endif() + +# +### Macro: set_component_found +# +# Marks the given component as found if both *_LIBRARIES AND *_INCLUDE_DIRS is present. +# +macro(set_component_found _component ) + if (${_component}_LIBRARIES AND ${_component}_INCLUDE_DIRS) + message(DEBUG " - ${_component} found.") + set(${_component}_FOUND TRUE) + else () + message(DEBUG " - ${_component} not found.") + endif () +endmacro() + +# +### Macro: find_component +# +# Checks for the given component by invoking pkgconfig and then looking up the libraries and +# include directories. +# +macro(find_component _component _pkgconfig _library _header) + + if (NOT WIN32) + # use pkg-config to get the directories and then use these values + # in the FIND_PATH() and FIND_LIBRARY() calls + find_package(PkgConfig) + if (PKG_CONFIG_FOUND) + pkg_check_modules(PC_${_component} ${_pkgconfig}) + message(STATUS "Pkgconfig found: ${PC_${_component}_INCLUDEDIR}") + message(STATUS "Pkgconfig found: ${PC_${_component}_INCLUDE_DIRS}") + message(STATUS "${PC_${_component}_CFLAGS}") + endif () + endif (NOT WIN32) + + + find_path(${_component}_INCLUDE_DIRS ${_header} + HINTS + ${PC_${_component}_INCLUDEDIR} + ${PC_${_component}_INCLUDE_DIRS} + PATH_SUFFIXES + ffmpeg + ) + + # CMake's default is to search first for shared libraries and then for static libraries. + # Todo later: add option to prefer static libs over dynamic: + find_library(${_component}_LIBRARIES NAMES ${_library} lib${_library}.a + HINTS + ${PC_${_component}_LIBDIR} + ${PC_${_component}_LIBRARY_DIRS} + ) + + set(${_component}_DEFINITIONS ${PC_${_component}_CFLAGS_OTHER} CACHE STRING "The ${_component} CFLAGS.") + set(${_component}_VERSION ${PC_${_component}_VERSION} CACHE STRING "The ${_component} version number.") + + set_component_found(${_component}) + + mark_as_advanced( + ${_component}_INCLUDE_DIRS + ${_component}_LIBRARIES + ${_component}_DEFINITIONS + ${_component}_VERSION) + +endmacro() + + +# Check for cached results. If there are skip the costly part. +if (NOT FFMPEG_LIBRARIES) + + # Check for all possible component. + find_component(AVCODEC libavcodec avcodec libavcodec/avcodec.h) + find_component(AVFORMAT libavformat avformat libavformat/avformat.h) + find_component(AVDEVICE libavdevice avdevice libavdevice/avdevice.h) + #find_component(AVRESAMPLE libavresample avresample libavresample/avresample.h) # old name for swresample + find_component(AVUTIL libavutil avutil libavutil/avutil.h) + find_component(AVFILTER libavfilter avfilter libavfilter/avfilter.h) + find_component(SWSCALE libswscale swscale libswscale/swscale.h) + find_component(POSTPROC libpostproc postproc libpostproc/postprocess.h) + find_component(SWRESAMPLE libswresample swresample libswresample/swresample.h) + + # Check if the required components were found and add their stuff to the FFMPEG_* vars. + foreach (_component ${FFmpeg_FIND_COMPONENTS}) + if (${_component}_FOUND) + # message(STATUS "Required component ${_component} present.") + set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${${_component}_LIBRARIES}) + set(FFMPEG_DEFINITIONS ${FFMPEG_DEFINITIONS} ${${_component}_DEFINITIONS}) + list(APPEND FFMPEG_INCLUDE_DIRS ${${_component}_INCLUDE_DIRS}) + else () + # message(STATUS "Required component ${_component} missing.") + endif () + endforeach () + + # Build the include path with duplicates removed. + if (FFMPEG_INCLUDE_DIRS) + list(REMOVE_DUPLICATES FFMPEG_INCLUDE_DIRS) + endif () + + # cache the vars. + set(FFMPEG_INCLUDE_DIRS ${FFMPEG_INCLUDE_DIRS} CACHE STRING "The FFmpeg include directories." FORCE) + set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} CACHE STRING "The FFmpeg libraries." FORCE) + set(FFMPEG_DEFINITIONS ${FFMPEG_DEFINITIONS} CACHE STRING "The FFmpeg cflags." FORCE) + + mark_as_advanced(FFMPEG_INCLUDE_DIRS + FFMPEG_LIBRARIES + FFMPEG_DEFINITIONS) + +endif () + +# Now set the noncached _FOUND vars for the components. +# whisper.cpp does not need SWSCALE +foreach (_component AVCODEC AVDEVICE AVFORMAT AVRESAMPLE AVUTIL POSTPROCESS) + set_component_found(${_component}) +endforeach () + +# Compile the list of required vars +set(_FFmpeg_REQUIRED_VARS FFMPEG_LIBRARIES FFMPEG_INCLUDE_DIRS) +foreach (_component ${FFmpeg_FIND_COMPONENTS}) + list(APPEND _FFmpeg_REQUIRED_VARS ${_component}_LIBRARIES ${_component}_INCLUDE_DIRS) +endforeach () + +# Give a nice error message if some of the required vars are missing. +find_package_handle_standard_args(FFmpeg DEFAULT_MSG ${_FFmpeg_REQUIRED_VARS}) + diff --git a/cmake/arm64-apple-clang.cmake b/cmake/arm64-apple-clang.cmake new file mode 100644 index 0000000..5fcd288 --- /dev/null +++ b/cmake/arm64-apple-clang.cmake @@ -0,0 +1,16 @@ +set( CMAKE_SYSTEM_NAME Darwin ) +set( CMAKE_SYSTEM_PROCESSOR arm64 ) + +set( target arm64-apple-darwin-macho ) + +set( CMAKE_C_COMPILER clang ) +set( CMAKE_CXX_COMPILER clang++ ) + +set( CMAKE_C_COMPILER_TARGET ${target} ) +set( CMAKE_CXX_COMPILER_TARGET ${target} ) + +set( arch_c_flags "-march=armv8.4-a -fvectorize -ffp-model=fast -fno-finite-math-only" ) +set( warn_c_flags "-Wno-format -Wno-unused-variable -Wno-unused-function" ) + +set( CMAKE_C_FLAGS_INIT "${arch_c_flags} ${warn_c_flags}" ) +set( CMAKE_CXX_FLAGS_INIT "${arch_c_flags} ${warn_c_flags}" ) diff --git a/cmake/arm64-windows-llvm-cuda.cmake b/cmake/arm64-windows-llvm-cuda.cmake new file mode 100644 index 0000000..526a5a2 --- /dev/null +++ b/cmake/arm64-windows-llvm-cuda.cmake @@ -0,0 +1,24 @@ +include( ${CMAKE_CURRENT_LIST_DIR}/arm64-windows-llvm.cmake ) + +if ( DEFINED CUDAToolkit_ROOT ) + file( TO_CMAKE_PATH "${CUDAToolkit_ROOT}" CUDA_ROOT ) +elseif ( DEFINED ENV{CUDA_PATH} ) + file( TO_CMAKE_PATH "$ENV{CUDA_PATH}" CUDA_ROOT ) +else() + message( FATAL_ERROR "Set CUDAToolkit_ROOT or CUDA_PATH to a Windows CUDA Toolkit with ARM64 target libraries" ) +endif() + +if ( DEFINED ENV{VCToolsInstallDir} ) + file( TO_CMAKE_PATH "$ENV{VCToolsInstallDir}" MSVC_TOOLS_ROOT ) + set( CMAKE_CUDA_HOST_COMPILER "${MSVC_TOOLS_ROOT}/bin/Hostx64/arm64/cl.exe" CACHE FILEPATH "" ) +endif() + +set( CMAKE_CUDA_COMPILER "${CUDA_ROOT}/bin/nvcc.exe" CACHE FILEPATH "" ) +set( CMAKE_CUDA_FLAGS_INIT "-target-dir=arm64" ) + +# FindCUDAToolkit selects lib/x64 from the host architecture on Windows. +set( CUDA_CUDART "${CUDA_ROOT}/lib/arm64/cudart.lib" CACHE FILEPATH "" ) +set( CUDA_cudart_LIBRARY "${CUDA_ROOT}/lib/arm64/cudart.lib" CACHE FILEPATH "" ) +set( CUDA_cublas_LIBRARY "${CUDA_ROOT}/lib/arm64/cublas.lib" CACHE FILEPATH "" ) +set( CUDA_cublasLt_LIBRARY "${CUDA_ROOT}/lib/arm64/cublasLt.lib" CACHE FILEPATH "" ) +set( CUDA_cuda_driver_LIBRARY "${CUDA_ROOT}/lib/arm64/cuda.lib" CACHE FILEPATH "" ) diff --git a/cmake/arm64-windows-llvm.cmake b/cmake/arm64-windows-llvm.cmake new file mode 100644 index 0000000..8023796 --- /dev/null +++ b/cmake/arm64-windows-llvm.cmake @@ -0,0 +1,16 @@ +set( CMAKE_SYSTEM_NAME Windows ) +set( CMAKE_SYSTEM_PROCESSOR arm64 ) + +set( target arm64-pc-windows-msvc ) + +set( CMAKE_C_COMPILER clang ) +set( CMAKE_CXX_COMPILER clang++ ) + +set( CMAKE_C_COMPILER_TARGET ${target} ) +set( CMAKE_CXX_COMPILER_TARGET ${target} ) + +set( arch_c_flags "-march=armv8.7-a -fvectorize -ffp-model=fast -fno-finite-math-only" ) +set( warn_c_flags "-Wno-format -Wno-unused-variable -Wno-unused-function -Wno-gnu-zero-variadic-macro-arguments" ) + +set( CMAKE_C_FLAGS_INIT "${arch_c_flags} ${warn_c_flags}" ) +set( CMAKE_CXX_FLAGS_INIT "${arch_c_flags} ${warn_c_flags}" ) diff --git a/cmake/build-info.cmake b/cmake/build-info.cmake new file mode 100644 index 0000000..b293c9b --- /dev/null +++ b/cmake/build-info.cmake @@ -0,0 +1,60 @@ +set(BUILD_NUMBER 0) +set(BUILD_COMMIT "unknown") +set(BUILD_COMPILER "unknown") +set(BUILD_TARGET "unknown") + +# Look for git +find_package(Git) +if(NOT Git_FOUND) + find_program(GIT_EXECUTABLE NAMES git git.exe) + if(GIT_EXECUTABLE) + set(Git_FOUND TRUE) + message(STATUS "Found Git: ${GIT_EXECUTABLE}") + else() + message(WARNING "Git not found. Build info will not be accurate.") + endif() +endif() + +# Get the commit count and hash +if(Git_FOUND) + execute_process( + COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VARIABLE HEAD + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE RES + ) + if (RES EQUAL 0) + set(BUILD_COMMIT ${HEAD}) + endif() + execute_process( + COMMAND ${GIT_EXECUTABLE} rev-list --count HEAD + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VARIABLE COUNT + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE RES + ) + if (RES EQUAL 0) + set(BUILD_NUMBER ${COUNT}) + endif() +endif() + +if(MSVC) + set(BUILD_COMPILER "${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER_VERSION}") + set(BUILD_TARGET ${CMAKE_VS_PLATFORM_NAME}) + add_compile_options("$<$<COMPILE_LANGUAGE:C>:/utf-8>") + add_compile_options("$<$<COMPILE_LANGUAGE:CXX>:/utf-8>") +else() + execute_process( + COMMAND sh -c "$@ --version | head -1" _ ${CMAKE_C_COMPILER} + OUTPUT_VARIABLE OUT + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + set(BUILD_COMPILER ${OUT}) + execute_process( + COMMAND ${CMAKE_C_COMPILER} -dumpmachine + OUTPUT_VARIABLE OUT + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + set(BUILD_TARGET ${OUT}) +endif() diff --git a/cmake/git-vars.cmake b/cmake/git-vars.cmake new file mode 100644 index 0000000..1a4c24e --- /dev/null +++ b/cmake/git-vars.cmake @@ -0,0 +1,22 @@ +find_package(Git) + +# the commit's SHA1 +execute_process(COMMAND + "${GIT_EXECUTABLE}" describe --match=NeVeRmAtCh --always --abbrev=8 + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" + OUTPUT_VARIABLE GIT_SHA1 + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + +# the date of the commit +execute_process(COMMAND + "${GIT_EXECUTABLE}" log -1 --format=%ad --date=local + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" + OUTPUT_VARIABLE GIT_DATE + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + +# the subject of the commit +execute_process(COMMAND + "${GIT_EXECUTABLE}" log -1 --format=%s + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" + OUTPUT_VARIABLE GIT_COMMIT_SUBJECT + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) diff --git a/cmake/parakeet-config.cmake.in b/cmake/parakeet-config.cmake.in new file mode 100644 index 0000000..4895d7d --- /dev/null +++ b/cmake/parakeet-config.cmake.in @@ -0,0 +1,30 @@ +set(PARAKEET_VERSION @WHISPER_VERSION@) +set(PARAKEET_BUILD_COMMIT @WHISPER_BUILD_COMMIT@) +set(PARAKEET_BUILD_NUMBER @WHISPER_BUILD_NUMBER@) +set(PARAKEET_SHARED_LIB @BUILD_SHARED_LIBS@) + +@PACKAGE_INIT@ + +set_and_check(PARAKEET_INCLUDE_DIR "@PACKAGE_PARAKEET_INCLUDE_INSTALL_DIR@") +set_and_check(PARAKEET_LIB_DIR "@PACKAGE_PARAKEET_LIB_INSTALL_DIR@") +set(PARAKEET_BIN_DIR "@PACKAGE_PARAKEET_BIN_INSTALL_DIR@") + +find_package(ggml REQUIRED HINTS ${PARAKEET_LIB_DIR}/cmake) + +find_library(parakeet_LIBRARY parakeet + REQUIRED + HINTS ${PARAKEET_LIB_DIR} + NO_CMAKE_FIND_ROOT_PATH +) + +add_library(parakeet UNKNOWN IMPORTED) +set_target_properties(parakeet + PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${PARAKEET_INCLUDE_DIR}" + INTERFACE_LINK_LIBRARIES "ggml::ggml;ggml::ggml-base;" + IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" + IMPORTED_LOCATION "${parakeet_LIBRARY}" + INTERFACE_COMPILE_FEATURES cxx_std_11 + POSITION_INDEPENDENT_CODE ON) + +check_required_components(parakeet) diff --git a/cmake/parakeet.pc.in b/cmake/parakeet.pc.in new file mode 100644 index 0000000..de1acb7 --- /dev/null +++ b/cmake/parakeet.pc.in @@ -0,0 +1,10 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=@CMAKE_INSTALL_FULL_LIBDIR@ +includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ + +Name: parakeet +Description: Port of NVIDIA's Parakeet model in C/C++ +Version: @WHISPER_VERSION@ +Libs: -L${libdir} -lggml -lggml-base -lparakeet +Cflags: -I${includedir} diff --git a/cmake/riscv64-spacemit-linux-gnu-gcc.cmake b/cmake/riscv64-spacemit-linux-gnu-gcc.cmake new file mode 100644 index 0000000..08fdbf5 --- /dev/null +++ b/cmake/riscv64-spacemit-linux-gnu-gcc.cmake @@ -0,0 +1,29 @@ +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR riscv64) +set(CMAKE_SYSTEM_VERSION 1) + +if (CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^(riscv)") + message(STATUS "HOST SYSTEM ${CMAKE_HOST_SYSTEM_PROCESSOR}") +else() + set(GNU_MACHINE riscv64-unknown-linux-gnu CACHE STRING "GNU compiler triple") + if (DEFINED ENV{RISCV_ROOT_PATH}) + file(TO_CMAKE_PATH $ENV{RISCV_ROOT_PATH} RISCV_ROOT_PATH) + else() + message(FATAL_ERROR "RISCV_ROOT_PATH env must be defined") + endif() + + set(RISCV_ROOT_PATH ${RISCV_ROOT_PATH} CACHE STRING "root path to riscv toolchain") + set(CMAKE_C_COMPILER ${RISCV_ROOT_PATH}/bin/riscv64-unknown-linux-gnu-gcc) + set(CMAKE_CXX_COMPILER ${RISCV_ROOT_PATH}/bin/riscv64-unknown-linux-gnu-g++) + set(CMAKE_STRIP ${RISCV_ROOT_PATH}/bin/riscv64-unknown-linux-gnu-strip) + set(CMAKE_FIND_ROOT_PATH "${RISCV_ROOT_PATH}/riscv64-unknown-linux-gnu") + set(CMAKE_SYSROOT "${RISCV_ROOT_PATH}/sysroot") +endif() + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) +set(CMAKE_C_FLAGS "-march=rv64gcv_zfh_zba_zicbop -mabi=lp64d ${CMAKE_C_FLAGS}") +set(CMAKE_CXX_FLAGS "-march=rv64gcv_zfh_zba_zicbop -mabi=lp64d ${CXX_FLAGS}") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -latomic") diff --git a/cmake/whisper-config.cmake.in b/cmake/whisper-config.cmake.in new file mode 100644 index 0000000..551abe1 --- /dev/null +++ b/cmake/whisper-config.cmake.in @@ -0,0 +1,30 @@ +set(WHISPER_VERSION @WHISPER_VERSION@) +set(WHISPER_BUILD_COMMIT @WHISPER_BUILD_COMMIT@) +set(WHISPER_BUILD_NUMBER @WHISPER_BUILD_NUMBER@) +set(WHISPER_SHARED_LIB @BUILD_SHARED_LIBS@) + +@PACKAGE_INIT@ + +set_and_check(WHISPER_INCLUDE_DIR "@PACKAGE_WHISPER_INCLUDE_INSTALL_DIR@") +set_and_check(WHISPER_LIB_DIR "@PACKAGE_WHISPER_LIB_INSTALL_DIR@") +set(WHISPER_BIN_DIR "@PACKAGE_WHISPER_BIN_INSTALL_DIR@") + +find_package(ggml REQUIRED HINTS ${WHISPER_LIB_DIR}/cmake) + +find_library(whisper_LIBRARY whisper + REQUIRED + HINTS ${WHISPER_LIB_DIR} + NO_CMAKE_FIND_ROOT_PATH +) + +add_library(whisper UNKNOWN IMPORTED) +set_target_properties(whisper + PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${WHISPER_INCLUDE_DIR}" + INTERFACE_LINK_LIBRARIES "ggml::ggml;ggml::ggml-base;" + IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" + IMPORTED_LOCATION "${whisper_LIBRARY}" + INTERFACE_COMPILE_FEATURES cxx_std_11 + POSITION_INDEPENDENT_CODE ON ) + +check_required_components(whisper) diff --git a/cmake/whisper.pc.in b/cmake/whisper.pc.in new file mode 100644 index 0000000..270fc2e --- /dev/null +++ b/cmake/whisper.pc.in @@ -0,0 +1,10 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=@CMAKE_INSTALL_FULL_LIBDIR@ +includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ + +Name: whisper +Description: Port of OpenAI's Whisper model in C/C++ +Version: @WHISPER_VERSION@ +Libs: -L${libdir} -lggml -lggml-base -lwhisper +Cflags: -I${includedir} diff --git a/cmake/x64-windows-llvm.cmake b/cmake/x64-windows-llvm.cmake new file mode 100644 index 0000000..77e7914 --- /dev/null +++ b/cmake/x64-windows-llvm.cmake @@ -0,0 +1,5 @@ +set( CMAKE_SYSTEM_NAME Windows ) +set( CMAKE_SYSTEM_PROCESSOR x86_64 ) + +set( CMAKE_C_COMPILER clang ) +set( CMAKE_CXX_COMPILER clang++ ) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..7aedb9d --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,127 @@ +# dependencies + +find_package(Threads REQUIRED) + +# third-party + +if (WHISPER_SDL2) + # SDL2 + find_package(SDL2 REQUIRED) + + string(STRIP "${SDL2_LIBRARIES}" SDL2_LIBRARIES) + + message(STATUS "SDL2_INCLUDE_DIRS = ${SDL2_INCLUDE_DIRS}") + message(STATUS "SDL2_LIBRARIES = ${SDL2_LIBRARIES}") +endif() + +# common + +set(TARGET common) + +unset(COMMON_EXTRA_LIBS) + +if (WHISPER_COMMON_FFMPEG) + # As of cmake 3.27, there is no official cmake support for FindFFmpeg. + # Consequnelty we added a FindFFmpeg.cmake script the cmake subfolder: + # whisper.cpp does not need the full ffmpeg libs, just AVFORMAT AVCODEC AVUTIL SWRESAMPLE + # libswresample performs highly optimized audio resampling, rematrixing and sample format conversion operations + # libavcodec provides a generic encoding/decoding framework and contains multiple decoders and encoders for audio, video and subtitle streams, and several bitstream filters. + # libavformat provides a generic framework for multiplexing and demultiplexing (muxing and demuxing) audio, video and subtitle streams. + find_package(FFmpeg REQUIRED) + + if (NOT ${FFMPEG_FOUND}) + message(FATAL_ERROR "Cannot find ffmpeg libs/headers") + endif() + + message(STATUS "Found ffmpeg libs: ${FFMPEG_LIBRARIES}") + message(STATUS "Found ffmpeg headers in: ${FFMPEG_INCLUDE_DIRS}") + message(STATUS "ffmpeg definitions: ${FFMPEG_DEFINITIONS}") + message(STATUS "Found avformat ${AVFORMAT_VERSION}") + + include_directories(${FFMPEG_INCLUDE_DIRS}) + add_compile_definitions(WHISPER_COMMON_FFMPEG) + + list(APPEND COMMON_EXTRA_LIBS ${FFMPEG_LIBRARIES}) + + set(COMMON_SOURCES_FFMPEG ffmpeg-transcode.cpp) +endif() + + +add_library(${TARGET} STATIC + common.h + common.cpp + common-ggml.h + common-ggml.cpp + common-whisper.h + common-whisper.cpp + grammar-parser.h + grammar-parser.cpp + ${COMMON_SOURCES_FFMPEG} + ) + +include(DefaultTargetOptions) + +target_link_libraries(${TARGET} PRIVATE whisper ${COMMON_EXTRA_LIBS} ${CMAKE_DL_LIBS}) + +set_target_properties(${TARGET} PROPERTIES POSITION_INDEPENDENT_CODE ON) +set_target_properties(${TARGET} PROPERTIES FOLDER "libs") + +if (WHISPER_SDL2) + # common-sdl + + set(TARGET common-sdl) + + add_library(${TARGET} STATIC + common-sdl.h + common-sdl.cpp + ) + + include(DefaultTargetOptions) + + target_include_directories(${TARGET} PUBLIC ${SDL2_INCLUDE_DIRS}) + target_link_libraries (${TARGET} PRIVATE ${SDL2_LIBRARIES}) + + set_target_properties(${TARGET} PROPERTIES POSITION_INDEPENDENT_CODE ON) + set_target_properties(${TARGET} PROPERTIES FOLDER "libs") +endif() + +# add json lib +add_library(json_cpp INTERFACE) +target_include_directories(json_cpp INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) + +# examples + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + +if (EMSCRIPTEN) + add_subdirectory(whisper.wasm) + add_subdirectory(stream.wasm) + add_subdirectory(command.wasm) + add_subdirectory(bench.wasm) + add_subdirectory(wchess) +elseif(CMAKE_JS_VERSION) + add_subdirectory(addon.node) +else() + add_subdirectory(cli) + add_subdirectory(bench) + add_subdirectory(server) + add_subdirectory(quantize) + add_subdirectory(vad-speech-segments) + add_subdirectory(parakeet-cli) + add_subdirectory(parakeet-quantize) + if (WHISPER_SDL2) + add_subdirectory(stream) + add_subdirectory(command) + add_subdirectory(talk-llama) + add_subdirectory(lsp) + if (GGML_SYCL) + add_subdirectory(sycl) + endif() + endif (WHISPER_SDL2) + + add_subdirectory(deprecation-warning) +endif() + +if (WHISPER_SDL2) + add_subdirectory(wchess) +endif (WHISPER_SDL2) diff --git a/examples/addon.node/.gitignore b/examples/addon.node/.gitignore new file mode 100644 index 0000000..b456cad --- /dev/null +++ b/examples/addon.node/.gitignore @@ -0,0 +1,3 @@ +.idea +node_modules +build diff --git a/examples/addon.node/CMakeLists.txt b/examples/addon.node/CMakeLists.txt new file mode 100644 index 0000000..75c68d1 --- /dev/null +++ b/examples/addon.node/CMakeLists.txt @@ -0,0 +1,34 @@ +set(TARGET addon.node) + +# Base settings +#================================================================== +# env var supported by cmake-js +add_definitions(-DNAPI_VERSION=4) +include_directories(${CMAKE_JS_INC}) +#================================================================== + +add_library(${TARGET} SHARED ${CMAKE_JS_SRC} addon.cpp) +set_target_properties(${TARGET} PROPERTIES + PREFIX "" + OUTPUT_NAME "addon" + SUFFIX ".node" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/Release" +) + +# Include N-API wrappers +#================================================================== +execute_process(COMMAND node -p "require('node-addon-api').include" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VARIABLE NODE_ADDON_API_DIR + ) +string(REPLACE "\n" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR}) +string(REPLACE "\"" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR}) +target_include_directories(${TARGET} PRIVATE ${NODE_ADDON_API_DIR}) +#================================================================== + +target_link_libraries(${TARGET} ${CMAKE_JS_LIB} common whisper ${CMAKE_THREAD_LIBS_INIT}) + +if(MSVC AND CMAKE_JS_NODELIB_DEF AND CMAKE_JS_NODELIB_TARGET) + # Generate node.lib + execute_process(COMMAND ${CMAKE_AR} /def:${CMAKE_JS_NODELIB_DEF} /out:${CMAKE_JS_NODELIB_TARGET} ${CMAKE_STATIC_LINKER_FLAGS}) +endif() diff --git a/examples/addon.node/README.md b/examples/addon.node/README.md new file mode 100644 index 0000000..bb09ba1 --- /dev/null +++ b/examples/addon.node/README.md @@ -0,0 +1,115 @@ +# whisper.cpp Node.js addon + +This is an addon demo that can **perform whisper model reasoning in `node` and `electron` environments**, based on [cmake-js](https://github.com/cmake-js/cmake-js). +It can be used as a reference for using the whisper.cpp project in other node projects. + +This addon now supports **Voice Activity Detection (VAD)** for improved transcription performance. + +## Install + +```shell +npm install +``` + +## Compile + +Make sure it is in the project root directory and compiled with make-js. + +```shell +npx cmake-js compile -T addon.node -B Release +``` + +For Electron addon and cmake-js options, you can see [cmake-js](https://github.com/cmake-js/cmake-js) and make very few configuration changes. + +> Such as appointing special cmake path: +> ```shell +> npx cmake-js compile -c 'xxx/cmake' -T addon.node -B Release +> ``` + +## Run + +### Basic Usage + +```shell +cd examples/addon.node + +node index.js --language='language' --model='model-path' --fname_inp='file-path' +``` + +### VAD (Voice Activity Detection) Usage + +Run the VAD example with performance comparison: + +```shell +node vad-example.js +``` + +## Voice Activity Detection (VAD) Support + +VAD can significantly improve transcription performance by only processing speech segments, which is especially beneficial for audio files with long periods of silence. + +### VAD Model Setup + +Before using VAD, download a VAD model: + +```shell +# From the whisper.cpp root directory +./models/download-vad-model.sh silero-v6.2.0 +``` + +### VAD Parameters + +All VAD parameters are optional and have sensible defaults: + +- `vad`: Enable VAD (default: false) +- `vad_model`: Path to VAD model file (required when VAD enabled) +- `vad_threshold`: Speech detection threshold 0.0-1.0 (default: 0.5) +- `vad_min_speech_duration_ms`: Min speech duration in ms (default: 250) +- `vad_min_silence_duration_ms`: Min silence duration in ms (default: 100) +- `vad_max_speech_duration_s`: Max speech duration in seconds (default: FLT_MAX) +- `vad_speech_pad_ms`: Speech padding in ms (default: 30) +- `vad_samples_overlap`: Sample overlap 0.0-1.0 (default: 0.1) + +### JavaScript API Example + +```javascript +const path = require("path"); +const { whisper } = require(path.join(__dirname, "../../build/Release/addon.node")); +const { promisify } = require("util"); + +const whisperAsync = promisify(whisper); + +// With VAD enabled +const vadParams = { + language: "en", + model: path.join(__dirname, "../../models/ggml-base.en.bin"), + fname_inp: path.join(__dirname, "../../samples/jfk.wav"), + vad: true, + vad_model: path.join(__dirname, "../../models/ggml-silero-v6.2.0.bin"), + vad_threshold: 0.5, + progress_callback: (progress) => console.log(`Progress: ${progress}%`) +}; + +whisperAsync(vadParams).then(result => console.log(result)); +``` + +## Supported Parameters + +Both traditional whisper.cpp parameters and new VAD parameters are supported: + +- `language`: Language code (e.g., "en", "es", "fr") +- `model`: Path to whisper model file +- `fname_inp`: Path to input audio file +- `use_gpu`: Enable GPU acceleration (default: true) +- `flash_attn`: Enable flash attention (default: false) +- `no_prints`: Disable console output (default: false) +- `no_timestamps`: Disable timestamps (default: false) +- `detect_language`: Auto-detect language (default: false) +- `audio_ctx`: Audio context size (default: 0) +- `max_len`: Maximum segment length (default: 0) +- `max_context`: Maximum context size (default: -1) +- `prompt`: Initial prompt for decoder +- `comma_in_time`: Use comma in timestamps (default: true) +- `print_progress`: Print progress info (default: false) +- `progress_callback`: Progress callback function +- VAD parameters (see above section) diff --git a/examples/addon.node/__test__/whisper.spec.js b/examples/addon.node/__test__/whisper.spec.js new file mode 100644 index 0000000..b07430a --- /dev/null +++ b/examples/addon.node/__test__/whisper.spec.js @@ -0,0 +1,133 @@ +const { join } = require('path'); +const { whisper } = require('../../../build/Release/addon.node'); +const { promisify } = require('util'); + +const whisperAsync = promisify(whisper); + +const commonParams = { + language: 'en', + model: join(__dirname, '../../../models/ggml-base.en.bin'), + fname_inp: join(__dirname, '../../../samples/jfk.wav'), + use_gpu: true, + flash_attn: false, + no_prints: true, + no_timestamps: false, + detect_language: false, + audio_ctx: 0, + max_len: 0 +}; + +describe('Whisper.cpp Node.js addon with VAD support', () => { + test('Basic whisper transcription without VAD', async () => { + const params = { + ...commonParams, + vad: false + }; + + const result = await whisperAsync(params); + + expect(typeof result).toBe('object'); + expect(Array.isArray(result.transcription)).toBe(true); + expect(result.transcription.length).toBeGreaterThan(0); + + // Check that we got some transcription text + const text = result.transcription.map(segment => segment[2]).join(' '); + expect(text.length).toBeGreaterThan(0); + expect(text.toLowerCase()).toContain('ask not'); + }, 30000); + + test('VAD parameters validation', async () => { + // Test with invalid VAD model - should return empty transcription + const invalidParams = { + ...commonParams, + vad: true, + vad_model: 'non-existent-model.bin', + vad_threshold: 0.5 + }; + + // This should handle the error gracefully and return empty transcription + const result = await whisperAsync(invalidParams); + expect(typeof result).toBe('object'); + expect(Array.isArray(result.transcription)).toBe(true); + // When VAD model doesn't exist, it should return empty transcription + expect(result.transcription.length).toBe(0); + }, 10000); + + test('VAD parameter parsing', async () => { + // Test that VAD parameters are properly parsed (even if VAD model doesn't exist) + const vadParams = { + ...commonParams, + vad: false, // Disabled so no model required + vad_threshold: 0.7, + vad_min_speech_duration_ms: 300, + vad_min_silence_duration_ms: 150, + vad_max_speech_duration_s: 45.0, + vad_speech_pad_ms: 50, + vad_samples_overlap: 0.15 + }; + + const result = await whisperAsync(vadParams); + + expect(typeof result).toBe('object'); + expect(Array.isArray(result.transcription)).toBe(true); + }, 30000); + + test('Progress callback with VAD disabled', async () => { + let progressCalled = false; + let lastProgress = 0; + + const params = { + ...commonParams, + vad: false, + progress_callback: (progress) => { + progressCalled = true; + lastProgress = progress; + expect(progress).toBeGreaterThanOrEqual(0); + expect(progress).toBeLessThanOrEqual(100); + } + }; + + const result = await whisperAsync(params); + + expect(progressCalled).toBe(true); + expect(lastProgress).toBe(100); + expect(typeof result).toBe('object'); + }, 30000); + + test('Language detection without VAD', async () => { + const params = { + ...commonParams, + vad: false, + detect_language: true, + language: 'auto' + }; + + const result = await whisperAsync(params); + + expect(typeof result).toBe('object'); + expect(typeof result.language).toBe('string'); + expect(result.language.length).toBeGreaterThan(0); + }, 30000); + + test('Basic transcription with all VAD parameters set', async () => { + // Test with VAD disabled but all parameters set to ensure no crashes + const params = { + ...commonParams, + vad: false, // Disabled so it works without VAD model + vad_model: '', // Empty model path + vad_threshold: 0.6, + vad_min_speech_duration_ms: 200, + vad_min_silence_duration_ms: 80, + vad_max_speech_duration_s: 25.0, + vad_speech_pad_ms: 40, + vad_samples_overlap: 0.08 + }; + + const result = await whisperAsync(params); + + expect(typeof result).toBe('object'); + expect(Array.isArray(result.transcription)).toBe(true); + expect(result.transcription.length).toBeGreaterThan(0); + }, 30000); +}); + diff --git a/examples/addon.node/addon.cpp b/examples/addon.node/addon.cpp new file mode 100644 index 0000000..71f65b0 --- /dev/null +++ b/examples/addon.node/addon.cpp @@ -0,0 +1,557 @@ +#include "napi.h" +#include "common.h" +#include "common-whisper.h" + +#include "whisper.h" + +#include <string> +#include <thread> +#include <vector> +#include <cmath> +#include <cstdint> +#include <cfloat> + +struct whisper_params { + int32_t n_threads = std::min(4, (int32_t) std::thread::hardware_concurrency()); + int32_t n_processors = 1; + int32_t offset_t_ms = 0; + int32_t offset_n = 0; + int32_t duration_ms = 0; + int32_t max_context = -1; + int32_t max_len = 0; + int32_t best_of = 5; + int32_t beam_size = -1; + int32_t audio_ctx = 0; + + float word_thold = 0.01f; + float entropy_thold = 2.4f; + float logprob_thold = -1.0f; + + bool translate = false; + bool diarize = false; + bool output_txt = false; + bool output_vtt = false; + bool output_srt = false; + bool output_wts = false; + bool output_csv = false; + bool print_special = false; + bool print_colors = false; + bool print_progress = false; + bool no_timestamps = false; + bool no_prints = false; + bool detect_language= false; + bool use_gpu = true; + bool flash_attn = false; + bool comma_in_time = true; + + std::string language = "en"; + std::string prompt; + std::string model = "../../ggml-large.bin"; + + std::vector<std::string> fname_inp = {}; + std::vector<std::string> fname_out = {}; + + std::vector<float> pcmf32 = {}; // mono-channel F32 PCM + + // Voice Activity Detection (VAD) parameters + bool vad = false; + std::string vad_model = ""; + float vad_threshold = 0.5f; + int vad_min_speech_duration_ms = 250; + int vad_min_silence_duration_ms = 100; + float vad_max_speech_duration_s = FLT_MAX; + int vad_speech_pad_ms = 30; + float vad_samples_overlap = 0.1f; +}; + +struct whisper_print_user_data { + const whisper_params * params; + + const std::vector<std::vector<float>> * pcmf32s; +}; + +void whisper_print_segment_callback(struct whisper_context * ctx, struct whisper_state * state, int n_new, void * user_data) { + const auto & params = *((whisper_print_user_data *) user_data)->params; + const auto & pcmf32s = *((whisper_print_user_data *) user_data)->pcmf32s; + + const int n_segments = whisper_full_n_segments(ctx); + + std::string speaker = ""; + + int64_t t0; + int64_t t1; + + // print the last n_new segments + const int s0 = n_segments - n_new; + + if (s0 == 0) { + printf("\n"); + } + + for (int i = s0; i < n_segments; i++) { + if (!params.no_timestamps || params.diarize) { + t0 = whisper_full_get_segment_t0(ctx, i); + t1 = whisper_full_get_segment_t1(ctx, i); + } + + if (!params.no_timestamps && !params.no_prints) { + printf("[%s --> %s] ", to_timestamp(t0).c_str(), to_timestamp(t1).c_str()); + } + + if (params.diarize && pcmf32s.size() == 2) { + const int64_t n_samples = pcmf32s[0].size(); + + const int64_t is0 = timestamp_to_sample(t0, n_samples, WHISPER_SAMPLE_RATE); + const int64_t is1 = timestamp_to_sample(t1, n_samples, WHISPER_SAMPLE_RATE); + + double energy0 = 0.0f; + double energy1 = 0.0f; + + for (int64_t j = is0; j < is1; j++) { + energy0 += fabs(pcmf32s[0][j]); + energy1 += fabs(pcmf32s[1][j]); + } + + if (energy0 > 1.1*energy1) { + speaker = "(speaker 0)"; + } else if (energy1 > 1.1*energy0) { + speaker = "(speaker 1)"; + } else { + speaker = "(speaker ?)"; + } + + //printf("is0 = %lld, is1 = %lld, energy0 = %f, energy1 = %f, %s\n", is0, is1, energy0, energy1, speaker.c_str()); + } + + // colorful print bug + // + if (!params.no_prints) { + const char * text = whisper_full_get_segment_text(ctx, i); + printf("%s%s", speaker.c_str(), text); + } + + + // with timestamps or speakers: each segment on new line + if ((!params.no_timestamps || params.diarize) && !params.no_prints) { + printf("\n"); + } + + fflush(stdout); + } +} + +void cb_log_disable(enum ggml_log_level, const char *, void *) {} + +struct whisper_result { + std::vector<std::vector<std::string>> segments; + std::string language; +}; + +class ProgressWorker : public Napi::AsyncWorker { + public: + ProgressWorker(Napi::Function& callback, whisper_params params, Napi::Function progress_callback, Napi::Env env) + : Napi::AsyncWorker(callback), params(params), env(env) { + // Create thread-safe function + if (!progress_callback.IsEmpty()) { + tsfn = Napi::ThreadSafeFunction::New( + env, + progress_callback, + "Progress Callback", + 0, + 1 + ); + } + } + + ~ProgressWorker() { + if (tsfn) { + // Make sure to release the thread-safe function on destruction + tsfn.Release(); + } + } + + void Execute() override { + // Use custom run function with progress callback support + run_with_progress(params, result); + } + + void OnOK() override { + Napi::HandleScope scope(Env()); + + if (params.detect_language) { + Napi::Object resultObj = Napi::Object::New(Env()); + resultObj.Set("language", Napi::String::New(Env(), result.language)); + Callback().Call({Env().Null(), resultObj}); + } + + Napi::Object returnObj = Napi::Object::New(Env()); + if (!result.language.empty()) { + returnObj.Set("language", Napi::String::New(Env(), result.language)); + } + Napi::Array transcriptionArray = Napi::Array::New(Env(), result.segments.size()); + for (uint64_t i = 0; i < result.segments.size(); ++i) { + Napi::Object tmp = Napi::Array::New(Env(), 3); + for (uint64_t j = 0; j < 3; ++j) { + tmp[j] = Napi::String::New(Env(), result.segments[i][j]); + } + transcriptionArray[i] = tmp; + } + returnObj.Set("transcription", transcriptionArray); + Callback().Call({Env().Null(), returnObj}); + } + + // Progress callback function - using thread-safe function + void OnProgress(int progress) { + if (tsfn) { + // Use thread-safe function to call JavaScript callback + auto callback = [progress](Napi::Env env, Napi::Function jsCallback) { + jsCallback.Call({Napi::Number::New(env, progress)}); + }; + + tsfn.BlockingCall(callback); + } + } + + private: + whisper_params params; + whisper_result result; + Napi::Env env; + Napi::ThreadSafeFunction tsfn; + + // Custom run function with progress callback support + int run_with_progress(whisper_params ¶ms, whisper_result & result) { + if (params.no_prints) { + whisper_log_set(cb_log_disable, NULL); + } + + if (params.fname_inp.empty() && params.pcmf32.empty()) { + fprintf(stderr, "error: no input files or audio buffer specified\n"); + return 2; + } + + if (params.language != "auto" && whisper_lang_id(params.language.c_str()) == -1) { + fprintf(stderr, "error: unknown language '%s'\n", params.language.c_str()); + exit(0); + } + + // whisper init + struct whisper_context_params cparams = whisper_context_default_params(); + cparams.use_gpu = params.use_gpu; + cparams.flash_attn = params.flash_attn; + struct whisper_context * ctx = whisper_init_from_file_with_params(params.model.c_str(), cparams); + + if (ctx == nullptr) { + fprintf(stderr, "error: failed to initialize whisper context\n"); + return 3; + } + + // If params.pcmf32 provides, set params.fname_inp as "buffer" + if (!params.pcmf32.empty()) { + fprintf(stderr, "info: using audio buffer as input\n"); + params.fname_inp.clear(); + params.fname_inp.emplace_back("buffer"); + } + + for (int f = 0; f < (int) params.fname_inp.size(); ++f) { + const auto fname_inp = params.fname_inp[f]; + const auto fname_out = f < (int)params.fname_out.size() && !params.fname_out[f].empty() ? params.fname_out[f] : params.fname_inp[f]; + + std::vector<float> pcmf32; // mono-channel F32 PCM + std::vector<std::vector<float>> pcmf32s; // stereo-channel F32 PCM + + // If params.pcmf32 is empty, read input audio file + if (params.pcmf32.empty()) { + if (!::read_audio_data(fname_inp, pcmf32, pcmf32s, params.diarize)) { + fprintf(stderr, "error: failed to read audio file '%s'\n", fname_inp.c_str()); + continue; + } + } else { + pcmf32 = params.pcmf32; + } + + // Print system info + if (!params.no_prints) { + fprintf(stderr, "\n"); + fprintf(stderr, "system_info: n_threads = %d / %d | %s\n", + params.n_threads*params.n_processors, std::thread::hardware_concurrency(), whisper_print_system_info()); + } + + // Print processing info + if (!params.no_prints) { + fprintf(stderr, "\n"); + if (!whisper_is_multilingual(ctx)) { + if (params.language != "en" || params.translate) { + params.language = "en"; + params.translate = false; + fprintf(stderr, "%s: WARNING: model is not multilingual, ignoring language and translation options\n", __func__); + } + } + fprintf(stderr, "%s: processing '%s' (%d samples, %.1f sec), %d threads, %d processors, lang = %s, task = %s, timestamps = %d, audio_ctx = %d ...\n", + __func__, fname_inp.c_str(), int(pcmf32.size()), float(pcmf32.size())/WHISPER_SAMPLE_RATE, + params.n_threads, params.n_processors, + params.language.c_str(), + params.translate ? "translate" : "transcribe", + params.no_timestamps ? 0 : 1, + params.audio_ctx); + + fprintf(stderr, "\n"); + } + + // Run inference + { + whisper_full_params wparams = whisper_full_default_params(WHISPER_SAMPLING_GREEDY); + + wparams.strategy = params.beam_size > 1 ? WHISPER_SAMPLING_BEAM_SEARCH : WHISPER_SAMPLING_GREEDY; + + wparams.print_realtime = false; + wparams.print_progress = params.print_progress; + wparams.print_timestamps = !params.no_timestamps; + wparams.print_special = params.print_special; + wparams.translate = params.translate; + wparams.language = params.detect_language ? "auto" : params.language.c_str(); + wparams.detect_language = params.detect_language; + wparams.n_threads = params.n_threads; + wparams.n_max_text_ctx = params.max_context >= 0 ? params.max_context : wparams.n_max_text_ctx; + wparams.offset_ms = params.offset_t_ms; + wparams.duration_ms = params.duration_ms; + + wparams.token_timestamps = params.output_wts || params.max_len > 0; + wparams.thold_pt = params.word_thold; + wparams.entropy_thold = params.entropy_thold; + wparams.logprob_thold = params.logprob_thold; + wparams.max_len = params.output_wts && params.max_len == 0 ? 60 : params.max_len; + wparams.audio_ctx = params.audio_ctx; + + wparams.greedy.best_of = params.best_of; + wparams.beam_search.beam_size = params.beam_size; + + wparams.initial_prompt = params.prompt.c_str(); + + wparams.no_timestamps = params.no_timestamps; + + whisper_print_user_data user_data = { ¶ms, &pcmf32s }; + + // This callback is called for each new segment + if (!wparams.print_realtime) { + wparams.new_segment_callback = whisper_print_segment_callback; + wparams.new_segment_callback_user_data = &user_data; + } + + // Set progress callback + wparams.progress_callback = [](struct whisper_context * /*ctx*/, struct whisper_state * /*state*/, int progress, void * user_data) { + ProgressWorker* worker = static_cast<ProgressWorker*>(user_data); + worker->OnProgress(progress); + }; + wparams.progress_callback_user_data = this; + + // Set VAD parameters + wparams.vad = params.vad; + wparams.vad_model_path = params.vad_model.c_str(); + + wparams.vad_params.threshold = params.vad_threshold; + wparams.vad_params.min_speech_duration_ms = params.vad_min_speech_duration_ms; + wparams.vad_params.min_silence_duration_ms = params.vad_min_silence_duration_ms; + wparams.vad_params.max_speech_duration_s = params.vad_max_speech_duration_s; + wparams.vad_params.speech_pad_ms = params.vad_speech_pad_ms; + wparams.vad_params.samples_overlap = params.vad_samples_overlap; + + if (whisper_full_parallel(ctx, wparams, pcmf32.data(), pcmf32.size(), params.n_processors) != 0) { + fprintf(stderr, "failed to process audio\n"); + return 10; + } + } + } + + if (params.detect_language || params.language == "auto") { + result.language = whisper_lang_str(whisper_full_lang_id(ctx)); + } + const int n_segments = whisper_full_n_segments(ctx); + result.segments.resize(n_segments); + + for (int i = 0; i < n_segments; ++i) { + const char * text = whisper_full_get_segment_text(ctx, i); + const int64_t t0 = whisper_full_get_segment_t0(ctx, i); + const int64_t t1 = whisper_full_get_segment_t1(ctx, i); + + result.segments[i].emplace_back(to_timestamp(t0, params.comma_in_time)); + result.segments[i].emplace_back(to_timestamp(t1, params.comma_in_time)); + result.segments[i].emplace_back(text); + } + + whisper_print_timings(ctx); + whisper_free(ctx); + + return 0; + } +}; + +Napi::Value whisper(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + if (info.Length() <= 0 || !info[0].IsObject()) { + Napi::TypeError::New(env, "object expected").ThrowAsJavaScriptException(); + } + whisper_params params; + + Napi::Object whisper_params = info[0].As<Napi::Object>(); + std::string language = whisper_params.Get("language").As<Napi::String>(); + std::string model = whisper_params.Get("model").As<Napi::String>(); + std::string input = whisper_params.Get("fname_inp").As<Napi::String>(); + + bool use_gpu = true; + if (whisper_params.Has("use_gpu") && whisper_params.Get("use_gpu").IsBoolean()) { + use_gpu = whisper_params.Get("use_gpu").As<Napi::Boolean>(); + } + + bool flash_attn = false; + if (whisper_params.Has("flash_attn") && whisper_params.Get("flash_attn").IsBoolean()) { + flash_attn = whisper_params.Get("flash_attn").As<Napi::Boolean>(); + } + + bool no_prints = false; + if (whisper_params.Has("no_prints") && whisper_params.Get("no_prints").IsBoolean()) { + no_prints = whisper_params.Get("no_prints").As<Napi::Boolean>(); + } + + bool no_timestamps = false; + if (whisper_params.Has("no_timestamps") && whisper_params.Get("no_timestamps").IsBoolean()) { + no_timestamps = whisper_params.Get("no_timestamps").As<Napi::Boolean>(); + } + + bool detect_language = false; + if (whisper_params.Has("detect_language") && whisper_params.Get("detect_language").IsBoolean()) { + detect_language = whisper_params.Get("detect_language").As<Napi::Boolean>(); + } + + int32_t audio_ctx = 0; + if (whisper_params.Has("audio_ctx") && whisper_params.Get("audio_ctx").IsNumber()) { + audio_ctx = whisper_params.Get("audio_ctx").As<Napi::Number>(); + } + + bool comma_in_time = true; + if (whisper_params.Has("comma_in_time") && whisper_params.Get("comma_in_time").IsBoolean()) { + comma_in_time = whisper_params.Get("comma_in_time").As<Napi::Boolean>(); + } + + int32_t max_len = 0; + if (whisper_params.Has("max_len") && whisper_params.Get("max_len").IsNumber()) { + max_len = whisper_params.Get("max_len").As<Napi::Number>(); + } + + // Add support for max_context + int32_t max_context = -1; + if (whisper_params.Has("max_context") && whisper_params.Get("max_context").IsNumber()) { + max_context = whisper_params.Get("max_context").As<Napi::Number>(); + } + + // support prompt + std::string prompt = ""; + if (whisper_params.Has("prompt") && whisper_params.Get("prompt").IsString()) { + prompt = whisper_params.Get("prompt").As<Napi::String>(); + } + + // Add support for print_progress + bool print_progress = false; + if (whisper_params.Has("print_progress") && whisper_params.Get("print_progress").IsBoolean()) { + print_progress = whisper_params.Get("print_progress").As<Napi::Boolean>(); + } + // Add support for progress_callback + Napi::Function progress_callback; + if (whisper_params.Has("progress_callback") && whisper_params.Get("progress_callback").IsFunction()) { + progress_callback = whisper_params.Get("progress_callback").As<Napi::Function>(); + } + + // Add support for VAD parameters + bool vad = false; + if (whisper_params.Has("vad") && whisper_params.Get("vad").IsBoolean()) { + vad = whisper_params.Get("vad").As<Napi::Boolean>(); + } + + std::string vad_model = ""; + if (whisper_params.Has("vad_model") && whisper_params.Get("vad_model").IsString()) { + vad_model = whisper_params.Get("vad_model").As<Napi::String>(); + } + + float vad_threshold = 0.5f; + if (whisper_params.Has("vad_threshold") && whisper_params.Get("vad_threshold").IsNumber()) { + vad_threshold = whisper_params.Get("vad_threshold").As<Napi::Number>(); + } + + int vad_min_speech_duration_ms = 250; + if (whisper_params.Has("vad_min_speech_duration_ms") && whisper_params.Get("vad_min_speech_duration_ms").IsNumber()) { + vad_min_speech_duration_ms = whisper_params.Get("vad_min_speech_duration_ms").As<Napi::Number>(); + } + + int vad_min_silence_duration_ms = 100; + if (whisper_params.Has("vad_min_silence_duration_ms") && whisper_params.Get("vad_min_silence_duration_ms").IsNumber()) { + vad_min_silence_duration_ms = whisper_params.Get("vad_min_silence_duration_ms").As<Napi::Number>(); + } + + float vad_max_speech_duration_s = FLT_MAX; + if (whisper_params.Has("vad_max_speech_duration_s") && whisper_params.Get("vad_max_speech_duration_s").IsNumber()) { + vad_max_speech_duration_s = whisper_params.Get("vad_max_speech_duration_s").As<Napi::Number>(); + } + + int vad_speech_pad_ms = 30; + if (whisper_params.Has("vad_speech_pad_ms") && whisper_params.Get("vad_speech_pad_ms").IsNumber()) { + vad_speech_pad_ms = whisper_params.Get("vad_speech_pad_ms").As<Napi::Number>(); + } + + float vad_samples_overlap = 0.1f; + if (whisper_params.Has("vad_samples_overlap") && whisper_params.Get("vad_samples_overlap").IsNumber()) { + vad_samples_overlap = whisper_params.Get("vad_samples_overlap").As<Napi::Number>(); + } + + Napi::Value pcmf32Value = whisper_params.Get("pcmf32"); + std::vector<float> pcmf32_vec; + if (pcmf32Value.IsTypedArray()) { + Napi::Float32Array pcmf32 = pcmf32Value.As<Napi::Float32Array>(); + size_t length = pcmf32.ElementLength(); + pcmf32_vec.reserve(length); + for (size_t i = 0; i < length; i++) { + pcmf32_vec.push_back(pcmf32[i]); + } + } + + params.language = language; + params.model = model; + params.fname_inp.emplace_back(input); + params.use_gpu = use_gpu; + params.flash_attn = flash_attn; + params.no_prints = no_prints; + params.no_timestamps = no_timestamps; + params.audio_ctx = audio_ctx; + params.pcmf32 = pcmf32_vec; + params.comma_in_time = comma_in_time; + params.max_len = max_len; + params.max_context = max_context; + params.print_progress = print_progress; + params.prompt = prompt; + params.detect_language = detect_language; + + // Set VAD parameters + params.vad = vad; + params.vad_model = vad_model; + params.vad_threshold = vad_threshold; + params.vad_min_speech_duration_ms = vad_min_speech_duration_ms; + params.vad_min_silence_duration_ms = vad_min_silence_duration_ms; + params.vad_max_speech_duration_s = vad_max_speech_duration_s; + params.vad_speech_pad_ms = vad_speech_pad_ms; + params.vad_samples_overlap = vad_samples_overlap; + + Napi::Function callback = info[1].As<Napi::Function>(); + // Create a new Worker class with progress callback support + ProgressWorker* worker = new ProgressWorker(callback, params, progress_callback, env); + worker->Queue(); + return env.Undefined(); +} + + +Napi::Object Init(Napi::Env env, Napi::Object exports) { + exports.Set( + Napi::String::New(env, "whisper"), + Napi::Function::New(env, whisper) + ); + return exports; +} + +NODE_API_MODULE(whisper, Init); diff --git a/examples/addon.node/index.js b/examples/addon.node/index.js new file mode 100644 index 0000000..2e4ed61 --- /dev/null +++ b/examples/addon.node/index.js @@ -0,0 +1,59 @@ +const path = require('path'); +const os = require('os'); + +const isWindows = os.platform() === 'win32'; +const buildPath = isWindows ? "../../build/bin/Release/addon.node" : "../../build/Release/addon.node"; + +const { whisper } = require(path.join(__dirname, buildPath)); +const { promisify } = require("util"); + +const whisperAsync = promisify(whisper); + +const whisperParams = { + language: "en", + model: path.join(__dirname, "../../models/ggml-base.en.bin"), + fname_inp: path.join(__dirname, "../../samples/jfk.wav"), + use_gpu: true, + flash_attn: false, + no_prints: true, + comma_in_time: false, + translate: true, + no_timestamps: false, + detect_language: false, + audio_ctx: 0, + max_len: 0, + progress_callback: (progress) => { + console.log(`progress: ${progress}%`); + } +}; + +const arguments = process.argv.slice(2); +const params = Object.fromEntries( + arguments.reduce((pre, item) => { + if (item.startsWith("--")) { + const [key, value] = item.slice(2).split("="); + if (key === "audio_ctx") { + whisperParams[key] = parseInt(value); + } else if (key === "detect_language") { + whisperParams[key] = value === "true"; + } else { + whisperParams[key] = value; + } + return pre; + } + return pre; + }, []) +); + +for (const key in params) { + if (whisperParams.hasOwnProperty(key)) { + whisperParams[key] = params[key]; + } +} + +console.log("whisperParams =", whisperParams); + +whisperAsync(whisperParams).then((result) => { + console.log(); + console.log(result); +}); diff --git a/examples/addon.node/package-lock.json b/examples/addon.node/package-lock.json new file mode 100644 index 0000000..1d1a8cd --- /dev/null +++ b/examples/addon.node/package-lock.json @@ -0,0 +1,4359 @@ +{ + "name": "addon.node", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "addon.node", + "version": "0.0.0", + "license": "MIT", + "devDependencies": { + "cmake-js": "^7.1.1", + "jest": "^29.4.0", + "node-addon-api": "^5.0.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", + "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.10", + "@babel/types": "^7.26.10", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz", + "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.0.tgz", + "integrity": "sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.26.8", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz", + "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", + "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", + "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", + "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", + "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", + "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.27.0", + "@babel/parser": "^7.27.0", + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", + "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/node": { + "version": "22.13.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.13.tgz", + "integrity": "sha512-ClsL5nMwKaBRwPcCvH8E7+nU4GxHVx1axNvMZTFHMEfNI7oahimt26P5zjVCRrjiIWj6YFXfE1v3dEp94wLcGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", + "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", + "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001707", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001707.tgz", + "integrity": "sha512-3qtRjw/HQSMlDWf+X79N206fepf4SOOU6SQLMaq/0KkZLmSjPxAkBOQQ+FxbHKfHmYLZFfdWsO3KA90ceHPSnw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cmake-js": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/cmake-js/-/cmake-js-7.3.0.tgz", + "integrity": "sha512-dXs2zq9WxrV87bpJ+WbnGKv8WUBXDw8blNiwNHoRe/it+ptscxhQHKB1SJXa1w+kocLMeP28Tk4/eTCezg4o+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "axios": "^1.6.5", + "debug": "^4", + "fs-extra": "^11.2.0", + "lodash.isplainobject": "^4.0.6", + "memory-stream": "^1.0.0", + "node-api-headers": "^1.1.0", + "npmlog": "^6.0.2", + "rc": "^1.2.7", + "semver": "^7.5.4", + "tar": "^6.2.0", + "url-join": "^4.0.1", + "which": "^2.0.2", + "yargs": "^17.7.2" + }, + "bin": { + "cmake-js": "bin/cmake-js" + }, + "engines": { + "node": ">= 14.15.0" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.123", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.123.tgz", + "integrity": "sha512-refir3NlutEZqlKaBLK0tzlVLe5P2wDKS7UQt/3SpibizgsRAPOsqQC3ffw1nlv3ze5gjRQZYHoPymgVZkplFA==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-extra": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/memory-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/memory-stream/-/memory-stream-1.0.0.tgz", + "integrity": "sha512-Wm13VcsPIMdG96dzILfij09PvuS3APtcKNh7M28FsCA/w6+1mjR7hhPmfFNoilX9xU7wTdhsH5lJAm6XNzdtww==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^3.4.0" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-api-headers": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/node-api-headers/-/node-api-headers-1.5.0.tgz", + "integrity": "sha512-Yi/FgnN8IU/Cd6KeLxyHkylBUvDTsSScT0Tna2zTrz8klmc8qF2ppj6Q1LHsmOueJWhigQwR4cO2p0XBGW5IaQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true, + "license": "MIT" + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true, + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dev": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "dev": true, + "license": "MIT" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/examples/addon.node/package.json b/examples/addon.node/package.json new file mode 100644 index 0000000..50046bf --- /dev/null +++ b/examples/addon.node/package.json @@ -0,0 +1,16 @@ +{ + "name": "addon.node", + "version": "0.0.0", + "description": "", + "main": "index.js", + "author": "Qanhe Chen", + "license": "MIT", + "scripts": { + "test": "jest" + }, + "devDependencies": { + "cmake-js": "^7.1.1", + "jest": "^29.4.0", + "node-addon-api": "^5.0.0" + } +} diff --git a/examples/addon.node/vad-example.js b/examples/addon.node/vad-example.js new file mode 100644 index 0000000..bdbb5ec --- /dev/null +++ b/examples/addon.node/vad-example.js @@ -0,0 +1,132 @@ +const path = require("path"); +const { whisper } = require(path.join( + __dirname, + "../../build/Release/addon.node" +)); +const { promisify } = require("util"); + +const whisperAsync = promisify(whisper); + +// Example with VAD enabled +const vadParams = { + language: "en", + model: path.join(__dirname, "../../models/ggml-base.en.bin"), + fname_inp: path.join(__dirname, "../../samples/jfk.wav"), + use_gpu: true, + flash_attn: false, + no_prints: false, + comma_in_time: true, + translate: false, + no_timestamps: false, + detect_language: false, + audio_ctx: 0, + max_len: 0, + // VAD parameters + vad: true, + vad_model: path.join(__dirname, "../../models/ggml-silero-v6.2.0.bin"), // You need to download this model + vad_threshold: 0.5, + vad_min_speech_duration_ms: 250, + vad_min_silence_duration_ms: 100, + vad_max_speech_duration_s: 30.0, + vad_speech_pad_ms: 30, + vad_samples_overlap: 0.1, + progress_callback: (progress) => { + console.log(`VAD Transcription progress: ${progress}%`); + } +}; + +// Example without VAD (traditional approach) +const traditionalParams = { + language: "en", + model: path.join(__dirname, "../../models/ggml-base.en.bin"), + fname_inp: path.join(__dirname, "../../samples/jfk.wav"), + use_gpu: true, + flash_attn: false, + no_prints: false, + comma_in_time: true, + translate: false, + no_timestamps: false, + detect_language: false, + audio_ctx: 0, + max_len: 0, + vad: false, // Explicitly disable VAD + progress_callback: (progress) => { + console.log(`Traditional transcription progress: ${progress}%`); + } +}; + +async function runVADExample() { + try { + console.log("=== Whisper.cpp Node.js VAD Example ===\n"); + + // Check if VAD model exists + const fs = require('fs'); + if (!fs.existsSync(vadParams.vad_model)) { + console.log("⚠️ VAD model not found. Please download the VAD model first:"); + console.log(" ./models/download-vad-model.sh silero-v6.2.0"); + console.log(" Or run: python models/convert-silero-vad-to-ggml.py"); + console.log("\n Falling back to traditional transcription without VAD...\n"); + + // Run without VAD + console.log("🎵 Running traditional transcription..."); + const traditionalResult = await whisperAsync(traditionalParams); + console.log("\n📝 Traditional transcription result:"); + console.log(traditionalResult); + return; + } + + console.log("🎵 Running transcription with VAD enabled..."); + console.log("VAD Parameters:"); + console.log(` - Threshold: ${vadParams.vad_threshold}`); + console.log(` - Min speech duration: ${vadParams.vad_min_speech_duration_ms}ms`); + console.log(` - Min silence duration: ${vadParams.vad_min_silence_duration_ms}ms`); + console.log(` - Max speech duration: ${vadParams.vad_max_speech_duration_s}s`); + console.log(` - Speech padding: ${vadParams.vad_speech_pad_ms}ms`); + console.log(` - Samples overlap: ${vadParams.vad_samples_overlap}\n`); + + const startTime = Date.now(); + const vadResult = await whisperAsync(vadParams); + const vadDuration = Date.now() - startTime; + + console.log("\n✅ VAD transcription completed!"); + console.log(`⏱️ Processing time: ${vadDuration}ms`); + console.log("\n📝 VAD transcription result:"); + console.log(vadResult); + + // Compare with traditional approach + console.log("\n🔄 Running traditional transcription for comparison..."); + const traditionalStartTime = Date.now(); + const traditionalResult = await whisperAsync(traditionalParams); + const traditionalDuration = Date.now() - traditionalStartTime; + + console.log("\n✅ Traditional transcription completed!"); + console.log(`⏱️ Processing time: ${traditionalDuration}ms`); + console.log("\n📝 Traditional transcription result:"); + console.log(traditionalResult); + + // Performance comparison + console.log("\n📊 Performance Comparison:"); + console.log(`VAD: ${vadDuration}ms`); + console.log(`Traditional: ${traditionalDuration}ms`); + const speedup = traditionalDuration / vadDuration; + if (speedup > 1) { + console.log(`🚀 VAD is ${speedup.toFixed(2)}x faster!`); + } else { + console.log(`ℹ️ Traditional approach was ${(1/speedup).toFixed(2)}x faster in this case.`); + } + + } catch (error) { + console.error("❌ Error during transcription:", error); + } +} + +// Run the example +if (require.main === module) { + runVADExample(); +} + +module.exports = { + runVADExample, + vadParams, + traditionalParams +}; \ No newline at end of file diff --git a/examples/bench.wasm/CMakeLists.txt b/examples/bench.wasm/CMakeLists.txt new file mode 100644 index 0000000..8e134aa --- /dev/null +++ b/examples/bench.wasm/CMakeLists.txt @@ -0,0 +1,49 @@ +# +# libbench +# + +set(TARGET libbench) + +add_executable(${TARGET} + emscripten.cpp + ) + +include(DefaultTargetOptions) + +target_link_libraries(${TARGET} PRIVATE + whisper + ) + +unset(EXTRA_FLAGS) + +if (WHISPER_WASM_SINGLE_FILE) + set(EXTRA_FLAGS "-s SINGLE_FILE=1") + message(STATUS "Embedding WASM inside bench.js") + + add_custom_command( + TARGET ${TARGET} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_BINARY_DIR}/bin/libbench.js + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/bench.wasm/bench.js + ) +endif() + +set_target_properties(${TARGET} PROPERTIES LINK_FLAGS " \ + --bind \ + -s USE_PTHREADS=1 \ + -s PTHREAD_POOL_SIZE_STRICT=0 \ + -s INITIAL_MEMORY=2000MB \ + -s TOTAL_MEMORY=2000MB \ + -s FORCE_FILESYSTEM=1 \ + -s EXPORTED_RUNTIME_METHODS=\"['print', 'printErr', 'ccall', 'cwrap', 'HEAPU8']\" \ + ${EXTRA_FLAGS} \ + ") + +# +# bench.wasm +# + +set(TARGET bench.wasm) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/index-tmpl.html ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}/index.html @ONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../helpers.js ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}/helpers.js @ONLY) diff --git a/examples/bench.wasm/README.md b/examples/bench.wasm/README.md new file mode 100644 index 0000000..be38c37 --- /dev/null +++ b/examples/bench.wasm/README.md @@ -0,0 +1,47 @@ +# bench.wasm + +Benchmark the performance of whisper.cpp in the browser using WebAssembly + +Link: https://ggml.ai/whisper.cpp/bench.wasm/ + +Terminal version: [examples/bench](/examples/bench) + +## Build instructions + +```bash +# build using Emscripten (v3.1.2) +git clone https://github.com/ggerganov/whisper.cpp +cd whisper.cpp +mkdir build-em && cd build-em +emcmake cmake .. +make -j +``` +The example can then be started by running a local HTTP server: +```console +python3 examples/server.py +``` +And then opening a browser to the following URL: +http://localhost:8000/bench.wasm + +To run the example in a different server, you need to copy the following files +to the server's HTTP path: +``` +# copy the produced page to your HTTP path +cp bin/bench.wasm/* /path/to/html/ +cp bin/libbench.js /path/to/html/ +cp bin/libbench.worker.js /path/to/html/ +``` + +> 📝 **Note:** By default this example is built with `WHISPER_WASM_SINGLE_FILE=ON` +> which means that that a separate .wasm file will not be generated. Instead, the +> WASM module is embedded in the main JS file as a base64 encoded string. To +> generate a separate .wasm file, you need to disable this option by passing +> `-DWHISPER_WASM_SINGLE_FILE=OFF`: +> ```console +> emcmake cmake .. -DWHISPER_WASM_SINGLE_FILE=OFF +> ``` +> This will generate a `libbench.wasm` file in the build/bin directory. + +> 📝 **Note:** As of Emscripten 3.1.58 (April 2024), separate worker.js files are no +> longer generated and the worker is embedded in the main JS file. So the worker +> file will not be generated for versions later than `3.1.58`. diff --git a/examples/bench.wasm/emscripten.cpp b/examples/bench.wasm/emscripten.cpp new file mode 100644 index 0000000..7e9f277 --- /dev/null +++ b/examples/bench.wasm/emscripten.cpp @@ -0,0 +1,87 @@ +#include "whisper.h" + +#include <emscripten.h> +#include <emscripten/bind.h> + +#include <cmath> +#include <string> +#include <thread> +#include <vector> + +constexpr int N_THREAD = 8; + +// TODO: get rid of this vector of contexts - bad idea in the first place +std::vector<struct whisper_context *> g_contexts(4, nullptr); + +std::thread g_worker; + +void bench_main(size_t index) { + const int n_threads = std::min(N_THREAD, (int) std::thread::hardware_concurrency()); + + // whisper context + auto & ctx = g_contexts[index]; + + fprintf(stderr, "%s: running benchmark with %d threads - please wait...\n", __func__, n_threads); + + const int n_mels = whisper_model_n_mels(ctx); + + if (int ret = whisper_set_mel(ctx, nullptr, 0, n_mels)) { + fprintf(stderr, "error: failed to set mel: %d\n", ret); + return; + } + + { + fprintf(stderr, "\n"); + fprintf(stderr, "system_info: n_threads = %d / %d | %s\n", n_threads, std::thread::hardware_concurrency(), whisper_print_system_info()); + } + + if (int ret = whisper_encode(ctx, 0, n_threads) != 0) { + fprintf(stderr, "error: failed to encode model: %d\n", ret); + return; + } + + whisper_print_timings(ctx); + + fprintf(stderr, "\n"); + fprintf(stderr, "If you wish, you can submit these results here:\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " https://github.com/ggml-org/whisper.cpp/issues/89\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Please include the following information:\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " - CPU model\n"); + fprintf(stderr, " - Operating system\n"); + fprintf(stderr, " - Browser\n"); + fprintf(stderr, "\n"); +} + +EMSCRIPTEN_BINDINGS(bench) { + emscripten::function("init", emscripten::optional_override([](const std::string & path_model) { + for (size_t i = 0; i < g_contexts.size(); ++i) { + if (g_contexts[i] == nullptr) { + g_contexts[i] = whisper_init_from_file_with_params(path_model.c_str(), whisper_context_default_params()); + if (g_contexts[i] != nullptr) { + if (g_worker.joinable()) { + g_worker.join(); + } + g_worker = std::thread([i]() { + bench_main(i); + }); + + return i + 1; + } else { + return (size_t) 0; + } + } + } + + return (size_t) 0; + })); + + emscripten::function("free", emscripten::optional_override([](size_t index) { + if (index < g_contexts.size()) { + whisper_free(g_contexts[index]); + g_contexts[index] = nullptr; + } + })); +} diff --git a/examples/bench.wasm/index-tmpl.html b/examples/bench.wasm/index-tmpl.html new file mode 100644 index 0000000..3a94174 --- /dev/null +++ b/examples/bench.wasm/index-tmpl.html @@ -0,0 +1,285 @@ +<!doctype html> +<html lang="en-us"> + <head> + <title>bench : Benchmark whisper.cpp performance in the browser + + + + + + +
+ bench : Benchmark whisper.cpp performance in the browser + +

+ + You can find more about this project on GitHub. + +

+ + More examples: + main | + bench | + stream | + command | + wchess | + +

+ +
+ + Select the model you would like to use and click the "Bench" button.
+ The results will be displayed in the textarea below. + +

+ +
+ Whisper model: + + + + +

+ Quantized models:

+ + + + + + +
+ +
+ +
+ + +
+ +
+ + Debug output: + + +
+ + Troubleshooting + +

+ + The page does some heavy computations, so make sure: + +
    +
  • To use a modern web browser (e.g. Chrome, Firefox)
  • +
  • To use a fast desktop or laptop computer (i.e. not a mobile phone)
  • +
  • Your browser supports WASM Fixed-width SIMD
  • +
+ +
+ + | + Build time: @GIT_DATE@ | + Commit hash: @GIT_SHA1@ | + Commit subject: @GIT_COMMIT_SUBJECT@ | + Source Code | + +
+
+ + + + + + diff --git a/examples/bench/CMakeLists.txt b/examples/bench/CMakeLists.txt new file mode 100644 index 0000000..f255f2d --- /dev/null +++ b/examples/bench/CMakeLists.txt @@ -0,0 +1,8 @@ +set(TARGET whisper-bench) +add_executable(${TARGET} bench.cpp) + +include(DefaultTargetOptions) + +target_link_libraries(${TARGET} PRIVATE whisper ${CMAKE_THREAD_LIBS_INIT}) + +install(TARGETS ${TARGET} RUNTIME) diff --git a/examples/bench/README.md b/examples/bench/README.md new file mode 100644 index 0000000..9002e2d --- /dev/null +++ b/examples/bench/README.md @@ -0,0 +1,51 @@ +# whisper.cpp/examples/bench + +A very basic tool for benchmarking the inference performance on your device. The tool simply runs the Encoder part of +the transformer on some random audio data and records the execution time. This way we can have an objective comparison +of the performance of the model for various setups. + +Benchmark results are tracked in the following Github issue: https://github.com/ggml-org/whisper.cpp/issues/89 + +```bash +# run the bench too on the small.en model using 4 threads +$ ./build/bin/whisper-bench -m ./models/ggml-small.en.bin -t 4 + +whisper_model_load: loading model from './models/ggml-small.en.bin' +whisper_model_load: n_vocab = 51864 +whisper_model_load: n_audio_ctx = 1500 +whisper_model_load: n_audio_state = 768 +whisper_model_load: n_audio_head = 12 +whisper_model_load: n_audio_layer = 12 +whisper_model_load: n_text_ctx = 448 +whisper_model_load: n_text_state = 768 +whisper_model_load: n_text_head = 12 +whisper_model_load: n_text_layer = 12 +whisper_model_load: n_mels = 80 +whisper_model_load: f16 = 1 +whisper_model_load: type = 3 +whisper_model_load: mem_required = 1048.00 MB +whisper_model_load: adding 1607 extra tokens +whisper_model_load: ggml ctx size = 533.05 MB +whisper_model_load: memory size = 68.48 MB +whisper_model_load: model size = 464.44 MB + +whisper_print_timings: load time = 240.82 ms +whisper_print_timings: mel time = 0.00 ms +whisper_print_timings: sample time = 0.00 ms +whisper_print_timings: encode time = 1062.21 ms / 88.52 ms per layer +whisper_print_timings: decode time = 0.00 ms / 0.00 ms per layer +whisper_print_timings: total time = 1303.04 ms + +system_info: n_threads = 4 | AVX2 = 0 | AVX512 = 0 | NEON = 1 | FP16_VA = 1 | WASM_SIMD = 0 | BLAS = 1 | + +If you wish, you can submit these results here: + + https://github.com/ggml-org/whisper.cpp/issues/89 + +Please include the following information: + + - CPU model + - Operating system + - Compiler + +``` diff --git a/examples/bench/bench.cpp b/examples/bench/bench.cpp new file mode 100644 index 0000000..84915c5 --- /dev/null +++ b/examples/bench/bench.cpp @@ -0,0 +1,191 @@ +#include "whisper.h" + +#include +#include +#include +#include + +// command-line parameters +struct whisper_params { + int32_t n_threads = std::min(4, (int32_t) std::thread::hardware_concurrency()); + int32_t what = 0; // what to benchmark: 0 - whisper encoder, 1 - memcpy, 2 - ggml_mul_mat + + std::string model = "models/ggml-base.en.bin"; + + bool use_gpu = true; + bool flash_attn = true; +}; + +void whisper_print_usage(int argc, char ** argv, const whisper_params & params); + +static bool whisper_params_parse(int argc, char ** argv, whisper_params & params) { + for (int i = 1; i < argc; i++) { + std::string arg = argv[i]; + + if (arg == "-h" || arg == "--help") { + whisper_print_usage(argc, argv, params); + exit(0); + } + else if (arg == "-t" || arg == "--threads") { params.n_threads = std::stoi(argv[++i]); } + else if (arg == "-m" || arg == "--model") { params.model = argv[++i]; } + else if (arg == "-w" || arg == "--what") { params.what = atoi(argv[++i]); } + else if (arg == "-ng" || arg == "--no-gpu") { params.use_gpu = false; } + else if (arg == "-fa" || arg == "--flash-attn") { params.flash_attn = true; } + else if (arg == "-nfa" || arg == "--no-flash-attn") { params.flash_attn = false; } + else { + fprintf(stderr, "error: unknown argument: %s\n", arg.c_str()); + whisper_print_usage(argc, argv, params); + exit(0); + } + } + + return true; +} + +void whisper_print_usage(int /*argc*/, char ** argv, const whisper_params & params) { + fprintf(stderr, "\n"); + fprintf(stderr, "usage: %s [options]\n", argv[0]); + fprintf(stderr, "\n"); + fprintf(stderr, "options:\n"); + fprintf(stderr, " -h, --help [default] show this help message and exit\n"); + fprintf(stderr, " -t N, --threads N [%-7d] number of threads to use during computation\n", params.n_threads); + fprintf(stderr, " -m FNAME, --model FNAME [%-7s] model path\n", params.model.c_str()); + fprintf(stderr, " -w N, --what N [%-7d] what to benchmark:\n", params.what); + fprintf(stderr, " %-7s 0 - whisper\n", ""); + fprintf(stderr, " %-7s 1 - memcpy\n", ""); + fprintf(stderr, " %-7s 2 - ggml_mul_mat\n", ""); + fprintf(stderr, " -ng, --no-gpu [%-7s] disable GPU\n", params.use_gpu ? "false" : "true"); + fprintf(stderr, " -fa, --flash-attn [%-7s] enable flash attention\n", params.flash_attn ? "true" : "false"); + fprintf(stderr, " -nfa, --no-flash-attn [%-7s] disable flash attention\n", params.flash_attn ? "false" : "true"); + fprintf(stderr, "\n"); +} + +static int whisper_bench_full(const whisper_params & params) { + // whisper init + + struct whisper_context_params cparams = whisper_context_default_params(); + + cparams.use_gpu = params.use_gpu; + cparams.flash_attn = params.flash_attn; + + { + fprintf(stderr, "\n"); + fprintf(stderr, "system_info: n_threads = %d / %d | %s\n", params.n_threads, std::thread::hardware_concurrency(), whisper_print_system_info()); + } + + struct whisper_context * ctx = whisper_init_from_file_with_params(params.model.c_str(), cparams); + if (ctx == nullptr) { + fprintf(stderr, "error: failed to initialize whisper context\n"); + return 2; + } + + const int n_mels = whisper_model_n_mels(ctx); + + if (int ret = whisper_set_mel(ctx, nullptr, 0, n_mels)) { + fprintf(stderr, "error: failed to set mel: %d\n", ret); + return 3; + } + + whisper_token tokens[512]; + memset(tokens, 0, sizeof(tokens)); + + // TODO: need 2 loops because of the current graph capture logic in the CUDA backend + // https://github.com/ggml-org/llama.cpp/pull/19754 + for (int h = 0; h < 2; ++h) { + // heat encoder + if (int ret = whisper_encode(ctx, 0, params.n_threads) != 0) { + fprintf(stderr, "error: failed to encode: %d\n", ret); + return 4; + } + + // prompt heat + if (int ret = whisper_decode(ctx, tokens, 256, 0, params.n_threads) != 0) { + fprintf(stderr, "error: failed to decode: %d\n", ret); + return 4; + } + + // text-generation heat + for (int i = 0; i < 256; i++) { + if (int ret = whisper_decode(ctx, tokens, 1, i, params.n_threads) != 0) { + fprintf(stderr, "error: failed to decode: %d\n", ret); + return 4; + } + } + + // batched heat + if (int ret = whisper_decode(ctx, tokens, 5, 0, params.n_threads) != 0) { + fprintf(stderr, "error: failed to decode: %d\n", ret); + return 4; + } + } + + whisper_reset_timings(ctx); + + // actual run + if (int ret = whisper_encode(ctx, 0, params.n_threads) != 0) { + fprintf(stderr, "error: failed to encode: %d\n", ret); + return 4; + } + + // text-generation + for (int i = 0; i < 256; i++) { + if (int ret = whisper_decode(ctx, tokens, 1, i, params.n_threads) != 0) { + fprintf(stderr, "error: failed to decode: %d\n", ret); + return 4; + } + } + + // batched decoding + for (int i = 0; i < 64; i++) { + if (int ret = whisper_decode(ctx, tokens, 5, 0, params.n_threads) != 0) { + fprintf(stderr, "error: failed to decode: %d\n", ret); + return 4; + } + } + + // prompt processing + for (int i = 0; i < 16; i++) { + if (int ret = whisper_decode(ctx, tokens, 256, 0, params.n_threads) != 0) { + fprintf(stderr, "error: failed to decode: %d\n", ret); + return 4; + } + } + + whisper_print_timings(ctx); + whisper_free(ctx); + + fprintf(stderr, "\n"); + fprintf(stderr, "If you wish, you can submit these results here:\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " https://github.com/ggml-org/whisper.cpp/issues/89\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Please include the following information:\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " - CPU model\n"); + fprintf(stderr, " - Operating system\n"); + fprintf(stderr, " - Compiler\n"); + fprintf(stderr, "\n"); + + return 0; +} + +int main(int argc, char ** argv) { + ggml_backend_load_all(); + + whisper_params params; + + if (whisper_params_parse(argc, argv, params) == false) { + return 1; + } + + int ret = -1; + + switch (params.what) { + case 0: ret = whisper_bench_full(params); break; + case 1: ret = whisper_bench_memcpy(params.n_threads); break; + case 2: ret = whisper_bench_ggml_mul_mat(params.n_threads); break; + default: fprintf(stderr, "error: unknown benchmark: %d\n", params.what); break; + } + + return ret; +} diff --git a/examples/cli/CMakeLists.txt b/examples/cli/CMakeLists.txt new file mode 100644 index 0000000..3a73776 --- /dev/null +++ b/examples/cli/CMakeLists.txt @@ -0,0 +1,8 @@ +set(TARGET whisper-cli) +add_executable(${TARGET} cli.cpp) + +include(DefaultTargetOptions) + +target_link_libraries(${TARGET} PRIVATE common whisper ${FFMPEG_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) + +install(TARGETS ${TARGET} RUNTIME) diff --git a/examples/cli/README.md b/examples/cli/README.md new file mode 100644 index 0000000..65285c3 --- /dev/null +++ b/examples/cli/README.md @@ -0,0 +1,66 @@ +# whisper.cpp/examples/cli + +This is the main example demonstrating most of the functionality of the Whisper model. +It can be used as a reference for using the `whisper.cpp` library in other projects. + +``` +./build/bin/whisper-cli -h + +usage: ./build/bin/whisper-cli [options] file0 file1 ... +supported audio formats: flac, mp3, ogg, wav + +options: + -h, --help [default] show this help message and exit + -t N, --threads N [4 ] number of threads to use during computation + -p N, --processors N [1 ] number of processors to use during computation + -ot N, --offset-t N [0 ] time offset in milliseconds + -on N, --offset-n N [0 ] segment index offset + -d N, --duration N [0 ] duration of audio to process in milliseconds + -mc N, --max-context N [-1 ] maximum number of text context tokens to store + -ml N, --max-len N [0 ] maximum segment length in characters + -sow, --split-on-word [false ] split on word rather than on token + -bo N, --best-of N [5 ] number of best candidates to keep + -bs N, --beam-size N [5 ] beam size for beam search + -ac N, --audio-ctx N [0 ] audio context size (0 - all) + -wt N, --word-thold N [0.01 ] word timestamp probability threshold + -et N, --entropy-thold N [2.40 ] entropy threshold for decoder fail + -lpt N, --logprob-thold N [-1.00 ] log probability threshold for decoder fail + -nth N, --no-speech-thold N [0.60 ] no speech threshold + -tp, --temperature N [0.00 ] The sampling temperature, between 0 and 1 + -tpi, --temperature-inc N [0.20 ] The increment of temperature, between 0 and 1 + -debug, --debug-mode [false ] enable debug mode (eg. dump log_mel) + -tr, --translate [false ] translate from source language to english + -di, --diarize [false ] stereo audio diarization + -tdrz, --tinydiarize [false ] enable tinydiarize (requires a tdrz model) + -nf, --no-fallback [false ] do not use temperature fallback while decoding + -otxt, --output-txt [false ] output result in a text file + -ovtt, --output-vtt [false ] output result in a vtt file + -osrt, --output-srt [false ] output result in a srt file + -olrc, --output-lrc [false ] output result in a lrc file + -owts, --output-words [false ] output script for generating karaoke video + -fp, --font-path [/System/Library/Fonts/Supplemental/Courier New Bold.ttf] path to a monospace font for karaoke video + -ocsv, --output-csv [false ] output result in a CSV file + -oj, --output-json [false ] output result in a JSON file + -ojf, --output-json-full [false ] include more information in the JSON file + -of FNAME, --output-file FNAME [ ] output file path (without file extension) + -np, --no-prints [false ] do not print anything other than the results + -ps, --print-special [false ] print special tokens + -pc, --print-colors [false ] print colors + -pp, --print-progress [false ] print progress + -nt, --no-timestamps [false ] do not print timestamps + -l LANG, --language LANG [en ] spoken language ('auto' for auto-detect) + -dl, --detect-language [false ] exit after automatically detecting language + --prompt PROMPT [ ] initial prompt (max n_text_ctx/2 tokens) + -m FNAME, --model FNAME [models/ggml-base.en.bin] model path + -f FNAME, --file FNAME [ ] input audio file path + -oved D, --ov-e-device DNAME [CPU ] the OpenVINO device used for encode inference + -dtw MODEL --dtw MODEL [ ] compute token-level timestamps + -ls, --log-score [false ] log best decoder scores of tokens + -ng, --no-gpu [false ] disable GPU + -fa, --flash-attn [false ] flash attention + -sns, --suppress-nst [false ] suppress non-speech tokens + --suppress-regex REGEX [ ] regular expression matching tokens to suppress + --grammar GRAMMAR [ ] GBNF grammar to guide decoding + --grammar-rule RULE [ ] top-level GBNF grammar rule name + --grammar-penalty N [100.0 ] scales down logits of nongrammar tokens +``` diff --git a/examples/cli/cli.cpp b/examples/cli/cli.cpp new file mode 100644 index 0000000..c64976a --- /dev/null +++ b/examples/cli/cli.cpp @@ -0,0 +1,1356 @@ +#include "common.h" +#include "common-whisper.h" + +#include "whisper.h" +#include "grammar-parser.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#endif + +// helper function to replace substrings +static void replace_all(std::string & s, const std::string & search, const std::string & replace) { + for (size_t pos = 0; ; pos += replace.length()) { + pos = s.find(search, pos); + if (pos == std::string::npos) break; + s.erase(pos, search.length()); + s.insert(pos, replace); + } +} + +// command-line parameters +struct whisper_params { + int32_t n_threads = std::min(4, (int32_t) std::thread::hardware_concurrency()); + int32_t n_processors = 1; + int32_t offset_t_ms = 0; + int32_t offset_n = 0; + int32_t duration_ms = 0; + int32_t progress_step = 5; + int32_t max_context = -1; + int32_t max_len = 0; + int32_t best_of = whisper_full_default_params(WHISPER_SAMPLING_GREEDY).greedy.best_of; + int32_t beam_size = whisper_full_default_params(WHISPER_SAMPLING_BEAM_SEARCH).beam_search.beam_size; + int32_t audio_ctx = 0; + + float word_thold = 0.01f; + float entropy_thold = 2.40f; + float logprob_thold = -1.00f; + float no_speech_thold = 0.6f; + float grammar_penalty = 100.0f; + float temperature = 0.0f; + float temperature_inc = 0.2f; + + bool debug_mode = false; + bool translate = false; + bool detect_language = false; + bool diarize = false; + bool tinydiarize = false; + bool split_on_word = false; + bool no_fallback = false; + bool output_txt = false; + bool output_vtt = false; + bool output_srt = false; + bool output_wts = false; + bool output_csv = false; + bool output_jsn = false; + bool output_jsn_full = false; + bool output_lrc = false; + bool no_prints = false; + bool print_special = false; + bool print_colors = false; + bool print_confidence= false; + bool print_progress = false; + bool no_timestamps = false; + bool log_score = false; + bool use_gpu = true; + bool flash_attn = true; + int32_t gpu_device = 0; + bool suppress_nst = false; + bool carry_initial_prompt = false; + + std::string language = "en"; + std::string prompt; + std::string font_path = "/System/Library/Fonts/Supplemental/Courier New Bold.ttf"; + std::string model = "models/ggml-base.en.bin"; + std::string grammar; + std::string grammar_rule; + + // [TDRZ] speaker turn string + std::string tdrz_speaker_turn = " [SPEAKER_TURN]"; // TODO: set from command line + + // A regular expression that matches tokens to suppress + std::string suppress_regex; + + std::string openvino_encode_device = "CPU"; + + std::string dtw = ""; + + std::vector fname_inp = {}; + std::vector fname_out = {}; + + grammar_parser::parse_state grammar_parsed; + + // Voice Activity Detection (VAD) parameters + bool vad = false; + std::string vad_model = ""; + float vad_threshold = 0.5f; + int vad_min_speech_duration_ms = 250; + int vad_min_silence_duration_ms = 100; + float vad_max_speech_duration_s = FLT_MAX; + int vad_speech_pad_ms = 30; + float vad_samples_overlap = 0.1f; +}; + +static void whisper_print_usage(int argc, char ** argv, const whisper_params & params); + +static char * whisper_param_turn_lowercase(char * in){ + int string_len = strlen(in); + for (int i = 0; i < string_len; i++){ + *(in+i) = tolower((unsigned char)*(in+i)); + } + return in; +} + +static char * requires_value_error(const std::string & arg) { + fprintf(stderr, "error: argument %s requires value\n", arg.c_str()); + exit(0); +} + +static bool whisper_params_parse(int argc, char ** argv, whisper_params & params) { + if (const char * env_device = std::getenv("WHISPER_ARG_DEVICE")) { + params.gpu_device = std::stoi(env_device); + } + + for (int i = 1; i < argc; i++) { + std::string arg = argv[i]; + + if (arg == "-"){ + params.fname_inp.push_back(arg); + continue; + } + + if (arg[0] != '-') { + params.fname_inp.push_back(arg); + continue; + } + + if (arg == "-h" || arg == "--help") { + whisper_print_usage(argc, argv, params); + exit(0); + } + if (arg == "--version") { + fprintf(stdout, "whisper.cpp version: %s\n", whisper_version()); + exit(0); + } + #define ARGV_NEXT (((i + 1) < argc) ? argv[++i] : requires_value_error(arg)) + else if (arg == "-t" || arg == "--threads") { params.n_threads = std::stoi(ARGV_NEXT); } + else if (arg == "-p" || arg == "--processors") { params.n_processors = std::stoi(ARGV_NEXT); } + else if (arg == "-ot" || arg == "--offset-t") { params.offset_t_ms = std::stoi(ARGV_NEXT); } + else if (arg == "-on" || arg == "--offset-n") { params.offset_n = std::stoi(ARGV_NEXT); } + else if (arg == "-d" || arg == "--duration") { params.duration_ms = std::stoi(ARGV_NEXT); } + else if (arg == "-mc" || arg == "--max-context") { params.max_context = std::stoi(ARGV_NEXT); } + else if (arg == "-ml" || arg == "--max-len") { params.max_len = std::stoi(ARGV_NEXT); } + else if (arg == "-bo" || arg == "--best-of") { params.best_of = std::stoi(ARGV_NEXT); } + else if (arg == "-bs" || arg == "--beam-size") { params.beam_size = std::stoi(ARGV_NEXT); } + else if (arg == "-ac" || arg == "--audio-ctx") { params.audio_ctx = std::stoi(ARGV_NEXT); } + else if (arg == "-wt" || arg == "--word-thold") { params.word_thold = std::stof(ARGV_NEXT); } + else if (arg == "-et" || arg == "--entropy-thold") { params.entropy_thold = std::stof(ARGV_NEXT); } + else if (arg == "-lpt" || arg == "--logprob-thold") { params.logprob_thold = std::stof(ARGV_NEXT); } + else if (arg == "-nth" || arg == "--no-speech-thold") { params.no_speech_thold = std::stof(ARGV_NEXT); } + else if (arg == "-tp" || arg == "--temperature") { params.temperature = std::stof(ARGV_NEXT); } + else if (arg == "-tpi" || arg == "--temperature-inc") { params.temperature_inc = std::stof(ARGV_NEXT); } + else if (arg == "-debug"|| arg == "--debug-mode") { params.debug_mode = true; } + else if (arg == "-tr" || arg == "--translate") { params.translate = true; } + else if (arg == "-di" || arg == "--diarize") { params.diarize = true; } + else if (arg == "-tdrz" || arg == "--tinydiarize") { params.tinydiarize = true; } + else if (arg == "-sow" || arg == "--split-on-word") { params.split_on_word = true; } + else if (arg == "-nf" || arg == "--no-fallback") { params.no_fallback = true; } + else if (arg == "-otxt" || arg == "--output-txt") { params.output_txt = true; } + else if (arg == "-ovtt" || arg == "--output-vtt") { params.output_vtt = true; } + else if (arg == "-osrt" || arg == "--output-srt") { params.output_srt = true; } + else if (arg == "-owts" || arg == "--output-words") { params.output_wts = true; } + else if (arg == "-olrc" || arg == "--output-lrc") { params.output_lrc = true; } + else if (arg == "-fp" || arg == "--font-path") { params.font_path = ARGV_NEXT; } + else if (arg == "-ocsv" || arg == "--output-csv") { params.output_csv = true; } + else if (arg == "-oj" || arg == "--output-json") { params.output_jsn = true; } + else if (arg == "-ojf" || arg == "--output-json-full") { params.output_jsn_full = params.output_jsn = true; } + else if (arg == "-of" || arg == "--output-file") { params.fname_out.emplace_back(ARGV_NEXT); } + else if (arg == "-np" || arg == "--no-prints") { params.no_prints = true; } + else if (arg == "-ps" || arg == "--print-special") { params.print_special = true; } + else if (arg == "-pc" || arg == "--print-colors") { params.print_colors = true; } + else if ( arg == "--print-confidence") { params.print_confidence= true; } + else if (arg == "-pp" || arg == "--print-progress") { params.print_progress = true; } + else if (arg == "-nt" || arg == "--no-timestamps") { params.no_timestamps = true; } + else if (arg == "-l" || arg == "--language") { params.language = whisper_param_turn_lowercase(ARGV_NEXT); } + else if (arg == "-dl" || arg == "--detect-language") { params.detect_language = true; } + else if ( arg == "--prompt") { params.prompt = ARGV_NEXT; } + else if ( arg == "--carry-initial-prompt") { params.carry_initial_prompt = true; } + else if (arg == "-m" || arg == "--model") { params.model = ARGV_NEXT; } + else if (arg == "-f" || arg == "--file") { params.fname_inp.emplace_back(ARGV_NEXT); } + else if (arg == "-oved" || arg == "--ov-e-device") { params.openvino_encode_device = ARGV_NEXT; } + else if (arg == "-dtw" || arg == "--dtw") { params.dtw = ARGV_NEXT; } + else if (arg == "-ls" || arg == "--log-score") { params.log_score = true; } + else if (arg == "-ng" || arg == "--no-gpu") { params.use_gpu = false; } + else if (arg == "-dev" || arg == "--device") { params.gpu_device = std::stoi(ARGV_NEXT); } + else if (arg == "-fa" || arg == "--flash-attn") { params.flash_attn = true; } + else if (arg == "-nfa" || arg == "--no-flash-attn") { params.flash_attn = false; } + else if (arg == "-sns" || arg == "--suppress-nst") { params.suppress_nst = true; } + else if ( arg == "--suppress-regex") { params.suppress_regex = ARGV_NEXT; } + else if ( arg == "--grammar") { params.grammar = ARGV_NEXT; } + else if ( arg == "--grammar-rule") { params.grammar_rule = ARGV_NEXT; } + else if ( arg == "--grammar-penalty") { params.grammar_penalty = std::stof(ARGV_NEXT); } + // Voice Activity Detection (VAD) + else if ( arg == "--vad") { params.vad = true; } + else if (arg == "-vm" || arg == "--vad-model") { params.vad_model = ARGV_NEXT; } + else if (arg == "-vt" || arg == "--vad-threshold") { params.vad_threshold = std::stof(ARGV_NEXT); } + else if (arg == "-vspd" || arg == "--vad-min-speech-duration-ms") { params.vad_min_speech_duration_ms = std::stoi(ARGV_NEXT); } + else if (arg == "-vsd" || arg == "--vad-min-silence-duration-ms") { params.vad_min_silence_duration_ms = std::stoi(ARGV_NEXT); } + else if (arg == "-vmsd" || arg == "--vad-max-speech-duration-s") { params.vad_max_speech_duration_s = std::stof(ARGV_NEXT); } + else if (arg == "-vp" || arg == "--vad-speech-pad-ms") { params.vad_speech_pad_ms = std::stoi(ARGV_NEXT); } + else if (arg == "-vo" || arg == "--vad-samples-overlap") { params.vad_samples_overlap = std::stof(ARGV_NEXT); } + else { + fprintf(stderr, "error: unknown argument: %s\n", arg.c_str()); + whisper_print_usage(argc, argv, params); + exit(0); + } + } + + return true; +} + +static void whisper_print_usage(int /*argc*/, char ** argv, const whisper_params & params) { + fprintf(stderr, "\n"); + fprintf(stderr, "usage: %s [options] file0 file1 ...\n", argv[0]); + fprintf(stderr, "supported audio formats: flac, mp3, ogg, wav\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "options:\n"); + fprintf(stderr, " -h, --help [default] show this help message and exit\n"); + fprintf(stderr, " --version show version information and exit\n"); + fprintf(stderr, " -t N, --threads N [%-7d] number of threads to use during computation\n", params.n_threads); + fprintf(stderr, " -p N, --processors N [%-7d] number of processors to use during computation\n", params.n_processors); + fprintf(stderr, " -ot N, --offset-t N [%-7d] time offset in milliseconds\n", params.offset_t_ms); + fprintf(stderr, " -on N, --offset-n N [%-7d] segment index offset\n", params.offset_n); + fprintf(stderr, " -d N, --duration N [%-7d] duration of audio to process in milliseconds\n", params.duration_ms); + fprintf(stderr, " -mc N, --max-context N [%-7d] maximum number of text context tokens to store\n", params.max_context); + fprintf(stderr, " -ml N, --max-len N [%-7d] maximum segment length in characters\n", params.max_len); + fprintf(stderr, " -sow, --split-on-word [%-7s] split on word rather than on token\n", params.split_on_word ? "true" : "false"); + fprintf(stderr, " -bo N, --best-of N [%-7d] number of best candidates to keep\n", params.best_of); + fprintf(stderr, " -bs N, --beam-size N [%-7d] beam size for beam search\n", params.beam_size); + fprintf(stderr, " -ac N, --audio-ctx N [%-7d] audio context size (0 - all)\n", params.audio_ctx); + fprintf(stderr, " -wt N, --word-thold N [%-7.2f] word timestamp probability threshold\n", params.word_thold); + fprintf(stderr, " -et N, --entropy-thold N [%-7.2f] entropy threshold for decoder fail\n", params.entropy_thold); + fprintf(stderr, " -lpt N, --logprob-thold N [%-7.2f] log probability threshold for decoder fail\n", params.logprob_thold); + fprintf(stderr, " -nth N, --no-speech-thold N [%-7.2f] no speech threshold\n", params.no_speech_thold); + fprintf(stderr, " -tp, --temperature N [%-7.2f] The sampling temperature, between 0 and 1\n", params.temperature); + fprintf(stderr, " -tpi, --temperature-inc N [%-7.2f] The increment of temperature, between 0 and 1\n",params.temperature_inc); + fprintf(stderr, " -debug, --debug-mode [%-7s] enable debug mode (eg. dump log_mel)\n", params.debug_mode ? "true" : "false"); + fprintf(stderr, " -tr, --translate [%-7s] translate from source language to english\n", params.translate ? "true" : "false"); + fprintf(stderr, " -di, --diarize [%-7s] stereo audio diarization\n", params.diarize ? "true" : "false"); + fprintf(stderr, " -tdrz, --tinydiarize [%-7s] enable tinydiarize (requires a tdrz model)\n", params.tinydiarize ? "true" : "false"); + fprintf(stderr, " -nf, --no-fallback [%-7s] do not use temperature fallback while decoding\n", params.no_fallback ? "true" : "false"); + fprintf(stderr, " -otxt, --output-txt [%-7s] output result in a text file\n", params.output_txt ? "true" : "false"); + fprintf(stderr, " -ovtt, --output-vtt [%-7s] output result in a vtt file\n", params.output_vtt ? "true" : "false"); + fprintf(stderr, " -osrt, --output-srt [%-7s] output result in a srt file\n", params.output_srt ? "true" : "false"); + fprintf(stderr, " -olrc, --output-lrc [%-7s] output result in a lrc file\n", params.output_lrc ? "true" : "false"); + fprintf(stderr, " -owts, --output-words [%-7s] output script for generating karaoke video\n", params.output_wts ? "true" : "false"); + fprintf(stderr, " -fp, --font-path [%-7s] path to a monospace font for karaoke video\n", params.font_path.c_str()); + fprintf(stderr, " -ocsv, --output-csv [%-7s] output result in a CSV file\n", params.output_csv ? "true" : "false"); + fprintf(stderr, " -oj, --output-json [%-7s] output result in a JSON file\n", params.output_jsn ? "true" : "false"); + fprintf(stderr, " -ojf, --output-json-full [%-7s] include more information in the JSON file\n", params.output_jsn_full ? "true" : "false"); + fprintf(stderr, " -of FNAME, --output-file FNAME [%-7s] output file path (without file extension)\n", ""); + fprintf(stderr, " -np, --no-prints [%-7s] do not print anything other than the results\n", params.no_prints ? "true" : "false"); + fprintf(stderr, " -ps, --print-special [%-7s] print special tokens\n", params.print_special ? "true" : "false"); + fprintf(stderr, " -pc, --print-colors [%-7s] print colors\n", params.print_colors ? "true" : "false"); + fprintf(stderr, " --print-confidence [%-7s] print confidence\n", params.print_confidence ? "true" : "false"); + fprintf(stderr, " -pp, --print-progress [%-7s] print progress\n", params.print_progress ? "true" : "false"); + fprintf(stderr, " -nt, --no-timestamps [%-7s] do not print timestamps\n", params.no_timestamps ? "true" : "false"); + fprintf(stderr, " -l LANG, --language LANG [%-7s] spoken language ('auto' for auto-detect)\n", params.language.c_str()); + fprintf(stderr, " -dl, --detect-language [%-7s] exit after automatically detecting language\n", params.detect_language ? "true" : "false"); + fprintf(stderr, " --prompt PROMPT [%-7s] initial prompt (max n_text_ctx/2 tokens)\n", params.prompt.c_str()); + fprintf(stderr, " --carry-initial-prompt [%-7s] always prepend initial prompt\n", params.carry_initial_prompt ? "true" : "false"); + fprintf(stderr, " -m FNAME, --model FNAME [%-7s] model path\n", params.model.c_str()); + fprintf(stderr, " -f FNAME, --file FNAME [%-7s] input audio file path\n", ""); + fprintf(stderr, " -oved D, --ov-e-device DNAME [%-7s] the OpenVINO device used for encode inference\n", params.openvino_encode_device.c_str()); + fprintf(stderr, " -dtw MODEL --dtw MODEL [%-7s] compute token-level timestamps\n", params.dtw.c_str()); + fprintf(stderr, " -ls, --log-score [%-7s] log best decoder scores of tokens\n", params.log_score?"true":"false"); + fprintf(stderr, " -ng, --no-gpu [%-7s] disable GPU\n", params.use_gpu ? "false" : "true"); + fprintf(stderr, " -dev N, --device N [%-7d] GPU device ID (default: 0)\n", params.gpu_device); + fprintf(stderr, " -fa, --flash-attn [%-7s] enable flash attention\n", params.flash_attn ? "true" : "false"); + fprintf(stderr, " -nfa, --no-flash-attn [%-7s] disable flash attention\n", params.flash_attn ? "false" : "true"); + fprintf(stderr, " -sns, --suppress-nst [%-7s] suppress non-speech tokens\n", params.suppress_nst ? "true" : "false"); + fprintf(stderr, " --suppress-regex REGEX [%-7s] regular expression matching tokens to suppress\n", params.suppress_regex.c_str()); + fprintf(stderr, " --grammar GRAMMAR [%-7s] GBNF grammar to guide decoding\n", params.grammar.c_str()); + fprintf(stderr, " --grammar-rule RULE [%-7s] top-level GBNF grammar rule name\n", params.grammar_rule.c_str()); + fprintf(stderr, " --grammar-penalty N [%-7.1f] scales down logits of nongrammar tokens\n", params.grammar_penalty); + // Voice Activity Detection (VAD) parameters + fprintf(stderr, "\nVoice Activity Detection (VAD) options:\n"); + fprintf(stderr, " --vad [%-7s] enable Voice Activity Detection (VAD)\n", params.vad ? "true" : "false"); + fprintf(stderr, " -vm FNAME, --vad-model FNAME [%-7s] VAD model path\n", params.vad_model.c_str()); + fprintf(stderr, " -vt N, --vad-threshold N [%-7.2f] VAD threshold for speech recognition\n", params.vad_threshold); + fprintf(stderr, " -vspd N, --vad-min-speech-duration-ms N [%-7d] VAD min speech duration (0.0-1.0)\n", params.vad_min_speech_duration_ms); + fprintf(stderr, " -vsd N, --vad-min-silence-duration-ms N [%-7d] VAD min silence duration (to split segments)\n", params.vad_min_silence_duration_ms); + fprintf(stderr, " -vmsd N, --vad-max-speech-duration-s N [%-7s] VAD max speech duration (auto-split longer)\n", params.vad_max_speech_duration_s == FLT_MAX ? + std::string("FLT_MAX").c_str() : + std::to_string(params.vad_max_speech_duration_s).c_str()); + fprintf(stderr, " -vp N, --vad-speech-pad-ms N [%-7d] VAD speech padding (extend segments)\n", params.vad_speech_pad_ms); + fprintf(stderr, " -vo N, --vad-samples-overlap N [%-7.2f] VAD samples overlap (seconds between segments)\n", params.vad_samples_overlap); + fprintf(stderr, "\n"); +} + +struct whisper_print_user_data { + const whisper_params * params; + + const std::vector> * pcmf32s; + int progress_prev; +}; + +static std::string estimate_diarization_speaker(std::vector> pcmf32s, int64_t t0, int64_t t1, bool id_only = false) { + std::string speaker = ""; + const int64_t n_samples = pcmf32s[0].size(); + + const int64_t is0 = timestamp_to_sample(t0, n_samples, WHISPER_SAMPLE_RATE); + const int64_t is1 = timestamp_to_sample(t1, n_samples, WHISPER_SAMPLE_RATE); + + double energy0 = 0.0f; + double energy1 = 0.0f; + + for (int64_t j = is0; j < is1; j++) { + energy0 += fabs(pcmf32s[0][j]); + energy1 += fabs(pcmf32s[1][j]); + } + + if (energy0 > 1.1*energy1) { + speaker = "0"; + } else if (energy1 > 1.1*energy0) { + speaker = "1"; + } else { + speaker = "?"; + } + + //printf("is0 = %lld, is1 = %lld, energy0 = %f, energy1 = %f, speaker = %s\n", is0, is1, energy0, energy1, speaker.c_str()); + + if (!id_only) { + speaker.insert(0, "(speaker "); + speaker.append(")"); + } + + return speaker; +} + +static void whisper_print_progress_callback(struct whisper_context * /*ctx*/, struct whisper_state * /*state*/, int progress, void * user_data) { + int progress_step = ((whisper_print_user_data *) user_data)->params->progress_step; + int * progress_prev = &(((whisper_print_user_data *) user_data)->progress_prev); + if (progress >= *progress_prev + progress_step) { + *progress_prev += progress_step; + fprintf(stderr, "%s: progress = %3d%%\n", __func__, progress); + } +} + +static void whisper_print_segment_callback(struct whisper_context * ctx, struct whisper_state * /*state*/, int n_new, void * user_data) { + const auto & params = *((whisper_print_user_data *) user_data)->params; + const auto & pcmf32s = *((whisper_print_user_data *) user_data)->pcmf32s; + + const int n_segments = whisper_full_n_segments(ctx); + + std::string speaker = ""; + + int64_t t0 = 0; + int64_t t1 = 0; + + // print the last n_new segments + const int s0 = n_segments - n_new; + + if (s0 == 0) { + printf("\n"); + } + + for (int i = s0; i < n_segments; i++) { + if (!params.no_timestamps || params.diarize) { + t0 = whisper_full_get_segment_t0(ctx, i); + t1 = whisper_full_get_segment_t1(ctx, i); + } + + if (!params.no_timestamps) { + printf("[%s --> %s] ", to_timestamp(t0).c_str(), to_timestamp(t1).c_str()); + } + + if (params.diarize && pcmf32s.size() == 2) { + speaker = estimate_diarization_speaker(pcmf32s, t0, t1); + } + + if (params.print_colors) { + for (int j = 0; j < whisper_full_n_tokens(ctx, i); ++j) { + if (params.print_special == false) { + const whisper_token id = whisper_full_get_token_id(ctx, i, j); + if (id >= whisper_token_eot(ctx)) { + continue; + } + } + + const char * text = whisper_full_get_token_text(ctx, i, j); + const float p = whisper_full_get_token_p (ctx, i, j); + + const int n_colors = (int) k_colors.size(); + int raw_col = (int) (std::pow(p, 3)*float(n_colors)); + if (raw_col < 0) raw_col = 0; + if (raw_col > n_colors - 1) raw_col = n_colors - 1; + const int col = raw_col; + + printf("%s%s%s%s", speaker.c_str(), k_colors[col].c_str(), text, "\033[0m"); + } + } else if (params.print_confidence) { + for (int j = 0; j < whisper_full_n_tokens(ctx, i); ++j) { + if (params.print_special == false) { + const whisper_token id = whisper_full_get_token_id(ctx, i, j); + if (id >= whisper_token_eot(ctx)) { + continue; + } + } + + const char * text = whisper_full_get_token_text(ctx, i, j); + const float p = whisper_full_get_token_p (ctx, i, j); + + int style_idx = 2; // High confidence - dim + if (p < 0.33) { + style_idx = 0; // Low confidence - inverse (highlighted) + } else if (p < 0.66) { + style_idx = 1; // Medium confidence - underlined + } + printf("%s%s%s%s", speaker.c_str(), k_styles[style_idx].c_str(), text, "\033[0m"); + } + } else { + const char * text = whisper_full_get_segment_text(ctx, i); + + printf("%s%s", speaker.c_str(), text); + } + + if (params.tinydiarize) { + if (whisper_full_get_segment_speaker_turn_next(ctx, i)) { + printf("%s", params.tdrz_speaker_turn.c_str()); + } + } + + // with timestamps or speakers: each segment on new line + if (!params.no_timestamps || params.diarize) { + printf("\n"); + } + + fflush(stdout); + } +} + +static void output_txt(struct whisper_context * ctx, std::ofstream & fout, const whisper_params & params, std::vector> pcmf32s) { + const int n_segments = whisper_full_n_segments(ctx); + for (int i = 0; i < n_segments; ++i) { + const char * text = whisper_full_get_segment_text(ctx, i); + std::string speaker = ""; + + if (params.diarize && pcmf32s.size() == 2) + { + const int64_t t0 = whisper_full_get_segment_t0(ctx, i); + const int64_t t1 = whisper_full_get_segment_t1(ctx, i); + speaker = estimate_diarization_speaker(pcmf32s, t0, t1); + } + + if (!speaker.empty()) { + fout << speaker << text; + } else { + while (*text == ' ' || *text == '\t') { + text++; + } + fout << text; + } + fout << "\n"; + } +} + +static void output_vtt(struct whisper_context * ctx, std::ofstream & fout, const whisper_params & params, std::vector> pcmf32s) { + fout << "WEBVTT\n\n"; + + const int n_segments = whisper_full_n_segments(ctx); + for (int i = 0; i < n_segments; ++i) { + const char * text = whisper_full_get_segment_text(ctx, i); + const int64_t t0 = whisper_full_get_segment_t0(ctx, i); + const int64_t t1 = whisper_full_get_segment_t1(ctx, i); + std::string speaker = ""; + + if (params.diarize && pcmf32s.size() == 2) + { + speaker = estimate_diarization_speaker(pcmf32s, t0, t1, true); + speaker.insert(0, ""); + } + + fout << to_timestamp(t0) << " --> " << to_timestamp(t1) << "\n"; + fout << speaker << text << "\n\n"; + } +} + +static void output_srt(struct whisper_context * ctx, std::ofstream & fout, const whisper_params & params, std::vector> pcmf32s) { + const int n_segments = whisper_full_n_segments(ctx); + for (int i = 0; i < n_segments; ++i) { + const char * text = whisper_full_get_segment_text(ctx, i); + const int64_t t0 = whisper_full_get_segment_t0(ctx, i); + const int64_t t1 = whisper_full_get_segment_t1(ctx, i); + std::string speaker = ""; + + if (params.diarize && pcmf32s.size() == 2) + { + speaker = estimate_diarization_speaker(pcmf32s, t0, t1); + } + + fout << i + 1 + params.offset_n << "\n"; + fout << to_timestamp(t0, true) << " --> " << to_timestamp(t1, true) << "\n"; + fout << speaker << text << "\n\n"; + } +} + +static char * escape_double_quotes_and_backslashes(const char * str) { + if (str == NULL) { + return NULL; + } + + size_t escaped_length = strlen(str) + 1; + + for (size_t i = 0; str[i] != '\0'; i++) { + if (str[i] == '"' || str[i] == '\\') { + escaped_length++; + } + } + + char * escaped = (char *)calloc(escaped_length, 1); // pre-zeroed + if (escaped == NULL) { + return NULL; + } + + size_t pos = 0; + for (size_t i = 0; str[i] != '\0'; i++) { + if (str[i] == '"' || str[i] == '\\') { + escaped[pos++] = '\\'; + } + escaped[pos++] = str[i]; + } + + // no need to set zero due to calloc() being used prior + + return escaped; +} + +// double quote should be escaped by another double quote. (rfc4180) +static char * escape_double_quotes_in_csv(const char * str) { + if (str == NULL) { + return NULL; + } + + size_t escaped_length = strlen(str) + 1; + + for (size_t i = 0; str[i] != '\0'; i++) { + if (str[i] == '"') { + escaped_length++; + } + } + + char *escaped = (char *)calloc(escaped_length, 1); // pre-zeroed + if (escaped == NULL) { + return NULL; + } + + size_t pos = 0; + for (size_t i = 0; str[i] != '\0'; i++) { + if (str[i] == '"') { + escaped[pos++] = '"'; + } + escaped[pos++] = str[i]; + } + + // no need to set zero due to calloc() being used prior + + return escaped; +} + +static void output_csv(struct whisper_context * ctx, std::ofstream & fout, const whisper_params & params, std::vector> pcmf32s) { + const int n_segments = whisper_full_n_segments(ctx); + fout << "start,end,"; + if (params.diarize && pcmf32s.size() == 2) + { + fout << "speaker,"; + } + fout << "text\n"; + + for (int i = 0; i < n_segments; ++i) { + const char * text = whisper_full_get_segment_text(ctx, i); + const int64_t t0 = whisper_full_get_segment_t0(ctx, i); + const int64_t t1 = whisper_full_get_segment_t1(ctx, i); + char * text_escaped = escape_double_quotes_in_csv(text); + + //need to multiply times returned from whisper_full_get_segment_t{0,1}() by 10 to get milliseconds. + fout << 10 * t0 << "," << 10 * t1 << ","; + if (params.diarize && pcmf32s.size() == 2) + { + fout << estimate_diarization_speaker(pcmf32s, t0, t1, true) << ","; + } + fout << "\"" << text_escaped << "\"\n"; + } +} + +static void output_score(struct whisper_context * ctx, std::ofstream & fout, const whisper_params & /*params*/, std::vector> /*pcmf32s*/) { + const int n_segments = whisper_full_n_segments(ctx); + // fprintf(stderr,"segments: %d\n",n_segments); + for (int i = 0; i < n_segments; ++i) { + const int n_tokens = whisper_full_n_tokens(ctx, i); + // fprintf(stderr,"tokens: %d\n",n_tokens); + for (int j = 0; j < n_tokens; j++) { + auto token = whisper_full_get_token_text(ctx, i, j); + auto probability = whisper_full_get_token_p(ctx, i, j); + fout << token << '\t' << probability << std::endl; + // fprintf(stderr,"token: %s %f\n",token,probability); + } + } +} + +static void output_json( + struct whisper_context * ctx, + std::ofstream & fout, + const whisper_params & params, + std::vector> pcmf32s) { + const bool full = params.output_jsn_full; + int indent = 0; + + auto doindent = [&]() { + for (int i = 0; i < indent; i++) fout << "\t"; + }; + + auto start_arr = [&](const char *name) { + doindent(); + fout << "\"" << name << "\": [\n"; + indent++; + }; + + auto end_arr = [&](bool end) { + indent--; + doindent(); + fout << (end ? "]\n" : "],\n"); + }; + + auto start_obj = [&](const char *name) { + doindent(); + if (name) { + fout << "\"" << name << "\": {\n"; + } else { + fout << "{\n"; + } + indent++; + }; + + auto end_obj = [&](bool end) { + indent--; + doindent(); + fout << (end ? "}\n" : "},\n"); + }; + + auto start_value = [&](const char *name) { + doindent(); + fout << "\"" << name << "\": "; + }; + + auto value_s = [&](const char *name, const char *val, bool end) { + start_value(name); + char * val_escaped = escape_double_quotes_and_backslashes(val); + fout << "\"" << val_escaped << (end ? "\"\n" : "\",\n"); + free(val_escaped); + }; + + auto end_value = [&](bool end) { + fout << (end ? "\n" : ",\n"); + }; + + auto value_i = [&](const char *name, const int64_t val, bool end) { + start_value(name); + fout << val; + end_value(end); + }; + + auto value_f = [&](const char *name, const float val, bool end) { + start_value(name); + fout << val; + end_value(end); + }; + + auto value_b = [&](const char *name, const bool val, bool end) { + start_value(name); + fout << (val ? "true" : "false"); + end_value(end); + }; + + auto times_o = [&](int64_t t0, int64_t t1, bool end) { + start_obj("timestamps"); + value_s("from", to_timestamp(t0, true).c_str(), false); + value_s("to", to_timestamp(t1, true).c_str(), true); + end_obj(false); + start_obj("offsets"); + value_i("from", t0 * 10, false); + value_i("to", t1 * 10, true); + end_obj(end); + }; + + start_obj(nullptr); + value_s("systeminfo", whisper_print_system_info(), false); + start_obj("model"); + value_s("type", whisper_model_type_readable(ctx), false); + value_b("multilingual", whisper_is_multilingual(ctx), false); + value_i("vocab", whisper_model_n_vocab(ctx), false); + start_obj("audio"); + value_i("ctx", whisper_model_n_audio_ctx(ctx), false); + value_i("state", whisper_model_n_audio_state(ctx), false); + value_i("head", whisper_model_n_audio_head(ctx), false); + value_i("layer", whisper_model_n_audio_layer(ctx), true); + end_obj(false); + start_obj("text"); + value_i("ctx", whisper_model_n_text_ctx(ctx), false); + value_i("state", whisper_model_n_text_state(ctx), false); + value_i("head", whisper_model_n_text_head(ctx), false); + value_i("layer", whisper_model_n_text_layer(ctx), true); + end_obj(false); + value_i("mels", whisper_model_n_mels(ctx), false); + value_i("ftype", whisper_model_ftype(ctx), true); + end_obj(false); + start_obj("params"); + value_s("model", params.model.c_str(), false); + value_s("language", params.language.c_str(), false); + value_b("translate", params.translate, true); + end_obj(false); + start_obj("result"); + value_s("language", whisper_lang_str(whisper_full_lang_id(ctx)), true); + end_obj(false); + start_arr("transcription"); + + const int n_segments = whisper_full_n_segments(ctx); + for (int i = 0; i < n_segments; ++i) { + const char * text = whisper_full_get_segment_text(ctx, i); + + const int64_t t0 = whisper_full_get_segment_t0(ctx, i); + const int64_t t1 = whisper_full_get_segment_t1(ctx, i); + + start_obj(nullptr); + times_o(t0, t1, false); + value_s("text", text, !params.diarize && !params.tinydiarize && !full); + + if (full) { + start_arr("tokens"); + const int n = whisper_full_n_tokens(ctx, i); + + // Merge adjacent tokens whose bytes together form a + // single UTF-8 codepoint. Multi-byte characters (CJK + // in particular) can end up split across whisper + // tokens, which used to produce invalid UTF-8 in the + // JSON string. Refs issue #1798. + struct merged_token { + std::string text; + whisper_token_data data; + int64_t t1; + }; + std::vector merged; + merged.reserve(n); + for (int j = 0; j < n; ) { + auto tok = whisper_full_get_token_data(ctx, i, j); + merged_token m{ whisper_token_to_str(ctx, tok.id), tok, tok.t1 }; + ++j; + while (j < n && utf8_trailing_bytes_needed(m.text) > 0) { + auto tok_next = whisper_full_get_token_data(ctx, i, j); + m.text += whisper_token_to_str(ctx, tok_next.id); + if (tok_next.t1 > -1) { + m.t1 = tok_next.t1; + } + ++j; + } + merged.push_back(std::move(m)); + } + + const int nm = (int) merged.size(); + for (int j = 0; j < nm; ++j) { + const auto & mt = merged[j]; + start_obj(nullptr); + value_s("text", mt.text.c_str(), false); + if (mt.data.t0 > -1 && mt.t1 > -1) { + // If we have per-token timestamps, write them out + times_o(mt.data.t0, mt.t1, false); + } + value_i("id", mt.data.id, false); + value_f("p", mt.data.p, false); + value_f("t_dtw", mt.data.t_dtw, true); + end_obj(j == (nm - 1)); + } + end_arr(!params.diarize && !params.tinydiarize); + } + + if (params.diarize && pcmf32s.size() == 2) { + value_s("speaker", estimate_diarization_speaker(pcmf32s, t0, t1, true).c_str(), true); + } + + if (params.tinydiarize) { + value_b("speaker_turn_next", whisper_full_get_segment_speaker_turn_next(ctx, i), true); + } + end_obj(i == (n_segments - 1)); + } + + end_arr(true); + end_obj(true); +} + +// karaoke video generation +// outputs a bash script that uses ffmpeg to generate a video with the subtitles +// TODO: font parameter adjustments +static bool output_wts(struct whisper_context * ctx, std::ofstream & fout, const whisper_params & params, std::vector> pcmf32s, const char * fname_inp, float t_sec, const char * fname_out) { + static const char * font = params.font_path.c_str(); + + std::ifstream fin(font); + if (!fin.is_open()) { + fprintf(stderr, "%s: font not found at '%s', please specify a monospace font with -fp\n", __func__, font); + return false; + } + + fout << "#!/bin/bash" << "\n"; + fout << "\n"; + + fout << "ffmpeg -i " << fname_inp << " -f lavfi -i color=size=1200x120:duration=" << t_sec << ":rate=25:color=black -vf \""; + + for (int i = 0; i < whisper_full_n_segments(ctx); i++) { + const int64_t t0 = whisper_full_get_segment_t0(ctx, i); + const int64_t t1 = whisper_full_get_segment_t1(ctx, i); + + const int n = whisper_full_n_tokens(ctx, i); + + std::vector tokens(n); + for (int j = 0; j < n; ++j) { + tokens[j] = whisper_full_get_token_data(ctx, i, j); + } + + if (i > 0) { + fout << ","; + } + + // background text + fout << "drawtext=fontfile='" << font << "':fontsize=24:fontcolor=gray:x=(w-text_w)/2:y=h/2:text='':enable='between(t," << t0/100.0 << "," << t0/100.0 << ")'"; + + bool is_first = true; + std::string speaker = ""; + + if (params.diarize && pcmf32s.size() == 2) { + speaker = estimate_diarization_speaker(pcmf32s, t0, t1); + } + + for (int j = 0; j < n; ++j) { + const auto & token = tokens[j]; + + if (tokens[j].id >= whisper_token_eot(ctx)) { + continue; + } + + std::string txt_bg = ""; + std::string txt_fg = ""; // highlight token + std::string txt_ul = ""; // underline + + if (params.diarize && pcmf32s.size() == 2) { + txt_bg = speaker; + txt_fg = speaker; + txt_ul = "\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ "; + } + + txt_bg.append("> "); + txt_fg.append("> "); + txt_ul.append("\\ \\ "); + + { + for (int k = 0; k < n; ++k) { + const auto & token2 = tokens[k]; + + if (tokens[k].id >= whisper_token_eot(ctx)) { + continue; + } + + const std::string txt = whisper_token_to_str(ctx, token2.id); + + txt_bg += txt; + + if (k == j) { + for (int l = 0; l < (int) txt.size(); ++l) { + txt_fg += txt[l]; + txt_ul += "_"; + } + txt_fg += "|"; + } else { + for (int l = 0; l < (int) txt.size(); ++l) { + txt_fg += "\\ "; + txt_ul += "\\ "; + } + } + } + + ::replace_all(txt_bg, "'", "\u2019"); + ::replace_all(txt_bg, "\"", "\\\""); + ::replace_all(txt_fg, "'", "\u2019"); + ::replace_all(txt_fg, "\"", "\\\""); + } + + if (is_first) { + // background text + fout << ",drawtext=fontfile='" << font << "':fontsize=24:fontcolor=gray:x=(w-text_w)/2:y=h/2:text='" << txt_bg << "':enable='between(t," << t0/100.0 << "," << t1/100.0 << ")'"; + is_first = false; + } + + // foreground text + fout << ",drawtext=fontfile='" << font << "':fontsize=24:fontcolor=lightgreen:x=(w-text_w)/2+8:y=h/2:text='" << txt_fg << "':enable='between(t," << token.t0/100.0 << "," << token.t1/100.0 << ")'"; + + // underline + fout << ",drawtext=fontfile='" << font << "':fontsize=24:fontcolor=lightgreen:x=(w-text_w)/2+8:y=h/2+16:text='" << txt_ul << "':enable='between(t," << token.t0/100.0 << "," << token.t1/100.0 << ")'"; + } + } + + fout << "\" -c:v libx264 -pix_fmt yuv420p -y " << fname_inp << ".mp4" << "\n"; + + fout << "\n\n"; + fout << "echo \"Your video has been saved to " << fname_inp << ".mp4\"" << "\n"; + fout << "\n"; + fout << "echo \" ffplay " << fname_inp << ".mp4\"\n"; + fout << "\n"; + + fout.close(); + + fprintf(stderr, "# %s: run 'source %s' to generate karaoke video\n", __func__, fname_out); + + return true; +} + +static void output_lrc(struct whisper_context * ctx, std::ofstream & fout, const whisper_params & params, std::vector> pcmf32s) { + fout << "[by:whisper.cpp]\n"; + + const int n_segments = whisper_full_n_segments(ctx); + for (int i = 0; i < n_segments; ++i) { + const char * text = whisper_full_get_segment_text(ctx, i); + const int64_t t = whisper_full_get_segment_t0(ctx, i); + + int64_t msec = t * 10; + int64_t min = msec / (1000 * 60); + msec = msec - min * (1000 * 60); + int64_t sec = msec / 1000; + msec = msec - sec * 1000; + + char buf[16]; + snprintf(buf, sizeof(buf), "%02d:%02d.%02d", (int) min, (int) sec, (int) ( msec / 10)); + std::string timestamp_lrc = std::string(buf); + std::string speaker = ""; + + if (params.diarize && pcmf32s.size() == 2) + { + const int64_t t0 = whisper_full_get_segment_t0(ctx, i); + const int64_t t1 = whisper_full_get_segment_t1(ctx, i); + speaker = estimate_diarization_speaker(pcmf32s, t0, t1); + } + + fout << '[' << timestamp_lrc << ']' << speaker << text << "\n"; + } +} + + +static void cb_log_disable(enum ggml_log_level , const char * , void * ) { } + +int main(int argc, char ** argv) { + ggml_backend_load_all(); + +#if defined(_WIN32) + // Set the console output code page to UTF-8, while command line arguments + // are still encoded in the system's code page. In this way, we can print + // non-ASCII characters to the console, and access files with non-ASCII paths. + SetConsoleOutputCP(CP_UTF8); +#endif + + whisper_params params; + + // If the only argument starts with "@", read arguments line-by-line + // from the given file. + std::vector vec_args; + if (argc == 2 && argv != nullptr && argv[1] != nullptr && argv[1][0] == '@') { + // Save the name of the executable. + vec_args.push_back(argv[0]); + + // Open the response file. + char const * rspfile = argv[1] + sizeof(char); + std::ifstream fin(rspfile); + if (fin.is_open() == false) { + fprintf(stderr, "error: response file '%s' not found\n", rspfile); + return 1; + } + + // Read the entire response file. + std::string line; + while (std::getline(fin, line)) { + vec_args.push_back(line); + } + + // Use the contents of the response file as the command-line arguments. + argc = static_cast(vec_args.size()); + argv = static_cast(alloca(argc * sizeof (char *))); + for (int i = 0; i < argc; ++i) { + argv[i] = const_cast(vec_args[i].c_str()); + } + } + + if (whisper_params_parse(argc, argv, params) == false) { + whisper_print_usage(argc, argv, params); + return 1; + } + + // remove non-existent files + for (auto it = params.fname_inp.begin(); it != params.fname_inp.end();) { + const auto fname_inp = it->c_str(); + + if (*it != "-" && !is_file_exist(fname_inp)) { + fprintf(stderr, "error: input file not found '%s'\n", fname_inp); + it = params.fname_inp.erase(it); + continue; + } + + it++; + } + + if (params.fname_inp.empty()) { + fprintf(stderr, "error: no input files specified\n"); + whisper_print_usage(argc, argv, params); + return 2; + } + + if (params.language != "auto" && whisper_lang_id(params.language.c_str()) == -1) { + fprintf(stderr, "error: unknown language '%s'\n", params.language.c_str()); + whisper_print_usage(argc, argv, params); + exit(0); + } + + if (params.diarize && params.tinydiarize) { + fprintf(stderr, "error: cannot use both --diarize and --tinydiarize\n"); + whisper_print_usage(argc, argv, params); + exit(0); + } + + if (params.no_prints) { + whisper_log_set(cb_log_disable, NULL); + } + + // whisper init + struct whisper_context_params cparams = whisper_context_default_params(); + + cparams.use_gpu = params.use_gpu; + cparams.gpu_device = params.gpu_device; + cparams.flash_attn = params.flash_attn; + + if (!params.dtw.empty()) { + cparams.dtw_token_timestamps = true; + cparams.dtw_aheads_preset = WHISPER_AHEADS_NONE; + + if (params.dtw == "tiny") cparams.dtw_aheads_preset = WHISPER_AHEADS_TINY; + if (params.dtw == "tiny.en") cparams.dtw_aheads_preset = WHISPER_AHEADS_TINY_EN; + if (params.dtw == "base") cparams.dtw_aheads_preset = WHISPER_AHEADS_BASE; + if (params.dtw == "base.en") cparams.dtw_aheads_preset = WHISPER_AHEADS_BASE_EN; + if (params.dtw == "small") cparams.dtw_aheads_preset = WHISPER_AHEADS_SMALL; + if (params.dtw == "small.en") cparams.dtw_aheads_preset = WHISPER_AHEADS_SMALL_EN; + if (params.dtw == "medium") cparams.dtw_aheads_preset = WHISPER_AHEADS_MEDIUM; + if (params.dtw == "medium.en") cparams.dtw_aheads_preset = WHISPER_AHEADS_MEDIUM_EN; + if (params.dtw == "large.v1") cparams.dtw_aheads_preset = WHISPER_AHEADS_LARGE_V1; + if (params.dtw == "large.v2") cparams.dtw_aheads_preset = WHISPER_AHEADS_LARGE_V2; + if (params.dtw == "large.v3") cparams.dtw_aheads_preset = WHISPER_AHEADS_LARGE_V3; + if (params.dtw == "large.v3.turbo") cparams.dtw_aheads_preset = WHISPER_AHEADS_LARGE_V3_TURBO; + + if (cparams.dtw_aheads_preset == WHISPER_AHEADS_NONE) { + fprintf(stderr, "error: unknown DTW preset '%s'\n", params.dtw.c_str()); + return 3; + } + } + + struct whisper_context * ctx = whisper_init_from_file_with_params(params.model.c_str(), cparams); + + if (ctx == nullptr) { + fprintf(stderr, "error: failed to initialize whisper context\n"); + return 3; + } + + // initialize openvino encoder. this has no effect on whisper.cpp builds that don't have OpenVINO configured + whisper_ctx_init_openvino_encoder(ctx, nullptr, params.openvino_encode_device.c_str(), nullptr); + + if (!params.grammar.empty()) { + auto & grammar = params.grammar_parsed; + if (is_file_exist(params.grammar.c_str())) { + // read grammar from file + std::ifstream ifs(params.grammar.c_str()); + const std::string txt = std::string((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); + grammar = grammar_parser::parse(txt.c_str()); + } else { + // read grammar from string + grammar = grammar_parser::parse(params.grammar.c_str()); + } + + // will be empty (default) if there are parse errors + if (grammar.rules.empty()) { + fprintf(stderr, "error: failed to parse grammar \"%s\"\n", params.grammar.c_str()); + return 4; + } else { + fprintf(stderr, "%s: grammar:\n", __func__); + grammar_parser::print_grammar(stderr, grammar); + fprintf(stderr, "\n"); + } + } + + for (int f = 0; f < (int) params.fname_inp.size(); ++f) { + const auto & fname_inp = params.fname_inp[f]; + struct fout_factory { + std::string fname_out; + const size_t basename_length; + const bool is_stdout; + bool used_stdout; + decltype(whisper_print_segment_callback) * const print_segment_callback; + std::ofstream fout; + + fout_factory (const std::string & fname_out_, const std::string & fname_inp, whisper_params & params) : + fname_out{!fname_out_.empty() ? fname_out_ : fname_inp}, + basename_length{fname_out.size()}, + is_stdout{fname_out == "-"}, + used_stdout{}, + print_segment_callback{is_stdout ? nullptr : whisper_print_segment_callback} { + if (!print_segment_callback) { + params.print_progress = false; + } + } + + bool open(const char * ext, const char * function) { + if (is_stdout) { + if (used_stdout) { + fprintf(stderr, "warning: Not appending multiple file formats to stdout\n"); + return false; + } + + used_stdout = true; +#ifdef _WIN32 + fout = std::ofstream{"CON"}; +#else + fout = std::ofstream{"/dev/stdout"}; +#endif + // Not using fprintf stderr here because it might equal stdout + // Also assuming /dev is mounted + return true; + } + + fname_out.resize(basename_length); + fname_out += ext; + fout = std::ofstream{fname_out}; + if (!fout.is_open()) { + fprintf(stderr, "%s: failed to open '%s' for writing\n", __func__, fname_out.c_str()); + return false; + } + fprintf(stderr, "%s: saving output to '%s'\n", function, fname_out.c_str()); + return true; + } + } fout_factory{f < (int) params.fname_out.size() ? params.fname_out[f] : "", fname_inp, params}; + + std::vector pcmf32; // mono-channel F32 PCM + std::vector> pcmf32s; // stereo-channel F32 PCM + + if (!::read_audio_data(fname_inp, pcmf32, pcmf32s, params.diarize)) { + fprintf(stderr, "error: failed to read audio file '%s'\n", fname_inp.c_str()); + continue; + } + + if (!whisper_is_multilingual(ctx)) { + if (params.language != "en" || params.translate) { + params.language = "en"; + params.translate = false; + fprintf(stderr, "%s: WARNING: model is not multilingual, ignoring language and translation options\n", __func__); + } + } + if (params.detect_language) { + params.language = "auto"; + } + + if (!params.no_prints) { + // print system information + fprintf(stderr, "\n"); + fprintf(stderr, "system_info: n_threads = %d / %d | %s\n", + params.n_threads*params.n_processors, std::thread::hardware_concurrency(), whisper_print_system_info()); + + // print some info about the processing + fprintf(stderr, "\n"); + fprintf(stderr, "%s: processing '%s' (%d samples, %.1f sec), %d threads, %d processors, %d beams + best of %d, lang = %s, task = %s, %stimestamps = %d ...\n", + __func__, fname_inp.c_str(), int(pcmf32.size()), float(pcmf32.size())/WHISPER_SAMPLE_RATE, + params.n_threads, params.n_processors, params.beam_size, params.best_of, + params.language.c_str(), + params.translate ? "translate" : "transcribe", + params.tinydiarize ? "tdrz = 1, " : "", + params.no_timestamps ? 0 : 1); + + if (params.print_colors) { + fprintf(stderr, "%s: color scheme: red (low confidence), yellow (medium), green (high confidence)\n", __func__); + } else if (params.print_confidence) { + fprintf(stderr, "%s: confidence: highlighted (low confidence), underlined (medium), dim (high confidence)\n", __func__); + } + fprintf(stderr, "\n"); + } + + // run the inference + { + whisper_full_params wparams = whisper_full_default_params(WHISPER_SAMPLING_GREEDY); + + const bool use_grammar = (!params.grammar_parsed.rules.empty() && !params.grammar_rule.empty()); + wparams.strategy = (params.beam_size > 1 || use_grammar) ? WHISPER_SAMPLING_BEAM_SEARCH : WHISPER_SAMPLING_GREEDY; + + wparams.print_realtime = false; + wparams.print_progress = params.print_progress; + wparams.print_timestamps = !params.no_timestamps; + wparams.print_special = params.print_special; + wparams.translate = params.translate; + wparams.language = params.language.c_str(); + wparams.detect_language = params.detect_language; + wparams.n_threads = params.n_threads; + wparams.n_max_text_ctx = params.max_context >= 0 ? params.max_context : wparams.n_max_text_ctx; + wparams.offset_ms = params.offset_t_ms; + wparams.duration_ms = params.duration_ms; + + wparams.token_timestamps = params.output_wts || params.output_jsn_full || params.max_len > 0; + wparams.thold_pt = params.word_thold; + wparams.max_len = params.output_wts && params.max_len == 0 ? 60 : params.max_len; + wparams.split_on_word = params.split_on_word; + wparams.audio_ctx = params.audio_ctx; + + wparams.debug_mode = params.debug_mode; + + wparams.tdrz_enable = params.tinydiarize; // [TDRZ] + + wparams.suppress_regex = params.suppress_regex.empty() ? nullptr : params.suppress_regex.c_str(); + + wparams.initial_prompt = params.prompt.c_str(); + wparams.carry_initial_prompt = params.carry_initial_prompt; + + wparams.greedy.best_of = params.best_of; + wparams.beam_search.beam_size = params.beam_size; + + wparams.temperature_inc = params.no_fallback ? 0.0f : params.temperature_inc; + wparams.temperature = params.temperature; + + wparams.entropy_thold = params.entropy_thold; + wparams.logprob_thold = params.logprob_thold; + wparams.no_speech_thold = params.no_speech_thold; + + wparams.no_timestamps = params.no_timestamps; + + wparams.suppress_nst = params.suppress_nst; + + wparams.vad = params.vad; + wparams.vad_model_path = params.vad_model.c_str(); + + wparams.vad_params.threshold = params.vad_threshold; + wparams.vad_params.min_speech_duration_ms = params.vad_min_speech_duration_ms; + wparams.vad_params.min_silence_duration_ms = params.vad_min_silence_duration_ms; + wparams.vad_params.max_speech_duration_s = params.vad_max_speech_duration_s; + wparams.vad_params.speech_pad_ms = params.vad_speech_pad_ms; + wparams.vad_params.samples_overlap = params.vad_samples_overlap; + + whisper_print_user_data user_data = { ¶ms, &pcmf32s, 0 }; + + const auto & grammar_parsed = params.grammar_parsed; + auto grammar_rules = grammar_parsed.c_rules(); + + if (use_grammar) { + if (grammar_parsed.symbol_ids.find(params.grammar_rule) == grammar_parsed.symbol_ids.end()) { + fprintf(stderr, "%s: warning: grammar rule '%s' not found - skipping grammar sampling\n", __func__, params.grammar_rule.c_str()); + } else { + wparams.grammar_rules = grammar_rules.data(); + wparams.n_grammar_rules = grammar_rules.size(); + wparams.i_start_rule = grammar_parsed.symbol_ids.at(params.grammar_rule); + wparams.grammar_penalty = params.grammar_penalty; + } + } + + // this callback is called on each new segment + if (!wparams.print_realtime) { + wparams.new_segment_callback = fout_factory.print_segment_callback; + wparams.new_segment_callback_user_data = &user_data; + } + + if (wparams.print_progress) { + wparams.progress_callback = whisper_print_progress_callback; + wparams.progress_callback_user_data = &user_data; + } + + // examples for abort mechanism + // in examples below, we do not abort the processing, but we could if the flag is set to true + + // the callback is called before every encoder run - if it returns false, the processing is aborted + { + static bool is_aborted = false; // NOTE: this should be atomic to avoid data race + + wparams.encoder_begin_callback = [](struct whisper_context * /*ctx*/, struct whisper_state * /*state*/, void * user_data) { + bool is_aborted = *(bool*)user_data; + return !is_aborted; + }; + wparams.encoder_begin_callback_user_data = &is_aborted; + } + + // the callback is called before every computation - if it returns true, the computation is aborted + { + static bool is_aborted = false; // NOTE: this should be atomic to avoid data race + + wparams.abort_callback = [](void * user_data) { + bool is_aborted = *(bool*)user_data; + return is_aborted; + }; + wparams.abort_callback_user_data = &is_aborted; + } + + if (whisper_full_parallel(ctx, wparams, pcmf32.data(), pcmf32.size(), params.n_processors) != 0) { + fprintf(stderr, "%s: failed to process audio\n", argv[0]); + return 10; + } + } + + // output stuff + { + // macros to stringify function name +#define output_func(func, ext, param, ...) if (param && fout_factory.open(ext, #func)) {\ + func(ctx, fout_factory.fout, params, __VA_ARGS__); \ +} +#define output_ext(ext, ...) output_func(output_##ext, "." #ext, params.output_##ext, __VA_ARGS__) + + output_ext(txt, pcmf32s); + output_ext(vtt, pcmf32s); + output_ext(srt, pcmf32s); + output_ext(wts, pcmf32s, fname_inp.c_str(), float(pcmf32.size() + 1000)/WHISPER_SAMPLE_RATE, fout_factory.fname_out.c_str()); + output_ext(csv, pcmf32s); + output_func(output_json, ".json", params.output_jsn, pcmf32s); + output_ext(lrc, pcmf32s); + output_func(output_score, ".score.txt", params.log_score, pcmf32s); + +#undef output_ext +#undef output_func + + if (fout_factory.is_stdout && !fout_factory.used_stdout) { + fprintf(stderr, "warning: '--output-file -' used without any other '--output-*'"); + } + } + } + + if (!params.no_prints) { + whisper_print_timings(ctx); + } + whisper_free(ctx); + + return 0; +} diff --git a/examples/coi-serviceworker.js b/examples/coi-serviceworker.js new file mode 100644 index 0000000..9901474 --- /dev/null +++ b/examples/coi-serviceworker.js @@ -0,0 +1,146 @@ +/*! coi-serviceworker v0.1.7 - Guido Zuidhof and contributors, licensed under MIT */ +let coepCredentialless = false; +if (typeof window === 'undefined') { + self.addEventListener("install", () => self.skipWaiting()); + self.addEventListener("activate", (event) => event.waitUntil(self.clients.claim())); + + self.addEventListener("message", (ev) => { + if (!ev.data) { + return; + } else if (ev.data.type === "deregister") { + self.registration + .unregister() + .then(() => { + return self.clients.matchAll(); + }) + .then(clients => { + clients.forEach((client) => client.navigate(client.url)); + }); + } else if (ev.data.type === "coepCredentialless") { + coepCredentialless = ev.data.value; + } + }); + + self.addEventListener("fetch", function (event) { + const r = event.request; + if (r.cache === "only-if-cached" && r.mode !== "same-origin") { + return; + } + + const request = (coepCredentialless && r.mode === "no-cors") + ? new Request(r, { + credentials: "omit", + }) + : r; + event.respondWith( + fetch(request) + .then((response) => { + if (response.status === 0) { + return response; + } + + const newHeaders = new Headers(response.headers); + newHeaders.set("Cross-Origin-Embedder-Policy", + coepCredentialless ? "credentialless" : "require-corp" + ); + if (!coepCredentialless) { + newHeaders.set("Cross-Origin-Resource-Policy", "cross-origin"); + } + newHeaders.set("Cross-Origin-Opener-Policy", "same-origin"); + + return new Response(response.body, { + status: response.status, + statusText: response.statusText, + headers: newHeaders, + }); + }) + .catch((e) => console.error(e)) + ); + }); + +} else { + (() => { + const reloadedBySelf = window.sessionStorage.getItem("coiReloadedBySelf"); + window.sessionStorage.removeItem("coiReloadedBySelf"); + const coepDegrading = (reloadedBySelf == "coepdegrade"); + + // You can customize the behavior of this script through a global `coi` variable. + const coi = { + shouldRegister: () => !reloadedBySelf, + shouldDeregister: () => false, + coepCredentialless: () => true, + coepDegrade: () => true, + doReload: () => window.location.reload(), + quiet: false, + ...window.coi + }; + + const n = navigator; + const controlling = n.serviceWorker && n.serviceWorker.controller; + + // Record the failure if the page is served by serviceWorker. + if (controlling && !window.crossOriginIsolated) { + window.sessionStorage.setItem("coiCoepHasFailed", "true"); + } + const coepHasFailed = window.sessionStorage.getItem("coiCoepHasFailed"); + + if (controlling) { + // Reload only on the first failure. + const reloadToDegrade = coi.coepDegrade() && !( + coepDegrading || window.crossOriginIsolated + ); + n.serviceWorker.controller.postMessage({ + type: "coepCredentialless", + value: (reloadToDegrade || coepHasFailed && coi.coepDegrade()) + ? false + : coi.coepCredentialless(), + }); + if (reloadToDegrade) { + !coi.quiet && console.log("Reloading page to degrade COEP."); + window.sessionStorage.setItem("coiReloadedBySelf", "coepdegrade"); + coi.doReload("coepdegrade"); + } + + if (coi.shouldDeregister()) { + n.serviceWorker.controller.postMessage({ type: "deregister" }); + } + } + + // If we're already coi: do nothing. Perhaps it's due to this script doing its job, or COOP/COEP are + // already set from the origin server. Also if the browser has no notion of crossOriginIsolated, just give up here. + if (window.crossOriginIsolated !== false || !coi.shouldRegister()) return; + + if (!window.isSecureContext) { + !coi.quiet && console.log("COOP/COEP Service Worker not registered, a secure context is required."); + return; + } + + // In some environments (e.g. Firefox private mode) this won't be available + if (!n.serviceWorker) { + !coi.quiet && console.error("COOP/COEP Service Worker not registered, perhaps due to private mode."); + return; + } + + n.serviceWorker.register(window.document.currentScript.src).then( + (registration) => { + !coi.quiet && console.log("COOP/COEP Service Worker registered", registration.scope); + + registration.addEventListener("updatefound", () => { + !coi.quiet && console.log("Reloading page to make use of updated COOP/COEP Service Worker."); + window.sessionStorage.setItem("coiReloadedBySelf", "updatefound"); + coi.doReload(); + }); + + // If the registration is active, but it's not controlling the page + if (registration.active && !n.serviceWorker.controller) { + !coi.quiet && console.log("Reloading page to make use of COOP/COEP Service Worker."); + window.sessionStorage.setItem("coiReloadedBySelf", "notcontrolling"); + coi.doReload(); + } + }, + (err) => { + !coi.quiet && console.error("COOP/COEP Service Worker failed to register:", err); + } + ); + })(); +} diff --git a/examples/command.wasm/CMakeLists.txt b/examples/command.wasm/CMakeLists.txt new file mode 100644 index 0000000..7c2c429 --- /dev/null +++ b/examples/command.wasm/CMakeLists.txt @@ -0,0 +1,50 @@ +# +# libcommand +# + +set(TARGET libcommand) + +add_executable(${TARGET} + emscripten.cpp + ) + +include(DefaultTargetOptions) + +target_link_libraries(${TARGET} PRIVATE + common + whisper + ) + +unset(EXTRA_FLAGS) + +if (WHISPER_WASM_SINGLE_FILE) + set(EXTRA_FLAGS "-s SINGLE_FILE=1") + message(STATUS "Embedding WASM inside command.js") + + add_custom_command( + TARGET ${TARGET} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_BINARY_DIR}/bin/libcommand.js + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/command.wasm/command.js + ) +endif() + +set_target_properties(${TARGET} PROPERTIES LINK_FLAGS " \ + --bind \ + -s USE_PTHREADS=1 \ + -s PTHREAD_POOL_SIZE=8 \ + -s INITIAL_MEMORY=1024MB \ + -s TOTAL_MEMORY=1024MB \ + -s FORCE_FILESYSTEM=1 \ + -s EXPORTED_RUNTIME_METHODS=\"['print', 'printErr', 'ccall', 'cwrap', 'HEAPU8']\" \ + ${EXTRA_FLAGS} \ + ") + +# +# command.wasm +# + +set(TARGET command.wasm) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/index-tmpl.html ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}/index.html @ONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../helpers.js ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}/helpers.js @ONLY) diff --git a/examples/command.wasm/README.md b/examples/command.wasm/README.md new file mode 100644 index 0000000..d134f2a --- /dev/null +++ b/examples/command.wasm/README.md @@ -0,0 +1,47 @@ +# command.wasm + +This is a basic Voice Assistant example that accepts voice commands from the microphone. +It runs in fully in the browser via WebAseembly. + +Online demo: https://ggml.ai/whisper.cpp/command.wasm/ + +Terminal version: [examples/command](/examples/command) + +## Build instructions + +```bash +# build using Emscripten (v3.1.2) +git clone https://github.com/ggerganov/whisper.cpp +cd whisper.cpp +mkdir build-em && cd build-em +emcmake cmake .. +make -j libcommand +``` +The example can then be started by running a local HTTP server: +```console +python3 examples/server.py +``` +And then opening a browser to the following URL: +http://localhost:8000/command.wasm/ + +To run the example in a different server, you need to copy the following files +to the server's HTTP path: +``` +cp bin/command.wasm/* /path/to/html/ +cp bin/libcommand.js /path/to/html/ +cp bin/libcommand.worker.js /path/to/html/ +``` + +> 📝 **Note:** By default this example is built with `WHISPER_WASM_SINGLE_FILE=ON` +> which means that that a separate .wasm file will not be generated. Instead, the +> WASM module is embedded in the main JS file as a base64 encoded string. To +> generate a separate .wasm file, you need to disable this option by passing +> `-DWHISPER_WASM_SINGLE_FILE=OFF`: +> ```console +> emcmake cmake .. -DWHISPER_WASM_SINGLE_FILE=OFF +> ``` +> This will generate a `libcommand.wasm` file in the build/bin directory. + +> 📝 **Note:** As of Emscripten 3.1.58 (April 2024), separate worker.js files are no +> longer generated and the worker is embedded in the main JS file. So the worker +> file will not be generated for versions later than `3.1.58`. diff --git a/examples/command.wasm/emscripten.cpp b/examples/command.wasm/emscripten.cpp new file mode 100644 index 0000000..528ff6a --- /dev/null +++ b/examples/command.wasm/emscripten.cpp @@ -0,0 +1,327 @@ +#include "ggml.h" +#include "common.h" +#include "whisper.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +constexpr int N_THREAD = 8; + +std::vector g_contexts(4, nullptr); + +std::mutex g_mutex; +std::thread g_worker; + +std::atomic g_running(false); + +std::string g_status = ""; +std::string g_status_forced = ""; +std::string g_transcribed = ""; + +std::vector g_pcmf32; + +void command_set_status(const std::string & status) { + std::lock_guard lock(g_mutex); + g_status = status; +} + +std::string command_transcribe(whisper_context * ctx, const whisper_full_params & wparams, const std::vector & pcmf32, float & prob, int64_t & t_ms) { + const auto t_start = std::chrono::high_resolution_clock::now(); + + prob = 0.0f; + t_ms = 0; + + if (whisper_full(ctx, wparams, pcmf32.data(), pcmf32.size()) != 0) { + return ""; + } + + int prob_n = 0; + std::string result; + + const int n_segments = whisper_full_n_segments(ctx); + for (int i = 0; i < n_segments; ++i) { + const char * text = whisper_full_get_segment_text(ctx, i); + + result += text; + + const int n_tokens = whisper_full_n_tokens(ctx, i); + for (int j = 0; j < n_tokens; ++j) { + const auto token = whisper_full_get_token_data(ctx, i, j); + + prob += token.p; + ++prob_n; + } + } + + if (prob_n > 0) { + prob /= prob_n; + } + + const auto t_end = std::chrono::high_resolution_clock::now(); + t_ms = std::chrono::duration_cast(t_end - t_start).count(); + + return result; +} + +void command_get_audio(int ms, int sample_rate, std::vector & audio) { + const int64_t n_samples = (ms * sample_rate) / 1000; + + int64_t n_take = 0; + if (n_samples > (int) g_pcmf32.size()) { + n_take = g_pcmf32.size(); + } else { + n_take = n_samples; + } + + audio.resize(n_take); + std::copy(g_pcmf32.end() - n_take, g_pcmf32.end(), audio.begin()); +} + +void command_main(size_t index) { + command_set_status("loading data ..."); + + struct whisper_full_params wparams = whisper_full_default_params(whisper_sampling_strategy::WHISPER_SAMPLING_GREEDY); + + wparams.n_threads = std::min(N_THREAD, (int) std::thread::hardware_concurrency()); + wparams.offset_ms = 0; + wparams.translate = false; + wparams.no_context = true; + wparams.single_segment = true; + wparams.print_realtime = false; + wparams.print_progress = false; + wparams.print_timestamps = true; + wparams.print_special = false; + + wparams.max_tokens = 32; + wparams.audio_ctx = 768; // partial encoder context for better performance + + wparams.language = "en"; + + printf("command: using %d threads\n", wparams.n_threads); + + bool have_prompt = false; + bool ask_prompt = true; + bool print_energy = false; + + float prob0 = 0.0f; + float prob = 0.0f; + + std::vector pcmf32_cur; + std::vector pcmf32_prompt; + + const std::string k_prompt = "Ok Whisper, start listening for commands."; + + // whisper context + auto & ctx = g_contexts[index]; + + const int32_t vad_ms = 2000; + const int32_t prompt_ms = 5000; + const int32_t command_ms = 4000; + + const float vad_thold = 0.1f; + const float freq_thold = -1.0f; + + while (g_running) { + // delay + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + if (ask_prompt) { + fprintf(stdout, "\n"); + fprintf(stdout, "%s: Say the following phrase: '%s%s%s'\n", __func__, "\033[1m", k_prompt.c_str(), "\033[0m"); + fprintf(stdout, "\n"); + + { + char txt[1024]; + snprintf(txt, sizeof(txt), "Say the following phrase: '%s'", k_prompt.c_str()); + command_set_status(txt); + } + + ask_prompt = false; + } + + int64_t t_ms = 0; + + { + command_get_audio(vad_ms, WHISPER_SAMPLE_RATE, pcmf32_cur); + + if (::vad_simple(pcmf32_cur, WHISPER_SAMPLE_RATE, 1000, vad_thold, freq_thold, print_energy)) { + fprintf(stdout, "%s: Speech detected! Processing ...\n", __func__); + command_set_status("Speech detected! Processing ..."); + + if (!have_prompt) { + command_get_audio(prompt_ms, WHISPER_SAMPLE_RATE, pcmf32_cur); + + const auto txt = ::trim(::command_transcribe(ctx, wparams, pcmf32_cur, prob0, t_ms)); + + fprintf(stdout, "%s: Heard '%s%s%s', (t = %d ms)\n", __func__, "\033[1m", txt.c_str(), "\033[0m", (int) t_ms); + + const float sim = similarity(txt, k_prompt); + + if (txt.length() < 0.8*k_prompt.length() || txt.length() > 1.2*k_prompt.length() || sim < 0.8f) { + fprintf(stdout, "%s: WARNING: prompt not recognized, try again\n", __func__); + ask_prompt = true; + } else { + fprintf(stdout, "\n"); + fprintf(stdout, "%s: The prompt has been recognized!\n", __func__); + fprintf(stdout, "%s: Waiting for voice commands ...\n", __func__); + fprintf(stdout, "\n"); + + { + char txt[1024]; + snprintf(txt, sizeof(txt), "Success! Waiting for voice commands ..."); + command_set_status(txt); + } + + // save the audio for the prompt + pcmf32_prompt = pcmf32_cur; + have_prompt = true; + } + } else { + command_get_audio(command_ms, WHISPER_SAMPLE_RATE, pcmf32_cur); + + // prepend the prompt audio + pcmf32_cur.insert(pcmf32_cur.begin(), pcmf32_prompt.begin(), pcmf32_prompt.end()); + + const auto txt = ::trim(::command_transcribe(ctx, wparams, pcmf32_cur, prob, t_ms)); + + prob = 100.0f*(prob - prob0); + + fprintf(stdout, "%s: heard '%s'\n", __func__, txt.c_str()); + + // find the prompt in the text + float best_sim = 0.0f; + size_t best_len = 0; + for (int n = 0.8*k_prompt.size(); n <= 1.2*k_prompt.size(); ++n) { + const auto prompt = txt.substr(0, n); + + const float sim = similarity(prompt, k_prompt); + + //fprintf(stderr, "%s: prompt = '%s', sim = %f\n", __func__, prompt.c_str(), sim); + + if (sim > best_sim) { + best_sim = sim; + best_len = n; + } + } + + const std::string command = ::trim(txt.substr(best_len)); + + fprintf(stdout, "%s: Command '%s%s%s', (t = %d ms)\n", __func__, "\033[1m", command.c_str(), "\033[0m", (int) t_ms); + fprintf(stdout, "\n"); + + { + char txt[1024]; + snprintf(txt, sizeof(txt), "Command '%s', (t = %d ms)", command.c_str(), (int) t_ms); + command_set_status(txt); + } + { + std::lock_guard lock(g_mutex); + g_transcribed = command; + } + } + + g_pcmf32.clear(); + } + } + } + + if (index < g_contexts.size()) { + whisper_free(g_contexts[index]); + g_contexts[index] = nullptr; + } +} + +EMSCRIPTEN_BINDINGS(command) { + emscripten::function("init", emscripten::optional_override([](const std::string & path_model) { + for (size_t i = 0; i < g_contexts.size(); ++i) { + if (g_contexts[i] == nullptr) { + g_contexts[i] = whisper_init_from_file_with_params(path_model.c_str(), whisper_context_default_params()); + if (g_contexts[i] != nullptr) { + g_running = true; + if (g_worker.joinable()) { + g_worker.join(); + } + g_worker = std::thread([i]() { + command_main(i); + }); + + return i + 1; + } else { + return (size_t) 0; + } + } + } + + return (size_t) 0; + })); + + emscripten::function("free", emscripten::optional_override([](size_t index) { + if (g_running) { + g_running = false; + } + })); + + emscripten::function("set_audio", emscripten::optional_override([](size_t index, const emscripten::val & audio) { + --index; + + if (index >= g_contexts.size()) { + return -1; + } + + if (g_contexts[index] == nullptr) { + return -2; + } + + { + std::lock_guard lock(g_mutex); + const int n = audio["length"].as(); + + emscripten::val heap = emscripten::val::module_property("HEAPU8"); + emscripten::val memory = heap["buffer"]; + + g_pcmf32.resize(n); + + emscripten::val memoryView = audio["constructor"].new_(memory, reinterpret_cast(g_pcmf32.data()), n); + memoryView.call("set", audio); + } + + return 0; + })); + + emscripten::function("get_transcribed", emscripten::optional_override([]() { + std::string transcribed; + + { + std::lock_guard lock(g_mutex); + transcribed = std::move(g_transcribed); + } + + return transcribed; + })); + + emscripten::function("get_status", emscripten::optional_override([]() { + std::string status; + + { + std::lock_guard lock(g_mutex); + status = g_status_forced.empty() ? g_status : g_status_forced; + } + + return status; + })); + + emscripten::function("set_status", emscripten::optional_override([](const std::string & status) { + { + std::lock_guard lock(g_mutex); + g_status_forced = status; + } + })); +} diff --git a/examples/command.wasm/index-tmpl.html b/examples/command.wasm/index-tmpl.html new file mode 100644 index 0000000..b8dabba --- /dev/null +++ b/examples/command.wasm/index-tmpl.html @@ -0,0 +1,415 @@ + + + + command : Voice assistant example using Whisper + WebAssembly + + + + + + +
+ command : Voice assistant example using Whisper + WebAssembly + +

+ + You can find more about this project on GitHub. + +

+ + More examples: + main | + bench | + stream | + command | + wchess | + +

+ +
+ + Select the model you would like to use, click the "Start" button and follow the instructions. + +

+ +
+ Whisper model: + + +

+ Quantized models:

+ + + + + +
+ +
+ +
+ + + +
+ +
+ +
+ Status: not started + +
[The recognized voice commands will be displayed here]
+
+ +
+ + Debug output: + + +
+ + Troubleshooting + +

+ + The page does some heavy computations, so make sure: + +
    +
  • To use a modern web browser (e.g. Chrome, Firefox)
  • +
  • To use a fast desktop or laptop computer (i.e. not a mobile phone)
  • +
  • Your browser supports WASM Fixed-width SIMD
  • +
+ +
+ + | + Build time: @GIT_DATE@ | + Commit hash: @GIT_SHA1@ | + Commit subject: @GIT_COMMIT_SUBJECT@ | + Source Code | + +
+
+ + + + + + diff --git a/examples/command/CMakeLists.txt b/examples/command/CMakeLists.txt new file mode 100644 index 0000000..c929a6f --- /dev/null +++ b/examples/command/CMakeLists.txt @@ -0,0 +1,10 @@ +if (WHISPER_SDL2) + set(TARGET whisper-command) + add_executable(${TARGET} command.cpp) + + include(DefaultTargetOptions) + + target_link_libraries(${TARGET} PRIVATE common common-sdl whisper ${CMAKE_THREAD_LIBS_INIT}) + + install(TARGETS ${TARGET} RUNTIME) +endif () diff --git a/examples/command/README.md b/examples/command/README.md new file mode 100644 index 0000000..7eb2bb6 --- /dev/null +++ b/examples/command/README.md @@ -0,0 +1,52 @@ +# whisper.cpp/examples/command + +This is a basic Voice Assistant example that accepts voice commands from the microphone. +More info is available in [issue #171](https://github.com/ggerganov/whisper.cpp/issues/171). + +```bash +# Run with default arguments and small model +./whisper-command -m ./models/ggml-small.en.bin -t 8 + +# On Raspberry Pi, use tiny or base models + "-ac 768" for better performance +./whisper-command -m ./models/ggml-tiny.en.bin -ac 768 -t 3 -c 0 +``` + +https://user-images.githubusercontent.com/1991296/204038393-2f846eae-c255-4099-a76d-5735c25c49da.mp4 + +Web version: [examples/command.wasm](/examples/command.wasm) + +## Guided mode + +"Guided mode" allows you to specify a list of commands (i.e. strings) and the transcription will be guided to classify your command into one from the list. This can be useful in situations where a device is listening only for a small subset of commands. + +Initial tests show that this approach might be extremely efficient in terms of performance, since it integrates very well with the "partial Encoder" idea from #137. + +```bash +# Run in guided mode, the list of allowed commands is in commands.txt +./whisper-command -m ./models/ggml-base.en.bin -cmd ./examples/command/commands.txt + +# On Raspberry Pi, in guided mode you can use "-ac 128" for extra performance +./whisper-command -m ./models/ggml-tiny.en.bin -cmd ./examples/command/commands.txt -ac 128 -t 3 -c 0 +``` + +https://user-images.githubusercontent.com/1991296/207435352-8fc4ed3f-bde5-4555-9b8b-aeeb76bee969.mp4 + + +## Building + +The `whisper-command` tool depends on SDL2 library to capture audio from the microphone. You can build it like this: + +```bash +# Install SDL2 +# On Debian based linux distributions: +sudo apt-get install libsdl2-dev + +# On Fedora Linux: +sudo dnf install SDL2 SDL2-devel + +# Install SDL2 on Mac OS +brew install sdl2 + +cmake -B build -DWHISPER_SDL2=ON +cmake --build build --config Release +``` diff --git a/examples/command/command.cpp b/examples/command/command.cpp new file mode 100644 index 0000000..ff7c037 --- /dev/null +++ b/examples/command/command.cpp @@ -0,0 +1,802 @@ +// Voice assistant example +// +// Speak short text commands to the microphone. +// This program will detect your voice command and convert them to text. +// +// ref: https://github.com/ggml-org/whisper.cpp/issues/171 +// + +#include "common-sdl.h" +#include "common.h" +#include "whisper.h" +#include "grammar-parser.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// command-line parameters +struct whisper_params { + int32_t n_threads = std::min(4, (int32_t) std::thread::hardware_concurrency()); + int32_t prompt_ms = 5000; + int32_t command_ms = 8000; + int32_t capture_id = -1; + int32_t max_tokens = 32; + int32_t audio_ctx = 0; + + float vad_thold = 0.6f; + float freq_thold = 100.0f; + + float grammar_penalty = 100.0f; + + grammar_parser::parse_state grammar_parsed; + + bool translate = false; + bool print_special = false; + bool print_energy = false; + bool no_timestamps = true; + bool use_gpu = true; + bool flash_attn = true; + + std::string language = "en"; + std::string model = "models/ggml-base.en.bin"; + std::string fname_out; + std::string commands; + std::string prompt; + std::string context; + std::string grammar; + + // A regular expression that matches tokens to suppress + std::string suppress_regex; +}; + +void whisper_print_usage(int argc, char ** argv, const whisper_params & params); + +static bool whisper_params_parse(int argc, char ** argv, whisper_params & params) { + for (int i = 1; i < argc; i++) { + std::string arg = argv[i]; + + if (arg == "-h" || arg == "--help") { + whisper_print_usage(argc, argv, params); + exit(0); + } + else if (arg == "-t" || arg == "--threads") { params.n_threads = std::stoi(argv[++i]); } + else if (arg == "-pms" || arg == "--prompt-ms") { params.prompt_ms = std::stoi(argv[++i]); } + else if (arg == "-cms" || arg == "--command-ms") { params.command_ms = std::stoi(argv[++i]); } + else if (arg == "-c" || arg == "--capture") { params.capture_id = std::stoi(argv[++i]); } + else if (arg == "-mt" || arg == "--max-tokens") { params.max_tokens = std::stoi(argv[++i]); } + else if (arg == "-ac" || arg == "--audio-ctx") { params.audio_ctx = std::stoi(argv[++i]); } + else if (arg == "-vth" || arg == "--vad-thold") { params.vad_thold = std::stof(argv[++i]); } + else if (arg == "-fth" || arg == "--freq-thold") { params.freq_thold = std::stof(argv[++i]); } + else if (arg == "-tr" || arg == "--translate") { params.translate = true; } + else if (arg == "-ps" || arg == "--print-special") { params.print_special = true; } + else if (arg == "-pe" || arg == "--print-energy") { params.print_energy = true; } + else if (arg == "-ng" || arg == "--no-gpu") { params.use_gpu = false; } + else if (arg == "-fa" || arg == "--flash-attn") { params.flash_attn = true; } + else if (arg == "-nfa" || arg == "--no-flash-attn") { params.flash_attn = false; } + else if (arg == "-l" || arg == "--language") { params.language = argv[++i]; } + else if (arg == "-m" || arg == "--model") { params.model = argv[++i]; } + else if (arg == "-f" || arg == "--file") { params.fname_out = argv[++i]; } + else if (arg == "-cmd" || arg == "--commands") { params.commands = argv[++i]; } + else if (arg == "-p" || arg == "--prompt") { params.prompt = argv[++i]; } + else if (arg == "-ctx" || arg == "--context") { params.context = argv[++i]; } + else if ( arg == "--grammar") { params.grammar = argv[++i]; } + else if ( arg == "--grammar-penalty") { params.grammar_penalty = std::stof(argv[++i]); } + else if ( arg == "--suppress-regex") { params.suppress_regex = argv[++i]; } + else { + fprintf(stderr, "error: unknown argument: %s\n", arg.c_str()); + whisper_print_usage(argc, argv, params); + exit(0); + } + } + + return true; +} + +void whisper_print_usage(int /*argc*/, char ** argv, const whisper_params & params) { + fprintf(stderr, "\n"); + fprintf(stderr, "usage: %s [options]\n", argv[0]); + fprintf(stderr, "\n"); + fprintf(stderr, "options:\n"); + fprintf(stderr, " -h, --help [default] show this help message and exit\n"); + fprintf(stderr, " -t N, --threads N [%-7d] number of threads to use during computation\n", params.n_threads); + fprintf(stderr, " -pms N, --prompt-ms N [%-7d] prompt duration in milliseconds\n", params.prompt_ms); + fprintf(stderr, " -cms N, --command-ms N [%-7d] command duration in milliseconds\n", params.command_ms); + fprintf(stderr, " -c ID, --capture ID [%-7d] capture device ID\n", params.capture_id); + fprintf(stderr, " -mt N, --max-tokens N [%-7d] maximum number of tokens per audio chunk\n", params.max_tokens); + fprintf(stderr, " -ac N, --audio-ctx N [%-7d] audio context size (0 - all)\n", params.audio_ctx); + fprintf(stderr, " -vth N, --vad-thold N [%-7.2f] voice activity detection threshold\n", params.vad_thold); + fprintf(stderr, " -fth N, --freq-thold N [%-7.2f] high-pass frequency cutoff\n", params.freq_thold); + fprintf(stderr, " -tr, --translate [%-7s] translate from source language to english\n", params.translate ? "true" : "false"); + fprintf(stderr, " -ps, --print-special [%-7s] print special tokens\n", params.print_special ? "true" : "false"); + fprintf(stderr, " -pe, --print-energy [%-7s] print sound energy (for debugging)\n", params.print_energy ? "true" : "false"); + fprintf(stderr, " -ng, --no-gpu [%-7s] disable GPU\n", params.use_gpu ? "false" : "true"); + fprintf(stderr, " -fa, --flash-attn [%-7s] enbale flash attention\n", params.flash_attn ? "true" : "false"); + fprintf(stderr, " -nfa, --no-flash-attn [%-7s] disable flash attention\n", params.flash_attn ? "false" : "true"); + fprintf(stderr, " -l LANG, --language LANG [%-7s] spoken language\n", params.language.c_str()); + fprintf(stderr, " -m FNAME, --model FNAME [%-7s] model path\n", params.model.c_str()); + fprintf(stderr, " -f FNAME, --file FNAME [%-7s] text output file name\n", params.fname_out.c_str()); + fprintf(stderr, " -cmd FNAME, --commands FNAME [%-7s] text file with allowed commands\n", params.commands.c_str()); + fprintf(stderr, " -p, --prompt [%-7s] the required activation prompt\n", params.prompt.c_str()); + fprintf(stderr, " -ctx, --context [%-7s] sample text to help the transcription\n", params.context.c_str()); + fprintf(stderr, " --grammar GRAMMAR [%-7s] GBNF grammar to guide decoding\n", params.grammar.c_str()); + fprintf(stderr, " --grammar-penalty N [%-7.1f] scales down logits of nongrammar tokens\n", params.grammar_penalty); + fprintf(stderr, " --suppress-regex REGEX [%-7s] regular expression matching tokens to suppress\n", params.suppress_regex.c_str()); + fprintf(stderr, "\n"); +} + +static std::string transcribe( + whisper_context * ctx, + const whisper_params & params, + const std::vector & pcmf32, + const std::string & grammar_rule, + float & logprob_min, + float & logprob_sum, + int & n_tokens, + int64_t & t_ms) { + const auto t_start = std::chrono::high_resolution_clock::now(); + + logprob_min = 0.0f; + logprob_sum = 0.0f; + n_tokens = 0; + t_ms = 0; + + //whisper_full_params wparams = whisper_full_default_params(WHISPER_SAMPLING_GREEDY); + whisper_full_params wparams = whisper_full_default_params(WHISPER_SAMPLING_BEAM_SEARCH); + + wparams.print_progress = false; + wparams.print_special = params.print_special; + wparams.print_realtime = false; + wparams.print_timestamps = !params.no_timestamps; + wparams.translate = params.translate; + wparams.no_context = true; + wparams.no_timestamps = params.no_timestamps; + wparams.single_segment = true; + wparams.max_tokens = params.max_tokens; + wparams.language = params.language.c_str(); + wparams.n_threads = params.n_threads; + + wparams.audio_ctx = params.audio_ctx; + + wparams.temperature = 0.4f; + wparams.temperature_inc = 1.0f; + wparams.greedy.best_of = 5; + + wparams.beam_search.beam_size = 5; + + wparams.initial_prompt = params.context.data(); + + wparams.suppress_regex = params.suppress_regex.c_str(); + + const auto & grammar_parsed = params.grammar_parsed; + auto grammar_rules = grammar_parsed.c_rules(); + + if (!params.grammar_parsed.rules.empty() && !grammar_rule.empty()) { + if (grammar_parsed.symbol_ids.find(grammar_rule) == grammar_parsed.symbol_ids.end()) { + fprintf(stderr, "%s: warning: grammar rule '%s' not found - skipping grammar sampling\n", __func__, grammar_rule.c_str()); + } else { + wparams.grammar_rules = grammar_rules.data(); + wparams.n_grammar_rules = grammar_rules.size(); + wparams.i_start_rule = grammar_parsed.symbol_ids.at(grammar_rule); + wparams.grammar_penalty = params.grammar_penalty; + } + } + + if (whisper_full(ctx, wparams, pcmf32.data(), pcmf32.size()) != 0) { + return ""; + } + + std::string result; + + const int n_segments = whisper_full_n_segments(ctx); + for (int i = 0; i < n_segments; ++i) { + const char * text = whisper_full_get_segment_text(ctx, i); + + result += text; + + const int n = whisper_full_n_tokens(ctx, i); + for (int j = 0; j < n; ++j) { + const auto token = whisper_full_get_token_data(ctx, i, j); + + if(token.plog > 0.0f) exit(0); + logprob_min = std::min(logprob_min, token.plog); + logprob_sum += token.plog; + ++n_tokens; + } + } + + const auto t_end = std::chrono::high_resolution_clock::now(); + t_ms = std::chrono::duration_cast(t_end - t_start).count(); + + return result; +} + +static std::vector read_allowed_commands(const std::string & fname) { + std::vector allowed_commands; + + std::ifstream ifs(fname); + if (!ifs.is_open()) { + return allowed_commands; + } + + std::string line; + while (std::getline(ifs, line)) { + line = ::trim(line); + if (line.empty()) { + continue; + } + + std::transform(line.begin(), line.end(),line.begin(), ::tolower); + allowed_commands.push_back(std::move(line)); + } + + return allowed_commands; +} + +static std::vector get_words(const std::string &txt) { + std::vector words; + + std::istringstream iss(txt); + std::string word; + while (iss >> word) { + words.push_back(word); + } + + return words; +} + +// command-list mode +// guide the transcription to match the most likely command from a provided list +static int process_command_list(struct whisper_context * ctx, audio_async &audio, const whisper_params ¶ms, std::ofstream &fout) { + fprintf(stderr, "\n"); + fprintf(stderr, "%s: guided mode\n", __func__); + + std::vector allowed_commands = read_allowed_commands(params.commands); + + if (allowed_commands.empty()) { + fprintf(stderr, "%s: error: failed to read allowed commands from '%s'\n", __func__, params.commands.c_str()); + return 2; + } + + int max_len = 0; + + std::vector> allowed_tokens; + + for (const auto & cmd : allowed_commands) { + whisper_token tokens[1024]; + allowed_tokens.emplace_back(); + + for (int l = 0; l < (int) cmd.size(); ++l) { + // NOTE: very important to add the whitespace ! + // the reason is that the first decoded token starts with a whitespace too! + std::string ss = std::string(" ") + cmd.substr(0, l + 1); + + const int n = whisper_tokenize(ctx, ss.c_str(), tokens, 1024); + if (n < 0) { + fprintf(stderr, "%s: error: failed to tokenize command '%s'\n", __func__, cmd.c_str()); + return 3; + } + + if (n == 1) { + allowed_tokens.back().push_back(tokens[0]); + } + } + + max_len = std::max(max_len, (int) cmd.size()); + } + + fprintf(stderr, "%s: allowed commands [ tokens ]:\n", __func__); + fprintf(stderr, "\n"); + for (int i = 0; i < (int) allowed_commands.size(); ++i) { + fprintf(stderr, " - \033[1m%-*s\033[0m = [", max_len, allowed_commands[i].c_str()); + for (const auto & token : allowed_tokens[i]) { + fprintf(stderr, " %5d", token); + } + fprintf(stderr, " ]\n"); + } + + std::string k_prompt = "select one from the available words: "; + for (int i = 0; i < (int) allowed_commands.size(); ++i) { + if (i > 0) { + k_prompt += ", "; + } + k_prompt += allowed_commands[i]; + } + k_prompt += ". selected word: "; + + // tokenize prompt + std::vector k_tokens; + { + k_tokens.resize(1024); + const int n = whisper_tokenize(ctx, k_prompt.c_str(), k_tokens.data(), 1024); + if (n < 0) { + fprintf(stderr, "%s: error: failed to tokenize prompt '%s'\n", __func__, k_prompt.c_str()); + return 4; + } + k_tokens.resize(n); + } + + fprintf(stderr, "\n"); + fprintf(stderr, "%s: prompt: '%s'\n", __func__, k_prompt.c_str()); + fprintf(stderr, "%s: tokens: [", __func__); + for (const auto & token : k_tokens) { + fprintf(stderr, " %d", token); + } + fprintf(stderr, " ]\n"); + + fprintf(stderr, "\n"); + fprintf(stderr, "%s: listening for a command ...\n", __func__); + fprintf(stderr, "\n"); + + bool is_running = true; + + std::vector pcmf32_cur; + std::vector pcmf32_prompt; + + // main loop + while (is_running) { + // handle Ctrl + C + is_running = sdl_poll_events(); + + // delay + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + audio.get(2000, pcmf32_cur); + + if (::vad_simple(pcmf32_cur, WHISPER_SAMPLE_RATE, 1000, params.vad_thold, params.freq_thold, params.print_energy)) { + fprintf(stdout, "%s: Speech detected! Processing ...\n", __func__); + + const auto t_start = std::chrono::high_resolution_clock::now(); + + whisper_full_params wparams = whisper_full_default_params(WHISPER_SAMPLING_GREEDY); + + wparams.print_progress = false; + wparams.print_special = params.print_special; + wparams.print_realtime = false; + wparams.print_timestamps = !params.no_timestamps; + wparams.translate = params.translate; + wparams.no_context = true; + wparams.single_segment = true; + wparams.max_tokens = 1; + wparams.language = params.language.c_str(); + wparams.n_threads = params.n_threads; + + wparams.audio_ctx = params.audio_ctx; + + wparams.prompt_tokens = k_tokens.data(); + wparams.prompt_n_tokens = k_tokens.size(); + + // run the transformer and a single decoding pass + if (whisper_full(ctx, wparams, pcmf32_cur.data(), pcmf32_cur.size()) != 0) { + fprintf(stderr, "%s: ERROR: whisper_full() failed\n", __func__); + break; + } + + // estimate command probability + // NOTE: not optimal + { + const auto * logits = whisper_get_logits(ctx); + + std::vector probs(whisper_n_vocab(ctx), 0.0f); + + // compute probs from logits via softmax + { + float max = -1e9; + for (int i = 0; i < (int) probs.size(); ++i) { + max = std::max(max, logits[i]); + } + + float sum = 0.0f; + for (int i = 0; i < (int) probs.size(); ++i) { + probs[i] = expf(logits[i] - max); + sum += probs[i]; + } + + for (int i = 0; i < (int) probs.size(); ++i) { + probs[i] /= sum; + } + } + + std::vector> probs_id; + + double psum = 0.0; + for (int i = 0; i < (int) allowed_commands.size(); ++i) { + probs_id.emplace_back(probs[allowed_tokens[i][0]], i); + for (int j = 1; j < (int) allowed_tokens[i].size(); ++j) { + probs_id.back().first += probs[allowed_tokens[i][j]]; + } + probs_id.back().first /= allowed_tokens[i].size(); + psum += probs_id.back().first; + } + + // normalize + for (auto & p : probs_id) { + p.first /= psum; + } + + // sort descending + { + using pair_type = decltype(probs_id)::value_type; + std::sort(probs_id.begin(), probs_id.end(), [](const pair_type & a, const pair_type & b) { + return a.first > b.first; + }); + } + + // print the commands and the respective probabilities + { + fprintf(stdout, "\n"); + for (const auto & cmd : probs_id) { + fprintf(stdout, "%s: %s%-*s%s = %f | ", __func__, "\033[1m", max_len, allowed_commands[cmd.second].c_str(), "\033[0m", cmd.first); + for (int token : allowed_tokens[cmd.second]) { + fprintf(stdout, "'%4s' %f ", whisper_token_to_str(ctx, token), probs[token]); + } + fprintf(stdout, "\n"); + } + } + + // best command + { + const auto t_end = std::chrono::high_resolution_clock::now(); + + const float prob = probs_id[0].first; + const int index = probs_id[0].second; + const char * best_command = allowed_commands[index].c_str(); + + fprintf(stdout, "\n"); + fprintf(stdout, "%s: detected command: %s%s%s | p = %f | t = %d ms\n", __func__, + "\033[1m", best_command, "\033[0m", prob, + (int) std::chrono::duration_cast(t_end - t_start).count()); + fprintf(stdout, "\n"); + if (fout.is_open()) { + fout << best_command << std::endl; + } + } + } + + audio.clear(); + } + } + + return 0; +} + +// always-prompt mode +// transcribe the voice into text after valid prompt +static int always_prompt_transcription(struct whisper_context * ctx, audio_async & audio, const whisper_params & params, std::ofstream & fout) { + bool is_running = true; + bool ask_prompt = true; + + float logprob_min = 0.0f; + float logprob_sum = 0.0f; + int n_tokens = 0; + + std::vector pcmf32_cur; + + const std::string k_prompt = params.prompt; + + const int k_prompt_length = get_words(k_prompt).size(); + + fprintf(stderr, "\n"); + fprintf(stderr, "%s: always-prompt mode\n", __func__); + + // main loop + while (is_running) { + // handle Ctrl + C + is_running = sdl_poll_events(); + + // delay + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + if (ask_prompt) { + fprintf(stdout, "\n"); + fprintf(stdout, "%s: The prompt is: '%s%s%s'\n", __func__, "\033[1m", k_prompt.c_str(), "\033[0m"); + fprintf(stdout, "\n"); + + ask_prompt = false; + } + + { + audio.get(2000, pcmf32_cur); + + if (::vad_simple(pcmf32_cur, WHISPER_SAMPLE_RATE, 1000, params.vad_thold, params.freq_thold, params.print_energy)) { + fprintf(stdout, "%s: Speech detected! Processing ...\n", __func__); + + int64_t t_ms = 0; + + // detect the commands + audio.get(params.command_ms, pcmf32_cur); + + const auto txt = ::trim(::transcribe(ctx, params, pcmf32_cur, "", logprob_min, logprob_sum, n_tokens, t_ms)); + + const auto words = get_words(txt); + + std::string prompt; + std::string command; + + for (int i = 0; i < (int) words.size(); ++i) { + if (i < k_prompt_length) { + prompt += words[i] + " "; + } else { + command += words[i] + " "; + } + } + + const float sim = similarity(prompt, k_prompt); + + //debug + //fprintf(stdout, "command size: %i\n", command_length); + + if ((sim > 0.7f) && (command.size() > 0)) { + fprintf(stdout, "%s: Command '%s%s%s', (t = %d ms)\n", __func__, "\033[1m", command.c_str(), "\033[0m", (int) t_ms); + if (fout.is_open()) { + fout << command << std::endl; + } + } + + fprintf(stdout, "\n"); + + audio.clear(); + } + } + } + + return 0; +} + +// general-purpose mode +// freely transcribe the voice into text +static int process_general_transcription(struct whisper_context * ctx, audio_async & audio, const whisper_params & params, std::ofstream & fout) { + bool is_running = true; + bool have_prompt = false; + bool ask_prompt = true; + + float logprob_min0 = 0.0f; + float logprob_min = 0.0f; + + float logprob_sum0 = 0.0f; + float logprob_sum = 0.0f; + + int n_tokens0 = 0; + int n_tokens = 0; + + std::vector pcmf32_cur; + std::vector pcmf32_prompt; + + std::string k_prompt = "Ok Whisper, start listening for commands."; + if (!params.prompt.empty()) { + k_prompt = params.prompt; + } + + fprintf(stderr, "\n"); + fprintf(stderr, "%s: general-purpose mode\n", __func__); + + // main loop + while (is_running) { + // handle Ctrl + C + is_running = sdl_poll_events(); + + // delay + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + if (ask_prompt) { + fprintf(stdout, "\n"); + fprintf(stdout, "%s: Say the following phrase: '%s%s%s'\n", __func__, "\033[1m", k_prompt.c_str(), "\033[0m"); + fprintf(stdout, "\n"); + + ask_prompt = false; + } + + { + audio.get(2000, pcmf32_cur); + + if (::vad_simple(pcmf32_cur, WHISPER_SAMPLE_RATE, 1000, params.vad_thold, params.freq_thold, params.print_energy)) { + fprintf(stdout, "%s: Speech detected! Processing ...\n", __func__); + + int64_t t_ms = 0; + + if (!have_prompt) { + // wait for activation phrase + audio.get(params.prompt_ms, pcmf32_cur); + + const auto txt = ::trim(::transcribe(ctx, params, pcmf32_cur, "prompt", logprob_min0, logprob_sum0, n_tokens0, t_ms)); + + const float p = 100.0f * std::exp(logprob_min0); + + fprintf(stdout, "%s: Heard '%s%s%s', (t = %d ms, p = %.2f%%)\n", __func__, "\033[1m", txt.c_str(), "\033[0m", (int) t_ms, p); + + const float sim = similarity(txt, k_prompt); + + if (txt.length() < 0.8*k_prompt.length() || txt.length() > 1.2*k_prompt.length() || sim < 0.8f) { + fprintf(stdout, "%s: WARNING: prompt not recognized, try again\n", __func__); + ask_prompt = true; + } else { + fprintf(stdout, "\n"); + fprintf(stdout, "%s: The prompt has been recognized!\n", __func__); + fprintf(stdout, "%s: Waiting for voice commands ...\n", __func__); + fprintf(stdout, "\n"); + + // save the audio for the prompt + pcmf32_prompt = pcmf32_cur; + have_prompt = true; + } + } else { + // we have heard the activation phrase, now detect the commands + audio.get(params.command_ms, pcmf32_cur); + + //printf("len prompt: %.4f\n", pcmf32_prompt.size() / (float) WHISPER_SAMPLE_RATE); + //printf("len command: %.4f\n", pcmf32_cur.size() / (float) WHISPER_SAMPLE_RATE); + + // prepend 3 second of silence + pcmf32_cur.insert(pcmf32_cur.begin(), 3.0f*WHISPER_SAMPLE_RATE, 0.0f); + + // prepend the prompt audio + pcmf32_cur.insert(pcmf32_cur.begin(), pcmf32_prompt.begin(), pcmf32_prompt.end()); + + const auto txt = ::trim(::transcribe(ctx, params, pcmf32_cur, "root", logprob_min, logprob_sum, n_tokens, t_ms)); + + //const float p = 100.0f * std::exp((logprob - logprob0) / (n_tokens - n_tokens0)); + const float p = 100.0f * std::exp(logprob_min); + + //fprintf(stdout, "%s: heard '%s'\n", __func__, txt.c_str()); + + // find the prompt in the text + float best_sim = 0.0f; + size_t best_len = 0; + for (size_t n = 0.8*k_prompt.size(); n <= 1.2*k_prompt.size(); ++n) { + if (n >= txt.size()) { + break; + } + + const auto prompt = txt.substr(0, n); + + const float sim = similarity(prompt, k_prompt); + + //fprintf(stderr, "%s: prompt = '%s', sim = %f\n", __func__, prompt.c_str(), sim); + + if (sim > best_sim) { + best_sim = sim; + best_len = n; + } + } + + fprintf(stdout, "%s: DEBUG: txt = '%s', prob = %.2f%%\n", __func__, txt.c_str(), p); + if (best_len == 0) { + fprintf(stdout, "%s: WARNING: command not recognized, try again\n", __func__); + } else { + // cut the prompt from the decoded text + const std::string command = ::trim(txt.substr(best_len)); + fprintf(stdout, "%s: Command '%s%s%s', (t = %d ms)\n", __func__, "\033[1m", command.c_str(), "\033[0m", (int) t_ms); + if (fout.is_open()) { + fout << command << std::endl; + } + } + + fprintf(stdout, "\n"); + } + + audio.clear(); + } + } + } + + return 0; +} + +int main(int argc, char ** argv) { + ggml_backend_load_all(); + + whisper_params params; + + if (whisper_params_parse(argc, argv, params) == false) { + return 1; + } + + if (whisper_lang_id(params.language.c_str()) == -1) { + fprintf(stderr, "error: unknown language '%s'\n", params.language.c_str()); + whisper_print_usage(argc, argv, params); + exit(0); + } + + // whisper init + + struct whisper_context_params cparams = whisper_context_default_params(); + + cparams.use_gpu = params.use_gpu; + cparams.flash_attn = params.flash_attn; + + struct whisper_context * ctx = whisper_init_from_file_with_params(params.model.c_str(), cparams); + if (ctx == nullptr) { + fprintf(stderr, "error: failed to initialize whisper context\n"); + return 2; + } + + // print some info about the processing + { + fprintf(stderr, "\n"); + if (!whisper_is_multilingual(ctx)) { + if (params.language != "en" || params.translate) { + params.language = "en"; + params.translate = false; + fprintf(stderr, "%s: WARNING: model is not multilingual, ignoring language and translation options\n", __func__); + } + } + fprintf(stderr, "%s: processing, %d threads, lang = %s, task = %s, timestamps = %d ...\n", + __func__, + params.n_threads, + params.language.c_str(), + params.translate ? "translate" : "transcribe", + params.no_timestamps ? 0 : 1); + + fprintf(stderr, "\n"); + } + + // init audio + + audio_async audio(30*1000); + if (!audio.init(params.capture_id, WHISPER_SAMPLE_RATE)) { + fprintf(stderr, "%s: audio.init() failed!\n", __func__); + return 1; + } + + audio.resume(); + + // wait for 1 second to avoid any buffered noise + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + audio.clear(); + + int ret_val = 0; + + if (!params.grammar.empty()) { + auto & grammar = params.grammar_parsed; + if (is_file_exist(params.grammar.c_str())) { + // read grammar from file + std::ifstream ifs(params.grammar.c_str()); + const std::string txt = std::string((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); + grammar = grammar_parser::parse(txt.c_str()); + } else { + // read grammar from string + grammar = grammar_parser::parse(params.grammar.c_str()); + } + + // will be empty (default) if there are parse errors + if (grammar.rules.empty()) { + ret_val = 1; + } else { + fprintf(stderr, "%s: grammar:\n", __func__); + grammar_parser::print_grammar(stderr, grammar); + fprintf(stderr, "\n"); + } + } + + std::ofstream fout; + if (params.fname_out.length() > 0) { + fout.open(params.fname_out); + if (!fout.is_open()) { + fprintf(stderr, "%s: failed to open output file '%s'!\n", __func__, params.fname_out.c_str()); + return 1; + } + } + + if (ret_val == 0) { + if (!params.commands.empty()) { + ret_val = process_command_list(ctx, audio, params, fout); + } else if (!params.prompt.empty() && params.grammar_parsed.rules.empty()) { + ret_val = always_prompt_transcription(ctx, audio, params, fout); + } else { + ret_val = process_general_transcription(ctx, audio, params, fout); + } + } + + audio.pause(); + + whisper_print_timings(ctx); + whisper_free(ctx); + + return ret_val; +} diff --git a/examples/command/commands.txt b/examples/command/commands.txt new file mode 100644 index 0000000..2653de6 --- /dev/null +++ b/examples/command/commands.txt @@ -0,0 +1,9 @@ +enable +disable +cat +dog +apple +red +blue +green +lightblue diff --git a/examples/common-ggml.cpp b/examples/common-ggml.cpp new file mode 100644 index 0000000..11a5c0a --- /dev/null +++ b/examples/common-ggml.cpp @@ -0,0 +1,246 @@ +#include "common-ggml.h" + +#include +#include + +static const std::map GGML_FTYPE_MAP = { + {"q4_0", GGML_FTYPE_MOSTLY_Q4_0}, + {"q4_1", GGML_FTYPE_MOSTLY_Q4_1}, + {"q5_0", GGML_FTYPE_MOSTLY_Q5_0}, + {"q5_1", GGML_FTYPE_MOSTLY_Q5_1}, + {"q8_0", GGML_FTYPE_MOSTLY_Q8_0}, + {"q2_k", GGML_FTYPE_MOSTLY_Q2_K}, + {"q3_k", GGML_FTYPE_MOSTLY_Q3_K}, + {"q4_k", GGML_FTYPE_MOSTLY_Q4_K}, + {"q5_k", GGML_FTYPE_MOSTLY_Q5_K}, + {"q6_k", GGML_FTYPE_MOSTLY_Q6_K}, +}; + +void ggml_print_ftypes(FILE * fp) { + for (auto it = GGML_FTYPE_MAP.begin(); it != GGML_FTYPE_MAP.end(); it++) { + fprintf(fp, " type = \"%s\" or %d\n", it->first.c_str(), it->second); + } +} + +enum ggml_ftype ggml_parse_ftype(const char * str) { + enum ggml_ftype ftype; + if (str[0] == 'q') { + const auto it = GGML_FTYPE_MAP.find(str); + if (it == GGML_FTYPE_MAP.end()) { + fprintf(stderr, "%s: unknown ftype '%s'\n", __func__, str); + return GGML_FTYPE_UNKNOWN; + } + ftype = it->second; + } else { + ftype = (enum ggml_ftype) atoi(str); + } + + return ftype; +} + +bool ggml_common_quantize_0( + std::ifstream & finp, + std::ofstream & fout, + const ggml_ftype ftype, + const std::vector & to_quant, + const std::vector & to_skip) { + + ggml_type qtype = GGML_TYPE_F32; + + switch (ftype) { + case GGML_FTYPE_MOSTLY_Q4_0: qtype = GGML_TYPE_Q4_0; break; + case GGML_FTYPE_MOSTLY_Q4_1: qtype = GGML_TYPE_Q4_1; break; + case GGML_FTYPE_MOSTLY_Q5_0: qtype = GGML_TYPE_Q5_0; break; + case GGML_FTYPE_MOSTLY_Q5_1: qtype = GGML_TYPE_Q5_1; break; + case GGML_FTYPE_MOSTLY_Q8_0: qtype = GGML_TYPE_Q8_0; break; + case GGML_FTYPE_MOSTLY_Q2_K: qtype = GGML_TYPE_Q2_K; break; + case GGML_FTYPE_MOSTLY_Q3_K: qtype = GGML_TYPE_Q3_K; break; + case GGML_FTYPE_MOSTLY_Q4_K: qtype = GGML_TYPE_Q4_K; break; + case GGML_FTYPE_MOSTLY_Q5_K: qtype = GGML_TYPE_Q5_K; break; + case GGML_FTYPE_MOSTLY_Q6_K: qtype = GGML_TYPE_Q6_K; break; + case GGML_FTYPE_UNKNOWN: + case GGML_FTYPE_ALL_F32: + case GGML_FTYPE_MOSTLY_F16: + case GGML_FTYPE_MOSTLY_Q4_1_SOME_F16: + case GGML_FTYPE_MOSTLY_IQ2_XXS: + case GGML_FTYPE_MOSTLY_IQ2_XS: + case GGML_FTYPE_MOSTLY_IQ2_S: + case GGML_FTYPE_MOSTLY_IQ3_XXS: + case GGML_FTYPE_MOSTLY_IQ3_S: + case GGML_FTYPE_MOSTLY_IQ1_S: + case GGML_FTYPE_MOSTLY_IQ4_NL: + case GGML_FTYPE_MOSTLY_IQ4_XS: + case GGML_FTYPE_MOSTLY_IQ1_M: + case GGML_FTYPE_MOSTLY_BF16: + case GGML_FTYPE_MOSTLY_MXFP4: + case GGML_FTYPE_MOSTLY_NVFP4: + case GGML_FTYPE_MOSTLY_Q1_0: + case GGML_FTYPE_MOSTLY_Q2_0: + { + fprintf(stderr, "%s: invalid model type %d\n", __func__, ftype); + return false; + } + }; + + if (!ggml_is_quantized(qtype)) { + fprintf(stderr, "%s: invalid quantization type %d (%s)\n", __func__, qtype, ggml_type_name(qtype)); + return false; + } + + size_t total_size_org = 0; + size_t total_size_new = 0; + + std::vector work; + + std::vector data_u8; + std::vector data_f16; + std::vector data_f32; + + while (true) { + int32_t n_dims; + int32_t length; + int32_t ttype; + + finp.read(reinterpret_cast(&n_dims), sizeof(n_dims)); + finp.read(reinterpret_cast(&length), sizeof(length)); + finp.read(reinterpret_cast(&ttype), sizeof(ttype)); + + if (finp.eof()) { + break; + } + + int32_t nelements = 1; + int32_t ne[4] = { 1, 1, 1, 1 }; + for (int i = 0; i < n_dims; ++i) { + finp.read (reinterpret_cast(&ne[i]), sizeof(ne[i])); + nelements *= ne[i]; + } + + std::string name(length, 0); + finp.read (&name[0], length); + + printf("%64s - [%5d, %5d, %5d], type = %6s ", name.data(), ne[0], ne[1], ne[2], ggml_type_name((ggml_type) ttype)); + + bool quantize = false; + + // check if we should quantize this tensor + for (const auto & s : to_quant) { + if (std::regex_match(name, std::regex(s))) { + quantize = true; + break; + } + } + + // check if we should skip this tensor + for (const auto & s : to_skip) { + if (std::regex_match(name, std::regex(s))) { + quantize = false; + break; + } + } + + // quantize only 2D tensors + quantize &= (n_dims == 2); + + if (quantize) { + if (ttype != GGML_TYPE_F32 && ttype != GGML_TYPE_F16) { + fprintf(stderr, "%s: unsupported ttype %d (%s) for integer quantization\n", __func__, ttype, ggml_type_name((ggml_type) ttype)); + return false; + } + + if (ttype == GGML_TYPE_F16) { + data_f16.resize(nelements); + finp.read(reinterpret_cast(data_f16.data()), nelements * sizeof(ggml_fp16_t)); + data_f32.resize(nelements); + for (int i = 0; i < nelements; ++i) { + data_f32[i] = ggml_fp16_to_fp32(data_f16[i]); + } + } else { + data_f32.resize(nelements); + finp.read(reinterpret_cast(data_f32.data()), nelements * sizeof(float)); + } + + ttype = qtype; + } else { + const int bpe = (ttype == 0) ? sizeof(float) : sizeof(uint16_t); + + data_u8.resize(nelements*bpe); + finp.read(reinterpret_cast(data_u8.data()), nelements * bpe); + } + + fout.write(reinterpret_cast(&n_dims), sizeof(n_dims)); + fout.write(reinterpret_cast(&length), sizeof(length)); + fout.write(reinterpret_cast(&ttype), sizeof(ttype)); + for (int i = 0; i < n_dims; ++i) { + fout.write(reinterpret_cast(&ne[i]), sizeof(ne[i])); + } + fout.write(&name[0], length); + + if (quantize) { + work.resize(nelements); // for quantization + + size_t cur_size = 0; + switch ((ggml_type) ttype) { + case GGML_TYPE_Q4_0: + case GGML_TYPE_Q4_1: + case GGML_TYPE_Q5_0: + case GGML_TYPE_Q5_1: + case GGML_TYPE_Q8_0: + case GGML_TYPE_Q2_K: + case GGML_TYPE_Q3_K: + case GGML_TYPE_Q4_K: + case GGML_TYPE_Q5_K: + case GGML_TYPE_Q6_K: + { + cur_size = ggml_quantize_chunk((ggml_type) ttype, data_f32.data(), work.data(), 0, nelements/ne[0], ne[0], nullptr); + } break; + case GGML_TYPE_F32: + case GGML_TYPE_F16: + case GGML_TYPE_I8: + case GGML_TYPE_I16: + case GGML_TYPE_I32: + case GGML_TYPE_I64: + case GGML_TYPE_F64: + case GGML_TYPE_Q8_1: + case GGML_TYPE_Q8_K: + case GGML_TYPE_IQ2_XXS: + case GGML_TYPE_IQ2_XS: + case GGML_TYPE_IQ2_S: + case GGML_TYPE_IQ3_XXS: + case GGML_TYPE_IQ3_S: + case GGML_TYPE_IQ1_S: + case GGML_TYPE_IQ4_NL: + case GGML_TYPE_IQ4_XS: + case GGML_TYPE_IQ1_M: + case GGML_TYPE_BF16: + case GGML_TYPE_TQ1_0: + case GGML_TYPE_TQ2_0: + case GGML_TYPE_MXFP4: + case GGML_TYPE_NVFP4: + case GGML_TYPE_Q1_0: + case GGML_TYPE_Q2_0: + case GGML_TYPE_COUNT: + { + fprintf(stderr, "%s: unsupported quantization type %d (%s)\n", __func__, ttype, ggml_type_name((ggml_type) ttype)); + return false; + } + } + + fout.write(reinterpret_cast(work.data()), cur_size); + total_size_new += cur_size; + + printf("size = %8.2f MB -> %8.2f MB\n", nelements * sizeof(float)/1024.0/1024.0, cur_size/1024.0/1024.0); + } else { + printf("size = %8.3f MB\n", data_u8.size()/1024.0/1024.0); + fout.write(reinterpret_cast(data_u8.data()), data_u8.size()); + total_size_new += data_u8.size(); + } + + total_size_org += nelements * sizeof(float); + } + + printf("%s: model size = %8.2f MB\n", __func__, total_size_org/1024.0/1024.0); + printf("%s: quant size = %8.2f MB | ftype = %d (%s)\n", __func__, total_size_new/1024.0/1024.0, ftype, ggml_type_name(qtype)); + + return true; +} diff --git a/examples/common-ggml.h b/examples/common-ggml.h new file mode 100644 index 0000000..477de34 --- /dev/null +++ b/examples/common-ggml.h @@ -0,0 +1,18 @@ +#pragma once + +#include "ggml.h" + +#include +#include +#include + +enum ggml_ftype ggml_parse_ftype(const char * str); + +void ggml_print_ftypes(FILE * fp = stderr); + +bool ggml_common_quantize_0( + std::ifstream & finp, + std::ofstream & fout, + const ggml_ftype ftype, + const std::vector & to_quant, + const std::vector & to_skip); diff --git a/examples/common-sdl.cpp b/examples/common-sdl.cpp new file mode 100644 index 0000000..c18a5f3 --- /dev/null +++ b/examples/common-sdl.cpp @@ -0,0 +1,227 @@ +#include "common-sdl.h" + +#include + +audio_async::audio_async(int len_ms) { + m_len_ms = len_ms; + + m_running = false; +} + +audio_async::~audio_async() { + if (m_dev_id_in) { + SDL_CloseAudioDevice(m_dev_id_in); + } +} + +bool audio_async::init(int capture_id, int sample_rate) { + SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); + + if (SDL_Init(SDL_INIT_AUDIO) < 0) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError()); + return false; + } + + SDL_SetHintWithPriority(SDL_HINT_AUDIO_RESAMPLING_MODE, "medium", SDL_HINT_OVERRIDE); + + { + int nDevices = SDL_GetNumAudioDevices(SDL_TRUE); + fprintf(stderr, "%s: found %d capture devices:\n", __func__, nDevices); + for (int i = 0; i < nDevices; i++) { + fprintf(stderr, "%s: - Capture device #%d: '%s'\n", __func__, i, SDL_GetAudioDeviceName(i, SDL_TRUE)); + } + } + + SDL_AudioSpec capture_spec_requested; + SDL_AudioSpec capture_spec_obtained; + + SDL_zero(capture_spec_requested); + SDL_zero(capture_spec_obtained); + + capture_spec_requested.freq = sample_rate; + capture_spec_requested.format = AUDIO_F32; + capture_spec_requested.channels = 1; + capture_spec_requested.samples = 1024; + capture_spec_requested.callback = [](void * userdata, uint8_t * stream, int len) { + audio_async * audio = (audio_async *) userdata; + audio->callback(stream, len); + }; + capture_spec_requested.userdata = this; + + if (capture_id >= 0) { + fprintf(stderr, "%s: attempt to open capture device %d : '%s' ...\n", __func__, capture_id, SDL_GetAudioDeviceName(capture_id, SDL_TRUE)); + m_dev_id_in = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(capture_id, SDL_TRUE), SDL_TRUE, &capture_spec_requested, &capture_spec_obtained, 0); + } else { + fprintf(stderr, "%s: attempt to open default capture device ...\n", __func__); + m_dev_id_in = SDL_OpenAudioDevice(nullptr, SDL_TRUE, &capture_spec_requested, &capture_spec_obtained, 0); + } + + if (!m_dev_id_in) { + fprintf(stderr, "%s: couldn't open an audio device for capture: %s!\n", __func__, SDL_GetError()); + m_dev_id_in = 0; + + return false; + } else { + fprintf(stderr, "%s: obtained spec for input device (SDL Id = %d):\n", __func__, m_dev_id_in); + fprintf(stderr, "%s: - sample rate: %d\n", __func__, capture_spec_obtained.freq); + fprintf(stderr, "%s: - format: %d (required: %d)\n", __func__, capture_spec_obtained.format, + capture_spec_requested.format); + fprintf(stderr, "%s: - channels: %d (required: %d)\n", __func__, capture_spec_obtained.channels, + capture_spec_requested.channels); + fprintf(stderr, "%s: - samples per frame: %d\n", __func__, capture_spec_obtained.samples); + } + + m_sample_rate = capture_spec_obtained.freq; + + m_audio.resize((m_sample_rate*m_len_ms)/1000); + + return true; +} + +bool audio_async::resume() { + if (!m_dev_id_in) { + fprintf(stderr, "%s: no audio device to resume!\n", __func__); + return false; + } + + if (m_running) { + fprintf(stderr, "%s: already running!\n", __func__); + return false; + } + + SDL_PauseAudioDevice(m_dev_id_in, 0); + + m_running = true; + + return true; +} + +bool audio_async::pause() { + if (!m_dev_id_in) { + fprintf(stderr, "%s: no audio device to pause!\n", __func__); + return false; + } + + if (!m_running) { + fprintf(stderr, "%s: already paused!\n", __func__); + return false; + } + + SDL_PauseAudioDevice(m_dev_id_in, 1); + + m_running = false; + + return true; +} + +bool audio_async::clear() { + if (!m_dev_id_in) { + fprintf(stderr, "%s: no audio device to clear!\n", __func__); + return false; + } + + if (!m_running) { + fprintf(stderr, "%s: not running!\n", __func__); + return false; + } + + { + std::lock_guard lock(m_mutex); + + m_audio_pos = 0; + m_audio_len = 0; + } + + return true; +} + +// callback to be called by SDL +void audio_async::callback(uint8_t * stream, int len) { + if (!m_running) { + return; + } + + size_t n_samples = len / sizeof(float); + + if (n_samples > m_audio.size()) { + n_samples = m_audio.size(); + + stream += (len - (n_samples * sizeof(float))); + } + + //fprintf(stderr, "%s: %zu samples, pos %zu, len %zu\n", __func__, n_samples, m_audio_pos, m_audio_len); + + { + std::lock_guard lock(m_mutex); + + if (m_audio_pos + n_samples > m_audio.size()) { + const size_t n0 = m_audio.size() - m_audio_pos; + + memcpy(&m_audio[m_audio_pos], stream, n0 * sizeof(float)); + memcpy(&m_audio[0], stream + n0 * sizeof(float), (n_samples - n0) * sizeof(float)); + } else { + memcpy(&m_audio[m_audio_pos], stream, n_samples * sizeof(float)); + } + m_audio_pos = (m_audio_pos + n_samples) % m_audio.size(); + m_audio_len = std::min(m_audio_len + n_samples, m_audio.size()); + } +} + +void audio_async::get(int ms, std::vector & result) { + if (!m_dev_id_in) { + fprintf(stderr, "%s: no audio device to get audio from!\n", __func__); + return; + } + + if (!m_running) { + fprintf(stderr, "%s: not running!\n", __func__); + return; + } + + result.clear(); + + { + std::lock_guard lock(m_mutex); + + if (ms <= 0) { + ms = m_len_ms; + } + + size_t n_samples = (m_sample_rate * ms) / 1000; + if (n_samples > m_audio_len) { + n_samples = m_audio_len; + } + + result.resize(n_samples); + + int s0 = m_audio_pos - n_samples; + if (s0 < 0) { + s0 += m_audio.size(); + } + + if (s0 + n_samples > m_audio.size()) { + const size_t n0 = m_audio.size() - s0; + + memcpy(result.data(), &m_audio[s0], n0 * sizeof(float)); + memcpy(&result[n0], &m_audio[0], (n_samples - n0) * sizeof(float)); + } else { + memcpy(result.data(), &m_audio[s0], n_samples * sizeof(float)); + } + } +} + +bool sdl_poll_events() { + SDL_Event event; + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_QUIT: + { + return false; + } + default: + break; + } + } + + return true; +} diff --git a/examples/common-sdl.h b/examples/common-sdl.h new file mode 100644 index 0000000..9ee8a32 --- /dev/null +++ b/examples/common-sdl.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include + +// +// SDL Audio capture +// + +class audio_async { +public: + audio_async(int len_ms); + ~audio_async(); + + bool init(int capture_id, int sample_rate); + + // start capturing audio via the provided SDL callback + // keep last len_ms seconds of audio in a circular buffer + bool resume(); + bool pause(); + bool clear(); + + // callback to be called by SDL + void callback(uint8_t * stream, int len); + + // get audio data from the circular buffer + void get(int ms, std::vector & audio); + +private: + SDL_AudioDeviceID m_dev_id_in = 0; + + int m_len_ms = 0; + int m_sample_rate = 0; + + std::atomic_bool m_running; + std::mutex m_mutex; + + std::vector m_audio; + size_t m_audio_pos = 0; + size_t m_audio_len = 0; +}; + +// Return false if need to quit +bool sdl_poll_events(); diff --git a/examples/common-whisper.cpp b/examples/common-whisper.cpp new file mode 100644 index 0000000..b12481c --- /dev/null +++ b/examples/common-whisper.cpp @@ -0,0 +1,247 @@ +#define _USE_MATH_DEFINES // for M_PI + +#include "common-whisper.h" + +#include "common.h" + +#include "whisper.h" + +// third-party utilities +// use your favorite implementations +#define STB_VORBIS_HEADER_ONLY +#include "stb_vorbis.c" /* Enables Vorbis decoding. */ + +#ifdef _WIN32 +#ifndef NOMINMAX + #define NOMINMAX +#endif +#endif + +#define MA_NO_DEVICE_IO +#define MA_NO_THREADING +#define MA_NO_ENCODING +#define MA_NO_GENERATION +#define MA_NO_RESOURCE_MANAGER +#define MA_NO_NODE_GRAPH +#define MINIAUDIO_IMPLEMENTATION +#include "miniaudio.h" + +#ifdef _WIN32 +#include +#include +#endif + +#include +#include + +#ifdef WHISPER_COMMON_FFMPEG +// as implemented in ffmpeg-trancode.cpp only embedded in common lib if whisper built with ffmpeg support +extern bool ffmpeg_decode_audio(const std::string & ifname, std::vector & wav_data, int out_sample_rate = WHISPER_SAMPLE_RATE); +#endif + +// extract f32 PCM frames from an initialized decoder, downmix to mono and keep the stereo split +static bool read_audio_from_decoder(ma_decoder & decoder, std::vector & pcmf32, std::vector> & pcmf32s, bool stereo) { + ma_result result; + ma_uint64 frame_count; + ma_uint64 frames_read; + + if ((result = ma_decoder_get_length_in_pcm_frames(&decoder, &frame_count)) != MA_SUCCESS) { + fprintf(stderr, "error: failed to retrieve the length of the audio data (%s)\n", ma_result_description(result)); + return false; + } + + pcmf32.resize(stereo ? frame_count*2 : frame_count); + + if ((result = ma_decoder_read_pcm_frames(&decoder, pcmf32.data(), frame_count, &frames_read)) != MA_SUCCESS) { + fprintf(stderr, "error: failed to read the frames of the audio data (%s)\n", ma_result_description(result)); + return false; + } + + if (stereo) { + std::vector stereo_data = pcmf32; + pcmf32.resize(frame_count); + for (uint64_t i = 0; i < frame_count; i++) { + pcmf32[i] = (stereo_data[2*i] + stereo_data[2*i + 1]); + } + pcmf32s.resize(2); + pcmf32s[0].resize(frame_count); + pcmf32s[1].resize(frame_count); + for (uint64_t i = 0; i < frame_count; i++) { + pcmf32s[0][i] = stereo_data[2*i]; + pcmf32s[1][i] = stereo_data[2*i + 1]; + } + } + + return true; +} + +bool read_audio_data(const std::string & fname, std::vector & pcmf32, std::vector> & pcmf32s, bool stereo) { + std::vector audio_data; // used for pipe input from stdin or ffmpeg decoding output + + ma_result result; + ma_decoder_config decoder_config; + + struct decoder_guard { + ma_decoder decoder; + bool initialized = false; + ma_decoder * operator&() { return &decoder; } + ~decoder_guard() { + if (initialized) { + ma_decoder_uninit(&decoder); + } + } + }; + decoder_guard decoder{}; + + decoder_config = ma_decoder_config_init(ma_format_f32, stereo ? 2 : 1, WHISPER_SAMPLE_RATE); + + if (fname == "-") { +#ifdef _WIN32 + _setmode(_fileno(stdin), _O_BINARY); +#endif + + uint8_t buf[1024]; + while (true) + { + const size_t n = fread(buf, 1, sizeof(buf), stdin); + if (n == 0) { + break; + } + audio_data.insert(audio_data.end(), buf, buf + n); + } + + result = ma_decoder_init_memory(audio_data.data(), audio_data.size(), &decoder_config, &decoder); + if (result != MA_SUCCESS) { + fprintf(stderr, "%s: failed to open audio data from stdin (%s)\n", __func__, ma_result_description(result)); + return false; + } + decoder.initialized = true; + + fprintf(stderr, "%s: read %zu bytes from stdin\n", __func__, audio_data.size()); + } else { + fprintf(stderr, "%s: reading audio data from '%s' ...\n", __func__, fname.c_str()); + + // first try miniaudio. if it fails (or skipped) - try ffmpeg + { + const char * skip = getenv("WHISPER_COMMON_MINIAUDIO_SKIP"); + if (!skip || strlen(skip) == 0 || strcmp(skip, "0") == 0) { + fprintf(stderr, "%s: trying to decode with miniaudio\n", __func__); + + result = ma_decoder_init_file(fname.c_str(), &decoder_config, &decoder); + if (result == MA_SUCCESS) { + decoder.initialized = true; + } + } else { + fprintf(stderr, "%s: skipping miniaudio\n", __func__); + } + } + +#if defined(WHISPER_COMMON_FFMPEG) + if (!decoder.initialized) { + fprintf(stderr, "%s: trying to decode with ffmpeg\n", __func__); + + if (ffmpeg_decode_audio(fname, audio_data) != 0) { + fprintf(stderr, "%s: failed to ffmpeg decode\n", __func__); + return false; + } + result = ma_decoder_init_memory(audio_data.data(), audio_data.size(), &decoder_config, &decoder); + if (result != MA_SUCCESS) { + fprintf(stderr, "%s: failed to read audio data as wav (%s)\n", __func__, ma_result_description(result)); + return false; + } + decoder.initialized = true; + } +#endif + + if (!decoder.initialized) { + fprintf(stderr, "%s: failed to read audio data\n", __func__); + return false; + } + } + + return read_audio_from_decoder(decoder.decoder, pcmf32, pcmf32s, stereo); +} + +// decode audio bytes already held in memory +bool read_audio_data(const char * buffer, size_t buffer_size, std::vector & pcmf32, std::vector> & pcmf32s, bool stereo) { + ma_decoder_config decoder_config = ma_decoder_config_init(ma_format_f32, stereo ? 2 : 1, WHISPER_SAMPLE_RATE); + ma_decoder decoder; + + if (ma_decoder_init_memory(buffer, buffer_size, &decoder_config, &decoder) != MA_SUCCESS) { + fprintf(stderr, "error: failed to decode audio data from memory buffer\n"); + return false; + } + + bool ok = read_audio_from_decoder(decoder, pcmf32, pcmf32s, stereo); + ma_decoder_uninit(&decoder); + return ok; +} + +// 500 -> 00:05.000 +// 6000 -> 01:00.000 +std::string to_timestamp(int64_t t, bool comma) { + int64_t msec = t * 10; + int64_t hr = msec / (1000 * 60 * 60); + msec = msec - hr * (1000 * 60 * 60); + int64_t min = msec / (1000 * 60); + msec = msec - min * (1000 * 60); + int64_t sec = msec / 1000; + msec = msec - sec * 1000; + + char buf[32]; + snprintf(buf, sizeof(buf), "%02d:%02d:%02d%s%03d", (int) hr, (int) min, (int) sec, comma ? "," : ".", (int) msec); + + return std::string(buf); +} + +int timestamp_to_sample(int64_t t, int n_samples, int whisper_sample_rate) { + return std::max(0, std::min((int) n_samples - 1, (int) ((t*whisper_sample_rate)/100))); +} + +int utf8_trailing_bytes_needed(const std::string & s) { + const int n = (int) s.size(); + int i = n - 1; + while (i >= 0 && ((unsigned char) s[i] & 0xC0) == 0x80) { + --i; + } + if (i < 0) { + return 0; + } + + const unsigned char c = (unsigned char) s[i]; + int expected; + if ((c & 0x80) == 0x00) { + expected = 1; + } else if ((c & 0xE0) == 0xC0) { + expected = 2; + } else if ((c & 0xF0) == 0xE0) { + expected = 3; + } else if ((c & 0xF8) == 0xF0) { + expected = 4; + } else { + return 0; + } + + const int have = n - i; + return have >= expected ? 0 : (expected - have); +} + +bool speak_with_file(const std::string & command, const std::string & text, const std::string & path, int voice_id) { + std::ofstream speak_file(path.c_str()); + if (speak_file.fail()) { + fprintf(stderr, "%s: failed to open speak_file\n", __func__); + return false; + } else { + speak_file.write(text.c_str(), text.size()); + speak_file.close(); + int ret = system((command + " " + std::to_string(voice_id) + " " + path).c_str()); + if (ret != 0) { + fprintf(stderr, "%s: failed to speak\n", __func__); + return false; + } + } + return true; +} + +#undef STB_VORBIS_HEADER_ONLY +#include "stb_vorbis.c" diff --git a/examples/common-whisper.h b/examples/common-whisper.h new file mode 100644 index 0000000..aec430d --- /dev/null +++ b/examples/common-whisper.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include + +// Read WAV audio file and store the PCM data into pcmf32 +// fname can be a buffer of WAV data instead of a filename +// The sample rate of the audio must be equal to COMMON_SAMPLE_RATE +// If stereo flag is set and the audio has 2 channels, the pcmf32s will contain 2 channel PCM +bool read_audio_data( + const std::string & fname, + std::vector & pcmf32, + std::vector> & pcmf32s, + bool stereo); + +// decode audio bytes already held in memory (uploaded file, network buffer) +bool read_audio_data( + const char * buffer, + size_t buffer_size, + std::vector & pcmf32, + std::vector> & pcmf32s, + bool stereo); + +// convert timestamp to string, 6000 -> 01:00.000 +std::string to_timestamp(int64_t t, bool comma = false); + +// given a timestamp get the sample +int timestamp_to_sample(int64_t t, int n_samples, int whisper_sample_rate); + +// Returns the number of trailing bytes still needed for s to end on a complete UTF-8 codepoint. +int utf8_trailing_bytes_needed(const std::string & s); + +// write text to file, and call system("command voice_id file") +bool speak_with_file(const std::string & command, const std::string & text, const std::string & path, int voice_id); diff --git a/examples/common.cpp b/examples/common.cpp new file mode 100644 index 0000000..8eb633e --- /dev/null +++ b/examples/common.cpp @@ -0,0 +1,675 @@ +#define _USE_MATH_DEFINES // for M_PI + +#include "common.h" + +#include +#include +#include +#include +#include +#include +#include + +// Function to check if the next argument exists +static std::string get_next_arg(int& i, int argc, char** argv, const std::string& flag, gpt_params& params) { + if (i + 1 < argc && argv[i + 1][0] != '-') { + return argv[++i]; + } else { + fprintf(stderr, "error: %s requires one argument.\n", flag.c_str()); + gpt_print_usage(argc, argv, params); + exit(0); + } +} + +bool gpt_params_parse(int argc, char ** argv, gpt_params & params) { + for (int i = 1; i < argc; i++) { + std::string arg = argv[i]; + + if (arg == "-s" || arg == "--seed") { + params.seed = std::stoi(get_next_arg(i, argc, argv, arg, params)); + } else if (arg == "-t" || arg == "--threads") { + params.n_threads = std::stoi(get_next_arg(i, argc, argv, arg, params)); + } else if (arg == "-p" || arg == "--prompt") { + params.prompt = get_next_arg(i, argc, argv, arg, params); + } else if (arg == "-n" || arg == "--n_predict") { + params.n_predict = std::stoi(get_next_arg(i, argc, argv, arg, params)); + } else if (arg == "-np" || arg == "--n_parallel") { + params.n_parallel = std::stoi(get_next_arg(i, argc, argv, arg, params)); + } else if (arg == "--top_k") { + params.top_k = std::stoi(get_next_arg(i, argc, argv, arg, params)); + } else if (arg == "--top_p") { + params.top_p = std::stof(get_next_arg(i, argc, argv, arg, params)); + } else if (arg == "--temp") { + params.temp = std::stof(get_next_arg(i, argc, argv, arg, params)); + } else if (arg == "--repeat-last-n") { + params.repeat_last_n = std::stoi(get_next_arg(i, argc, argv, arg, params)); + } else if (arg == "--repeat-penalty") { + params.repeat_penalty = std::stof(get_next_arg(i, argc, argv, arg, params)); + } else if (arg == "-b" || arg == "--batch_size") { + params.n_batch= std::stoi(get_next_arg(i, argc, argv, arg, params)); + } else if (arg == "-c" || arg == "--context") { + params.n_ctx= std::stoi(get_next_arg(i, argc, argv, arg, params)); + } else if (arg == "-ngl" || arg == "--gpu-layers" || arg == "--n-gpu-layers") { + params.n_gpu_layers = std::stoi(get_next_arg(i, argc, argv, arg, params)); + } else if (arg == "--ignore-eos") { + params.ignore_eos = true; + } else if (arg == "-m" || arg == "--model") { + params.model = get_next_arg(i, argc, argv, arg, params); + } else if (arg == "-i" || arg == "--interactive") { + params.interactive = true; + } else if (arg == "-ip" || arg == "--interactive-port") { + params.interactive = true; + params.interactive_port = std::stoi(get_next_arg(i, argc, argv, arg, params)); + } else if (arg == "-h" || arg == "--help") { + gpt_print_usage(argc, argv, params); + exit(0); + } else if (arg == "-f" || arg == "--file") { + get_next_arg(i, argc, argv, arg, params); + std::ifstream file(argv[i]); + if (!file) { + fprintf(stderr, "error: failed to open file '%s'\n", argv[i]); + break; + } + std::copy(std::istreambuf_iterator(file), std::istreambuf_iterator(), back_inserter(params.prompt)); + if (params.prompt.back() == '\n') { + params.prompt.pop_back(); + } + } else if (arg == "-tt" || arg == "--token_test") { + params.token_test = get_next_arg(i, argc, argv, arg, params); + } + else { + fprintf(stderr, "error: unknown argument: %s\n", arg.c_str()); + gpt_print_usage(argc, argv, params); + exit(0); + } + } + + return true; +} + +void gpt_print_usage(int /*argc*/, char ** argv, const gpt_params & params) { + fprintf(stderr, "usage: %s [options]\n", argv[0]); + fprintf(stderr, "\n"); + fprintf(stderr, "options:\n"); + fprintf(stderr, " -h, --help show this help message and exit\n"); + fprintf(stderr, " -s SEED, --seed SEED RNG seed (default: -1)\n"); + fprintf(stderr, " -t N, --threads N number of threads to use during computation (default: %d)\n", params.n_threads); + fprintf(stderr, " -p PROMPT, --prompt PROMPT\n"); + fprintf(stderr, " prompt to start generation with (default: random)\n"); + fprintf(stderr, " -f FNAME, --file FNAME\n"); + fprintf(stderr, " load prompt from a file\n"); + fprintf(stderr, " -tt TOKEN_TEST, --token_test TOKEN_TEST\n"); + fprintf(stderr, " test tokenization\n"); + fprintf(stderr, " -n N, --n_predict N number of tokens to predict (default: %d)\n", params.n_predict); + fprintf(stderr, " --top_k N top-k sampling (default: %d)\n", params.top_k); + fprintf(stderr, " --top_p N top-p sampling (default: %.1f)\n", params.top_p); + fprintf(stderr, " --temp N temperature (default: %.1f)\n", params.temp); + fprintf(stderr, " --repeat-last-n N last n tokens to consider for penalize (default: %d, 0 = disabled)\n", params.repeat_last_n); + fprintf(stderr, " --repeat-penalty N penalize repeat sequence of tokens (default: %.2f, 1.0 = disabled)\n", (double)params.repeat_penalty); + fprintf(stderr, " -b N, --batch_size N batch size for prompt processing (default: %d)\n", params.n_batch); + fprintf(stderr, " -c N, --context N context / KV cache size (default: %d)\n", params.n_ctx); + fprintf(stderr, " --ignore-eos ignore EOS token during generation\n"); + fprintf(stderr, " -ngl N, --gpu-layers N number of layers to offload to GPU on supported models (default: %d)\n", params.n_gpu_layers); + fprintf(stderr, " -m FNAME, --model FNAME\n"); + fprintf(stderr, " model path (default: %s)\n", params.model.c_str()); + fprintf(stderr, "\n"); +} + +std::string gpt_random_prompt(std::mt19937 & rng) { + const int r = rng() % 10; + switch (r) { + case 0: return "So"; + case 1: return "Once upon a time"; + case 2: return "When"; + case 3: return "The"; + case 4: return "After"; + case 5: return "If"; + case 6: return "import"; + case 7: return "He"; + case 8: return "She"; + case 9: return "They"; + } + + return "The"; +} + +std::string trim(const std::string & s) { + std::regex e("^\\s+|\\s+$"); + return std::regex_replace(s, e, ""); +} + +std::string replace(const std::string & s, const std::string & from, const std::string & to) { + std::string result = s; + size_t pos = 0; + while ((pos = result.find(from, pos)) != std::string::npos) { + result.replace(pos, from.length(), to); + pos += to.length(); + } + return result; +} + +void gpt_vocab::add_special_token(const std::string & token) { + special_tokens.push_back(token); +} + +std::map json_parse(const std::string & fname) { + std::map result; + + // read file into string + std::string json; + { + std::ifstream ifs(fname); + if (!ifs) { + fprintf(stderr, "Failed to open %s\n", fname.c_str()); + exit(1); + } + + json = std::string((std::istreambuf_iterator(ifs)), + (std::istreambuf_iterator())); + } + + if (json[0] != '{') { + return result; + } + + // parse json + { + bool has_key = false; + bool in_token = false; + + std::string str_key = ""; + std::string str_val = ""; + + int n = json.size(); + for (int i = 1; i < n; ++i) { + if (!in_token) { + if (json[i] == ' ') continue; + if (json[i] == '"') { + in_token = true; + continue; + } + } else { + if (json[i] == '\\' && i+1 < n) { + if (has_key == false) { + str_key += json[i]; + } else { + str_val += json[i]; + } + ++i; + } else if (json[i] == '"') { + if (has_key == false) { + has_key = true; + ++i; + while (json[i] == ' ') ++i; + ++i; // : + while (json[i] == ' ') ++i; + if (json[i] != '\"') { + while (json[i] != ',' && json[i] != '}') { + str_val += json[i++]; + } + has_key = false; + } else { + in_token = true; + continue; + } + } else { + has_key = false; + } + + str_key = ::replace(str_key, "\\u0120", " " ); // \u0120 -> space + str_key = ::replace(str_key, "\\u010a", "\n"); // \u010a -> new line + str_key = ::replace(str_key, "\\\"", "\""); // \\\" -> " + + try { + result[str_key] = std::stoi(str_val); + } catch (...) { + //fprintf(stderr, "%s: ignoring key '%s' with value '%s'\n", fname.c_str(), str_key.c_str(), str_val.c_str()); + + } + str_key = ""; + str_val = ""; + in_token = false; + continue; + } + if (has_key == false) { + str_key += json[i]; + } else { + str_val += json[i]; + } + } + } + } + + return result; +} + +void gpt_split_words(std::string str, std::vector& words) { + const std::string pattern = R"('s|'t|'re|'ve|'m|'ll|'d| ?[[:alpha:]]+| ?[[:digit:]]+| ?[^\s[:alpha:][:digit:]]+|\s+(?!\S)|\s+)"; + const std::regex re(pattern); + std::smatch m; + + while (std::regex_search(str, m, re)) { + for (auto x : m) { + words.push_back(x); + } + str = m.suffix(); + } +} + +std::vector gpt_tokenize(const gpt_vocab & vocab, const std::string & text) { + std::vector words; + + // first split the text into words + { + std::string str = text; + + // Generate the subpattern from the special_tokens vector if it's not empty + if (!vocab.special_tokens.empty()) { + const std::regex escape(R"([\[\\\^\$\.\|\?\*\+\(\)\{\}])"); + std::string special_tokens_subpattern; + for (const auto & token : vocab.special_tokens) { + if (!special_tokens_subpattern.empty()) { + special_tokens_subpattern += "|"; + } + special_tokens_subpattern += std::regex_replace(token, escape, R"(\$&)"); + } + + std::regex re(special_tokens_subpattern); + std::smatch m; + // Split the text by special tokens. + while (std::regex_search(str, m, re)) { + // Split the substrings in-between special tokens into words. + gpt_split_words(m.prefix(), words); + // Add matched special tokens as words. + for (auto x : m) { + words.push_back(x); + } + str = m.suffix(); + } + // Remaining text without special tokens will be handled below. + } + + gpt_split_words(str, words); + } + + // find the longest token that forms each word in words: + std::vector tokens; + for (const auto & word : words) { + for (int i = 0; i < (int) word.size(); ){ + for (int j = word.size() - 1; j >= i; j--){ + auto cand = word.substr(i, j-i+1); + auto it = vocab.token_to_id.find(cand); + if (it != vocab.token_to_id.end()){ // word.substr(i, j-i+1) in vocab + tokens.push_back(it->second); + i = j + 1; + break; + } + else if (j == i){ // word.substr(i, 1) has no matching + fprintf(stderr, "%s: unknown token '%s'\n", __func__, word.substr(i, 1).data()); + i++; + } + } + } + } + + return tokens; +} + +static std::vector parse_tokens_from_string(const std::string& input, char delimiter) { + std::vector output; + std::stringstream ss(input); + std::string token; + + while (std::getline(ss, token, delimiter)) { + output.push_back(std::stoi(token)); + } + + return output; +} + +static std::map> extract_tests_from_file(const std::string & fpath_test){ + if (fpath_test.empty()){ + fprintf(stderr, "%s : No test file found.\n", __func__); + return std::map>(); + } + + std::map> tests; + + auto fin = std::ifstream(fpath_test, std::ios_base::in); + const char * delimeter = " => "; + const char del_tok = ','; + std::string line; + while (std::getline(fin, line)) { + size_t delimiterPos = line.find(delimeter); + if (delimiterPos != std::string::npos) { + std::string text = line.substr(0, delimiterPos); + std::string s_tokens = line.substr(delimiterPos + std::strlen(delimeter)); + tests[text] = parse_tokens_from_string(s_tokens, del_tok); + } + } + return tests; +} + +void test_gpt_tokenizer(gpt_vocab & vocab, const std::string & fpath_test){ + std::map> tests = extract_tests_from_file(fpath_test); + + size_t n_fails = 0; + + for (const auto & test : tests) { + std::vector tokens = gpt_tokenize(vocab, test.first); + + if (tokens != test.second){ + n_fails++; + + // print out failure cases + fprintf(stderr, "%s : failed test: '%s'\n", __func__, test.first.c_str()); + fprintf(stderr, "%s : tokens in hf: ", __func__); + for (const auto & t : test.second) { + fprintf(stderr, "%s(%d), ", vocab.id_to_token[t].c_str(), t); + } + fprintf(stderr, "\n"); + fprintf(stderr, "%s : tokens in ggml: ", __func__); + for (const auto & t : tokens) { + fprintf(stderr, "%s(%d), ", vocab.id_to_token[t].c_str(), t); + } + fprintf(stderr, "\n"); + } + } + + fprintf(stderr, "%s : %zu tests failed out of %zu tests.\n", __func__, n_fails, tests.size()); +} + +bool gpt_vocab_init(const std::string & fname, gpt_vocab & vocab) { + printf("%s: loading vocab from '%s'\n", __func__, fname.c_str()); + + vocab.token_to_id = ::json_parse(fname); + + for (const auto & kv : vocab.token_to_id) { + vocab.id_to_token[kv.second] = kv.first; + } + + printf("%s: vocab size = %d\n", __func__, (int) vocab.token_to_id.size()); + + // print the vocabulary + //for (auto kv : vocab.token_to_id) { + // printf("'%s' -> %d\n", kv.first.data(), kv.second); + //} + + return true; +} + +gpt_vocab::id gpt_sample_top_k_top_p( + const gpt_vocab & vocab, + const float * logits, + int top_k, + double top_p, + double temp, + std::mt19937 & rng) { + int n_logits = vocab.id_to_token.size(); + + std::vector> logits_id; + logits_id.reserve(n_logits); + + { + const double scale = 1.0/temp; + for (int i = 0; i < n_logits; ++i) { + logits_id.push_back(std::make_pair(logits[i]*scale, i)); + } + } + + // find the top K tokens + std::partial_sort( + logits_id.begin(), + logits_id.begin() + top_k, logits_id.end(), + [](const std::pair & a, const std::pair & b) { + return a.first > b.first; + }); + + logits_id.resize(top_k); + + double maxl = -INFINITY; + for (const auto & kv : logits_id) { + maxl = std::max(maxl, kv.first); + } + + // compute probs for the top K tokens + std::vector probs; + probs.reserve(logits_id.size()); + + double sum = 0.0; + for (const auto & kv : logits_id) { + double p = exp(kv.first - maxl); + probs.push_back(p); + sum += p; + } + + // normalize the probs + for (auto & p : probs) { + p /= sum; + } + + if (top_p < 1.0f) { + double cumsum = 0.0f; + for (int i = 0; i < top_k; i++) { + cumsum += probs[i]; + if (cumsum >= top_p) { + top_k = i + 1; + probs.resize(top_k); + logits_id.resize(top_k); + break; + } + } + + cumsum = 1.0/cumsum; + for (int i = 0; i < (int) probs.size(); i++) { + probs[i] *= cumsum; + } + } + + //printf("\n"); + //for (int i = 0; i < (int) probs.size(); i++) { + // printf("%d: '%s' %f\n", i, vocab.id_to_token.at(logits_id[i].second).c_str(), probs[i]); + //} + //exit(0); + + std::discrete_distribution<> dist(probs.begin(), probs.end()); + int idx = dist(rng); + + return logits_id[idx].second; +} + +gpt_vocab::id gpt_sample_top_k_top_p_repeat( + const gpt_vocab & vocab, + const float * logits, + const int32_t * last_n_tokens_data, + size_t last_n_tokens_data_size, + int top_k, + double top_p, + double temp, + int repeat_last_n, + float repeat_penalty, + std::mt19937 & rng) { + + int n_logits = vocab.id_to_token.size(); + + const auto * plogits = logits; + + const auto last_n_tokens = std::vector(last_n_tokens_data, last_n_tokens_data + last_n_tokens_data_size); + + if (temp <= 0) { + // select the token with the highest logit directly + float max_logit = plogits[0]; + gpt_vocab::id max_id = 0; + + for (int i = 1; i < n_logits; ++i) { + if (plogits[i] > max_logit) { + max_logit = plogits[i]; + max_id = i; + } + } + return max_id; + } + + + std::vector> logits_id; + logits_id.reserve(n_logits); + + { + const float scale = 1.0f/temp; + for (int i = 0; i < n_logits; ++i) { + // repetition penalty from ctrl paper (https://arxiv.org/abs/1909.05858) + // credit https://github.com/facebookresearch/llama/compare/main...shawwn:llama:main + if (repeat_last_n > 0 && std::find(last_n_tokens.end()-repeat_last_n, last_n_tokens.end(), i) != last_n_tokens.end()) { + // if score < 0 then repetition penalty has to multiplied to reduce the previous token probability + if (plogits[i] < 0.0f) { + logits_id.push_back(std::make_pair(plogits[i]*scale*repeat_penalty, i)); + } else { + logits_id.push_back(std::make_pair(plogits[i]*scale/repeat_penalty, i)); + } + } else { + logits_id.push_back(std::make_pair(plogits[i]*scale, i)); + } + } + } + + // find the top K tokens + std::partial_sort( + logits_id.begin(), + logits_id.begin() + top_k, logits_id.end(), + [](const std::pair & a, const std::pair & b) { + return a.first > b.first; + }); + + logits_id.resize(top_k); + + double maxl = -INFINITY; + for (const auto & kv : logits_id) { + maxl = std::max(maxl, kv.first); + } + + // compute probs for the top K tokens + std::vector probs; + probs.reserve(logits_id.size()); + + double sum = 0.0; + for (const auto & kv : logits_id) { + double p = exp(kv.first - maxl); + probs.push_back(p); + sum += p; + } + + // normalize the probs + for (auto & p : probs) { + p /= sum; + } + + if (top_p < 1.0f) { + double cumsum = 0.0f; + for (int i = 0; i < top_k; i++) { + cumsum += probs[i]; + if (cumsum >= top_p) { + top_k = i + 1; + probs.resize(top_k); + logits_id.resize(top_k); + break; + } + } + + cumsum = 1.0/cumsum; + for (int i = 0; i < (int) probs.size(); i++) { + probs[i] *= cumsum; + } + } + +// printf("\n"); +// for (int i = 0; i < (int) probs.size(); i++) { +// for (int i = 0; i < 10; i++) { +// printf("%d: '%s' %f\n", i, vocab.id_to_token.at(logits_id[i].second).c_str(), probs[i]); +// } + + std::discrete_distribution<> dist(probs.begin(), probs.end()); + int idx = dist(rng); + + return logits_id[idx].second; + +} + +void high_pass_filter(std::vector & data, float cutoff, float sample_rate) { + const float rc = 1.0f / (2.0f * M_PI * cutoff); + const float dt = 1.0f / sample_rate; + const float alpha = dt / (rc + dt); + + float y = data[0]; + + for (size_t i = 1; i < data.size(); i++) { + y = alpha * (y + data[i] - data[i - 1]); + data[i] = y; + } +} + +bool vad_simple(std::vector & pcmf32, int sample_rate, int last_ms, float vad_thold, float freq_thold, bool verbose) { + const int n_samples = pcmf32.size(); + const int n_samples_last = (sample_rate * last_ms) / 1000; + + if (n_samples_last >= n_samples) { + // not enough samples - assume no speech + return false; + } + + if (freq_thold > 0.0f) { + high_pass_filter(pcmf32, freq_thold, sample_rate); + } + + float energy_all = 0.0f; + float energy_last = 0.0f; + + for (int i = 0; i < n_samples; i++) { + energy_all += fabsf(pcmf32[i]); + + if (i >= n_samples - n_samples_last) { + energy_last += fabsf(pcmf32[i]); + } + } + + energy_all /= n_samples; + energy_last /= n_samples_last; + + if (verbose) { + fprintf(stderr, "%s: energy_all: %f, energy_last: %f, vad_thold: %f, freq_thold: %f\n", __func__, energy_all, energy_last, vad_thold, freq_thold); + } + + if (energy_last > vad_thold*energy_all) { + return false; + } + + return true; +} + +float similarity(const std::string & s0, const std::string & s1) { + const size_t len0 = s0.size() + 1; + const size_t len1 = s1.size() + 1; + + std::vector col(len1, 0); + std::vector prevCol(len1, 0); + + for (size_t i = 0; i < len1; i++) { + prevCol[i] = i; + } + + for (size_t i = 0; i < len0; i++) { + col[0] = i; + for (size_t j = 1; j < len1; j++) { + col[j] = std::min(std::min(1 + col[j - 1], 1 + prevCol[j]), prevCol[j - 1] + (i > 0 && s0[i - 1] == s1[j - 1] ? 0 : 1)); + } + col.swap(prevCol); + } + + const float dist = prevCol[len1 - 1]; + + return 1.0f - (dist / std::max(s0.size(), s1.size())); +} + +bool is_file_exist(const char * filename) { + std::ifstream infile(filename); + return infile.good(); +} diff --git a/examples/common.h b/examples/common.h new file mode 100644 index 0000000..8f99df7 --- /dev/null +++ b/examples/common.h @@ -0,0 +1,322 @@ +// Various helper functions and utilities + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +// +// GPT CLI argument parsing +// + +struct gpt_params { + int32_t seed = -1; // RNG seed + int32_t n_threads = std::min(4, (int32_t) std::thread::hardware_concurrency()); + int32_t n_predict = 200; // new tokens to predict + int32_t n_parallel = 1; // number of parallel streams + int32_t n_batch = 32; // batch size for prompt processing + int32_t n_ctx = 2048; // context size (this is the KV cache max size) + int32_t n_gpu_layers = 0; // number of layers to offlload to the GPU + + bool ignore_eos = false; // ignore EOS token when generating text + + // sampling parameters + int32_t top_k = 40; + float top_p = 0.9f; + float temp = 0.9f; + int32_t repeat_last_n = 64; + float repeat_penalty = 1.00f; + + std::string model = "models/gpt-2-117M/ggml-model.bin"; // model path + std::string prompt = ""; + std::string token_test = ""; + + bool interactive = false; + int32_t interactive_port = -1; +}; + +bool gpt_params_parse(int argc, char ** argv, gpt_params & params); + +void gpt_print_usage(int argc, char ** argv, const gpt_params & params); + +std::string gpt_random_prompt(std::mt19937 & rng); + +// +// Vocab utils +// + +std::string trim(const std::string & s); + +std::string replace( + const std::string & s, + const std::string & from, + const std::string & to); + +struct gpt_vocab { + using id = int32_t; + using token = std::string; + + std::map token_to_id; + std::map id_to_token; + std::vector special_tokens; + + void add_special_token(const std::string & token); +}; + +// poor-man's JSON parsing +std::map json_parse(const std::string & fname); + +std::string convert_to_utf8(const std::wstring & input); + +std::wstring convert_to_wstring(const std::string & input); + +void gpt_split_words(std::string str, std::vector& words); + +// split text into tokens +// +// ref: https://github.com/openai/gpt-2/blob/a74da5d99abaaba920de8131d64da2862a8f213b/src/encoder.py#L53 +// +// Regex (Python): +// r"""'s|'t|'re|'ve|'m|'ll|'d| ?\p{L}+| ?\p{N}+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+""" +// +// Regex (C++): +// R"('s|'t|'re|'ve|'m|'ll|'d| ?[[:alpha:]]+| ?[[:digit:]]+| ?[^\s[:alpha:][:digit:]]+|\s+(?!\S)|\s+)" +// +std::vector gpt_tokenize(const gpt_vocab & vocab, const std::string & text); + +// test outputs of gpt_tokenize +// +// - compare with tokens generated by the huggingface tokenizer +// - test cases are chosen based on the model's main language (under 'prompt' directory) +// - if all sentences are tokenized identically, print 'All tests passed.' +// - otherwise, print sentence, huggingface tokens, ggml tokens +// +void test_gpt_tokenizer(gpt_vocab & vocab, const std::string & fpath_test); + +// load the tokens from encoder.json +bool gpt_vocab_init(const std::string & fname, gpt_vocab & vocab); + +// sample next token given probabilities for each embedding +// +// - consider only the top K tokens +// - from them, consider only the top tokens with cumulative probability > P +// +// TODO: not sure if this implementation is correct +// TODO: temperature is not implemented +// +gpt_vocab::id gpt_sample_top_k_top_p( + const gpt_vocab & vocab, + const float * logits, + int top_k, + double top_p, + double temp, + std::mt19937 & rng); + +gpt_vocab::id gpt_sample_top_k_top_p_repeat( + const gpt_vocab & vocab, + const float * logits, + const int32_t * last_n_tokens_data, + size_t last_n_tokens_data_size, + int top_k, + double top_p, + double temp, + int repeat_last_n, + float repeat_penalty, + std::mt19937 & rng); + +// +// Audio utils +// + +// Write PCM data into WAV audio file +class wav_writer { +private: + std::ofstream file; + uint32_t dataSize = 0; + std::string wav_filename; + + bool write_header(const uint32_t sample_rate, + const uint16_t bits_per_sample, + const uint16_t channels) { + + file.write("RIFF", 4); + file.write("\0\0\0\0", 4); // Placeholder for file size + file.write("WAVE", 4); + file.write("fmt ", 4); + + const uint32_t sub_chunk_size = 16; + const uint16_t audio_format = 1; // PCM format + const uint32_t byte_rate = sample_rate * channels * bits_per_sample / 8; + const uint16_t block_align = channels * bits_per_sample / 8; + + file.write(reinterpret_cast(&sub_chunk_size), 4); + file.write(reinterpret_cast(&audio_format), 2); + file.write(reinterpret_cast(&channels), 2); + file.write(reinterpret_cast(&sample_rate), 4); + file.write(reinterpret_cast(&byte_rate), 4); + file.write(reinterpret_cast(&block_align), 2); + file.write(reinterpret_cast(&bits_per_sample), 2); + file.write("data", 4); + file.write("\0\0\0\0", 4); // Placeholder for data size + + return true; + } + + // It is assumed that PCM data is normalized to a range from -1 to 1 + bool write_audio(const float * data, size_t length) { + for (size_t i = 0; i < length; ++i) { + const int16_t intSample = int16_t(data[i] * 32767); + file.write(reinterpret_cast(&intSample), sizeof(int16_t)); + dataSize += sizeof(int16_t); + } + if (file.is_open()) { + file.seekp(4, std::ios::beg); + uint32_t fileSize = 36 + dataSize; + file.write(reinterpret_cast(&fileSize), 4); + file.seekp(40, std::ios::beg); + file.write(reinterpret_cast(&dataSize), 4); + file.seekp(0, std::ios::end); + } + return true; + } + + bool open_wav(const std::string & filename) { + if (filename != wav_filename) { + if (file.is_open()) { + file.close(); + } + } + if (!file.is_open()) { + file.open(filename, std::ios::binary); + wav_filename = filename; + dataSize = 0; + } + return file.is_open(); + } + +public: + bool open(const std::string & filename, + const uint32_t sample_rate, + const uint16_t bits_per_sample, + const uint16_t channels) { + + if (open_wav(filename)) { + write_header(sample_rate, bits_per_sample, channels); + } else { + return false; + } + + return true; + } + + bool close() { + file.close(); + return true; + } + + bool write(const float * data, size_t length) { + return write_audio(data, length); + } + + ~wav_writer() { + if (file.is_open()) { + file.close(); + } + } +}; + + +// Apply a high-pass frequency filter to PCM audio +// Suppresses frequencies below cutoff Hz +void high_pass_filter( + std::vector & data, + float cutoff, + float sample_rate); + +// Basic voice activity detection (VAD) using audio energy adaptive threshold +bool vad_simple( + std::vector & pcmf32, + int sample_rate, + int last_ms, + float vad_thold, + float freq_thold, + bool verbose); + +// compute similarity between two strings using Levenshtein distance +float similarity(const std::string & s0, const std::string & s1); + +// +// Terminal utils +// + +#define SQR(X) ((X) * (X)) +#define UNCUBE(x) x < 48 ? 0 : x < 115 ? 1 : (x - 35) / 40 + +/** + * Quantizes 24-bit RGB to xterm256 code range [16,256). + */ +static int rgb2xterm256(int r, int g, int b) { + unsigned char cube[] = {0, 0137, 0207, 0257, 0327, 0377}; + int av, ir, ig, ib, il, qr, qg, qb, ql; + av = r * .299 + g * .587 + b * .114 + .5; + ql = (il = av > 238 ? 23 : (av - 3) / 10) * 10 + 8; + qr = cube[(ir = UNCUBE(r))]; + qg = cube[(ig = UNCUBE(g))]; + qb = cube[(ib = UNCUBE(b))]; + if (SQR(qr - r) + SQR(qg - g) + SQR(qb - b) <= + SQR(ql - r) + SQR(ql - g) + SQR(ql - b)) + return ir * 36 + ig * 6 + ib + 020; + return il + 0350; +} + +static std::string set_xterm256_foreground(int r, int g, int b) { + int x = rgb2xterm256(r, g, b); + std::ostringstream oss; + oss << "\033[38;5;" << x << "m"; + return oss.str(); +} + +// Lowest is red, middle is yellow, highest is green. Color scheme from +// Paul Tol; it is colorblind friendly https://sronpersonalpages.nl/~pault +const std::vector k_colors = { + set_xterm256_foreground(220, 5, 12), + set_xterm256_foreground(232, 96, 28), + set_xterm256_foreground(241, 147, 45), + set_xterm256_foreground(246, 193, 65), + set_xterm256_foreground(247, 240, 86), + set_xterm256_foreground(144, 201, 135), + set_xterm256_foreground( 78, 178, 101), +}; + +// ANSI formatting codes +static std::string set_inverse() { + return "\033[7m"; +} + +static std::string set_underline() { + return "\033[4m"; +} + +static std::string set_dim() { + return "\033[2m"; +} + +// Style scheme for different confidence levels +const std::vector k_styles = { + set_inverse(), // Low confidence - inverse (highlighted) + set_underline(), // Medium confidence - underlined + set_dim(), // High confidence - dim +}; + +// +// Other utils +// + +// check if file exists using ifstream +bool is_file_exist(const char * filename); diff --git a/examples/deprecation-warning/CMakeLists.txt b/examples/deprecation-warning/CMakeLists.txt new file mode 100644 index 0000000..55cef03 --- /dev/null +++ b/examples/deprecation-warning/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable(main ./deprecation-warning.cpp) +add_executable(bench ./deprecation-warning.cpp) +if (WHISPER_SDL2) + add_executable(stream ./deprecation-warning.cpp) + add_executable(command ./deprecation-warning.cpp) +endif() diff --git a/examples/deprecation-warning/README.md b/examples/deprecation-warning/README.md new file mode 100644 index 0000000..e07e134 --- /dev/null +++ b/examples/deprecation-warning/README.md @@ -0,0 +1,17 @@ +# Migration notice for binary filenames + +> [!IMPORTANT] +[2024 Dec 20] Binaries have been renamed w/ a `whisper-` prefix. `main` is now `whisper-cli`, `server` is `whisper-server`, etc (https://github.com/ggerganov/whisper.cpp/pull/2648) + +This migration was important, but it is a breaking change that may not always be immediately obvious to users. + +Please update all scripts and workflows to use the new binary names. + +| Old Filename | New Filename | +| ---- | ---- | +| main | whisper-cli | +| bench | whisper-bench | +| stream | whisper-stream | +| command | whisper-command | +| server | whisper-server | +| talk-llama | whisper-talk-llama | diff --git a/examples/deprecation-warning/deprecation-warning.cpp b/examples/deprecation-warning/deprecation-warning.cpp new file mode 100644 index 0000000..9cac82f --- /dev/null +++ b/examples/deprecation-warning/deprecation-warning.cpp @@ -0,0 +1,38 @@ +// Warns users that this filename was deprecated, and provides a link for more information. + +#include +#include + +// Main +int main(int argc, char** argv) { + std::string filename = "main"; + if (argc >= 1) { + filename = argv[0]; + } + + // Get only the program name from the full path + size_t pos = filename.find_last_of("/\\"); + if (pos != std::string::npos) { + filename = filename.substr(pos+1); + } + + // Append "whisper-" to the beginning of filename to get the replacemnt filename + std::string replacement_filename = "whisper-" + filename; + + // The exception is if the filename is "main", then our replacement filename is "whisper-cli" + if (filename == "main") { + replacement_filename = "whisper-cli"; + } + + if (filename == "main.exe") { + replacement_filename = "whisper-cli.exe"; + } + + fprintf(stdout, "\n"); + fprintf(stdout, "WARNING: The binary '%s' is deprecated.\n", filename.c_str()); + fprintf(stdout, " Please use '%s' instead.\n", replacement_filename.c_str()); + fprintf(stdout, " See https://github.com/ggerganov/whisper.cpp/tree/master/examples/deprecation-warning/README.md for more information.\n"); + fprintf(stdout, "\n"); + + return EXIT_FAILURE; +} diff --git a/examples/ffmpeg-transcode.cpp b/examples/ffmpeg-transcode.cpp new file mode 100644 index 0000000..7657af6 --- /dev/null +++ b/examples/ffmpeg-transcode.cpp @@ -0,0 +1,238 @@ +#ifdef WHISPER_COMMON_FFMPEG + +#include +#include +#include +#include + +extern "C" { +#include +#include +#include +} + +// Write a minimal WAV header into the output buffer. +// Returns the number of bytes written (44 for a standard PCM WAV header). +static size_t wav_header_write(uint8_t * buf, int num_channels, int sample_rate, int bits_per_sample, uint32_t data_size) { + // RIFF header + memcpy(buf, "RIFF", 4); + uint32_t chunk_size = 36 + data_size; + memcpy(buf + 4, &chunk_size, 4); + memcpy(buf + 8, "WAVE", 4); + + // fmt subchunk + memcpy(buf + 12, "fmt ", 4); + uint32_t subchunk1_size = 16; + memcpy(buf + 16, &subchunk1_size, 4); + uint16_t audio_format = 1; // PCM + memcpy(buf + 20, &audio_format, 2); + memcpy(buf + 22, &num_channels, 2); + memcpy(buf + 24, &sample_rate, 4); + + int bytes_per_sample = (bits_per_sample / 8) * num_channels; + int byte_rate = sample_rate * bytes_per_sample; + memcpy(buf + 28, &byte_rate, 4); + memcpy(buf + 32, &bytes_per_sample, 2); + memcpy(buf + 34, &bits_per_sample, 2); + + // data subchunk + memcpy(buf + 36, "data", 4); + memcpy(buf + 40, &data_size, 4); + + return 44; +} + +bool ffmpeg_decode_audio(const std::string & ifname, std::vector & wav_data, int out_sample_rate) { + { + const char * verbose = getenv("WHISPER_COMMON_FFMPEG_VERBOSE"); + if (verbose && strcmp(verbose, "2") == 0) { + av_log_set_level(AV_LOG_DEBUG); + } else if (verbose && strcmp(verbose, "1") == 0) { + av_log_set_level(AV_LOG_VERBOSE); + } else { + av_log_set_level(AV_LOG_WARNING); + } + } + + AVFormatContext * fmt_ctx = nullptr; + if (avformat_open_input(&fmt_ctx, ifname.c_str(), nullptr, nullptr) != 0) { + fprintf(stderr, "error: failed to open input file '%s'\n", ifname.c_str()); + return true; + } + + if (avformat_find_stream_info(fmt_ctx, nullptr) < 0) { + fprintf(stderr, "error: failed to find stream information\n"); + avformat_close_input(&fmt_ctx); + return true; + } + + // Find the first audio stream + int audio_stream_idx = -1; + for (unsigned int i = 0; i < fmt_ctx->nb_streams; i++) { + if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + audio_stream_idx = i; + break; + } + } + + if (audio_stream_idx == -1) { + fprintf(stderr, "error: failed to find an audio stream in '%s'\n", ifname.c_str()); + avformat_close_input(&fmt_ctx); + return true; + } + + AVStream * audio_stream = fmt_ctx->streams[audio_stream_idx]; + + // Open the decoder + const AVCodec * codec = avcodec_find_decoder(audio_stream->codecpar->codec_id); + if (!codec) { + fprintf(stderr, "error: failed to find decoder for codec id %d\n", audio_stream->codecpar->codec_id); + avformat_close_input(&fmt_ctx); + return true; + } + + AVCodecContext * codec_ctx = avcodec_alloc_context3(codec); + if (!codec_ctx) { + fprintf(stderr, "error: failed to allocate codec context\n"); + avformat_close_input(&fmt_ctx); + return true; + } + + if (avcodec_parameters_to_context(codec_ctx, audio_stream->codecpar) < 0) { + fprintf(stderr, "error: failed to copy codec parameters to context\n"); + avcodec_free_context(&codec_ctx); + avformat_close_input(&fmt_ctx); + return true; + } + + if (avcodec_open2(codec_ctx, codec, nullptr) < 0) { + fprintf(stderr, "error: failed to open codec\n"); + avcodec_free_context(&codec_ctx); + avformat_close_input(&fmt_ctx); + return true; + } + + // Setup resampler: convert to 16-bit signed PCM, mono, 16000 Hz + const enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16; + + AVChannelLayout out_ch_layout = AV_CHANNEL_LAYOUT_MONO; + + SwrContext * swr_ctx = nullptr; + if (swr_alloc_set_opts2(&swr_ctx, &out_ch_layout, out_sample_fmt, out_sample_rate, + &codec_ctx->ch_layout, codec_ctx->sample_fmt, codec_ctx->sample_rate, + 0, nullptr) < 0) { + fprintf(stderr, "error: failed to allocate swr context\n"); + avcodec_free_context(&codec_ctx); + avformat_close_input(&fmt_ctx); + return true; + } + + if (swr_init(swr_ctx) < 0) { + fprintf(stderr, "error: failed to initialize swr context\n"); + swr_free(&swr_ctx); + avcodec_free_context(&codec_ctx); + avformat_close_input(&fmt_ctx); + return true; + } + + // Decode and resample + AVPacket * packet = av_packet_alloc(); + AVFrame * frame = av_frame_alloc(); + + // Buffer to collect resampled output + std::vector pcm_data; + + // Max output samples per swr_convert call + const int max_out_samples = 16 * 1024; + std::vector out_buffer(max_out_samples); + + while (av_read_frame(fmt_ctx, packet) >= 0) { + if (packet->stream_index != audio_stream_idx) { + av_packet_unref(packet); + continue; + } + + int ret = avcodec_send_packet(codec_ctx, packet); + av_packet_unref(packet); + + if (ret < 0) { + continue; + } + + while (ret >= 0) { + ret = avcodec_receive_frame(codec_ctx, frame); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + break; + } + if (ret < 0) { + break; + } + + // Resample + int out_samples = av_rescale_rnd(swr_get_delay(swr_ctx, out_sample_rate) + frame->nb_samples, + out_sample_rate, out_sample_rate, AV_ROUND_UP); + if (out_samples > (int)out_buffer.size()) { + out_buffer.resize(out_samples); + } + + const uint8_t * in_data[16] = {0}; + for (int p = 0; p < (int)codec_ctx->ch_layout.nb_channels && p < 16; p++) { + in_data[p] = frame->data[p]; + } + uint8_t * out_data[16] = {0}; + out_data[0] = (uint8_t *)out_buffer.data(); + + int got_samples = swr_convert(swr_ctx, out_data, out_samples, in_data, frame->nb_samples); + if (got_samples > 0) { + pcm_data.insert(pcm_data.end(), out_buffer.begin(), out_buffer.begin() + got_samples); + } + } + } + + // Flush the decoder + avcodec_send_packet(codec_ctx, nullptr); + while (avcodec_receive_frame(codec_ctx, frame) >= 0) { + int out_samples = av_rescale_rnd(swr_get_delay(swr_ctx, out_sample_rate) + frame->nb_samples, + out_sample_rate, out_sample_rate, AV_ROUND_UP); + if (out_samples > (int)out_buffer.size()) { + out_buffer.resize(out_samples); + } + const uint8_t * in_data[16] = {0}; + for (int p = 0; p < (int)codec_ctx->ch_layout.nb_channels && p < 16; p++) { + in_data[p] = frame->data[p]; + } + uint8_t * out_data[16] = {0}; + out_data[0] = (uint8_t *)out_buffer.data(); + + int got_samples = swr_convert(swr_ctx, out_data, out_samples, in_data, frame->nb_samples); + if (got_samples > 0) { + pcm_data.insert(pcm_data.end(), out_buffer.begin(), out_buffer.begin() + got_samples); + } + } + + // Flush the resampler + uint8_t * out_data[16] = {0}; + out_data[0] = (uint8_t *)out_buffer.data(); + int flush_samples = swr_convert(swr_ctx, out_data, max_out_samples, nullptr, 0); + if (flush_samples > 0) { + pcm_data.insert(pcm_data.end(), out_buffer.begin(), out_buffer.begin() + flush_samples); + } + + // Build WAV output + uint32_t data_size = pcm_data.size() * sizeof(int16_t); + wav_data.resize(44 + data_size); + + wav_header_write(wav_data.data(), 1, out_sample_rate, 16, data_size); + memcpy(wav_data.data() + 44, pcm_data.data(), data_size); + + // Cleanup + av_frame_free(&frame); + av_packet_free(&packet); + swr_free(&swr_ctx); + avcodec_free_context(&codec_ctx); + avformat_close_input(&fmt_ctx); + + return false; // success +} + +#endif // WHISPER_COMMON_FFMPEG diff --git a/examples/generate-karaoke.sh b/examples/generate-karaoke.sh new file mode 100755 index 0000000..b30f40f --- /dev/null +++ b/examples/generate-karaoke.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +# Simple tool to record audio from the microphone and generate a karaoke video +# Usage: +# +# cd whisper.cpp +# make +# +# ./examples/generate-karaoke.sh [model] [step_ms] +# +# Press Ctrl+C to stop recording +# + +executable="./build/bin/whisper-cli" +model="base.en" +model_path="models/ggml-$model.bin" + +# require sox and ffmpeg to be installed +if ! command -v sox &> /dev/null +then + echo "sox could not be found" + exit 1 +fi + +if ! command -v ffmpeg &> /dev/null +then + echo "ffmpeg could not be found" + exit 2 +fi + +if [ ! -f "$executable" ]; then + echo "'$executable' does not exist. Please build it first." + exit 3 +fi + +if [ ! -f "$model_path" ]; then + echo "'$model_path' does not exist. Please download it first." + exit 4 +fi + +# record some raw audio +sox -d rec.wav + +# run Whisper +echo "Processing ..." +${executable} -m models/ggml-base.en.bin rec.wav -owts > /dev/null 2>&1 + +# generate Karaoke video +echo "Generating video ..." +source rec.wav.wts > /dev/null 2>&1 + +# play the video +echo "Playing ./rec16.wav.mp4 ..." +ffplay -loglevel 0 -autoexit ./rec.wav.mp4 + +echo "Done" +exit 0 diff --git a/examples/grammar-parser.cpp b/examples/grammar-parser.cpp new file mode 100644 index 0000000..e282fc7 --- /dev/null +++ b/examples/grammar-parser.cpp @@ -0,0 +1,423 @@ +#include "grammar-parser.h" +#include +#include +#include +#include +#include +#include + +namespace grammar_parser { + // NOTE: assumes valid utf8 (but checks for overrun) + // copied from whisper.cpp + static std::pair decode_utf8(const char * src) { + static const int lookup[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 3, 4 }; + uint8_t first_byte = static_cast(*src); + uint8_t highbits = first_byte >> 4; + int len = lookup[highbits]; + uint8_t mask = (1 << (8 - len)) - 1; + uint32_t value = first_byte & mask; + const char * end = src + len; // may overrun! + const char * pos = src + 1; + for ( ; pos < end && *pos; pos++) { + value = (value << 6) + (static_cast(*pos) & 0x3F); + } + return std::make_pair(value, pos); + } + + static uint32_t get_symbol_id(parse_state & state, const char * src, size_t len) { + uint32_t next_id = static_cast(state.symbol_ids.size()); + auto result = state.symbol_ids.insert(std::make_pair(std::string(src, len), next_id)); + return result.first->second; + } + + static uint32_t generate_symbol_id(parse_state & state, const std::string & base_name) { + uint32_t next_id = static_cast(state.symbol_ids.size()); + state.symbol_ids[base_name + '_' + std::to_string(next_id)] = next_id; + return next_id; + } + + static void add_rule( + parse_state & state, + uint32_t rule_id, + const std::vector & rule) { + if (state.rules.size() <= rule_id) { + state.rules.resize(rule_id + 1); + } + state.rules[rule_id] = rule; + } + + static bool is_word_char(char c) { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '-' || ('0' <= c && c <= '9'); + } + + static std::pair parse_hex(const char * src, int size) { + const char * pos = src; + const char * end = src + size; + uint32_t value = 0; + for ( ; pos < end && *pos; pos++) { + value <<= 4; + char c = *pos; + if ('a' <= c && c <= 'f') { + value += c - 'a' + 10; + } else if ('A' <= c && c <= 'F') { + value += c - 'A' + 10; + } else if ('0' <= c && c <= '9') { + value += c - '0'; + } else { + break; + } + } + if (pos != end) { + throw std::runtime_error("expecting " + std::to_string(size) + " hex chars at " + src); + } + return std::make_pair(value, pos); + } + + static const char * parse_space(const char * src, bool newline_ok) { + const char * pos = src; + while (*pos == ' ' || *pos == '\t' || *pos == '#' || + (newline_ok && (*pos == '\r' || *pos == '\n'))) { + if (*pos == '#') { + while (*pos && *pos != '\r' && *pos != '\n') { + pos++; + } + } else { + pos++; + } + } + return pos; + } + + static const char * parse_name(const char * src) { + const char * pos = src; + while (is_word_char(*pos)) { + pos++; + } + if (pos == src) { + throw std::runtime_error(std::string("expecting name at ") + src); + } + return pos; + } + + static std::pair parse_char(const char * src) { + if (*src == '\\') { + switch (src[1]) { + case 'x': return parse_hex(src + 2, 2); + case 'u': return parse_hex(src + 2, 4); + case 'U': return parse_hex(src + 2, 8); + case 't': return std::make_pair('\t', src + 2); + case 'r': return std::make_pair('\r', src + 2); + case 'n': return std::make_pair('\n', src + 2); + case '\\': + case '"': + case '[': + case ']': + return std::make_pair(src[1], src + 2); + default: + throw std::runtime_error(std::string("unknown escape at ") + src); + } + } else if (*src) { + return decode_utf8(src); + } + throw std::runtime_error("unexpected end of input"); + } + + static const char * parse_alternates( + parse_state & state, + const char * src, + const std::string & rule_name, + uint32_t rule_id, + bool is_nested); + + static const char * parse_sequence( + parse_state & state, + const char * src, + const std::string & rule_name, + std::vector & out_elements, + bool is_nested) { + size_t last_sym_start = out_elements.size(); + const char * pos = src; + while (*pos) { + if (*pos == '"') { // literal string + pos++; + last_sym_start = out_elements.size(); + while (*pos != '"') { + auto char_pair = parse_char(pos); + pos = char_pair.second; + out_elements.push_back({WHISPER_GRETYPE_CHAR, char_pair.first}); + } + pos = parse_space(pos + 1, is_nested); + } else if (*pos == '[') { // char range(s) + pos++; + enum whisper_gretype start_type = WHISPER_GRETYPE_CHAR; + if (*pos == '^') { + pos++; + start_type = WHISPER_GRETYPE_CHAR_NOT; + } + last_sym_start = out_elements.size(); + while (*pos != ']') { + auto char_pair = parse_char(pos); + pos = char_pair.second; + enum whisper_gretype type = last_sym_start < out_elements.size() + ? WHISPER_GRETYPE_CHAR_ALT + : start_type; + + out_elements.push_back({type, char_pair.first}); + if (pos[0] == '-' && pos[1] != ']') { + auto endchar_pair = parse_char(pos + 1); + pos = endchar_pair.second; + out_elements.push_back({WHISPER_GRETYPE_CHAR_RNG_UPPER, endchar_pair.first}); + } + } + pos = parse_space(pos + 1, is_nested); + } else if (is_word_char(*pos)) { // rule reference + const char * name_end = parse_name(pos); + uint32_t ref_rule_id = get_symbol_id(state, pos, name_end - pos); + pos = parse_space(name_end, is_nested); + last_sym_start = out_elements.size(); + out_elements.push_back({WHISPER_GRETYPE_RULE_REF, ref_rule_id}); + } else if (*pos == '(') { // grouping + // parse nested alternates into synthesized rule + pos = parse_space(pos + 1, true); + uint32_t sub_rule_id = generate_symbol_id(state, rule_name); + pos = parse_alternates(state, pos, rule_name, sub_rule_id, true); + last_sym_start = out_elements.size(); + // output reference to synthesized rule + out_elements.push_back({WHISPER_GRETYPE_RULE_REF, sub_rule_id}); + if (*pos != ')') { + throw std::runtime_error(std::string("expecting ')' at ") + pos); + } + pos = parse_space(pos + 1, is_nested); + } else if (*pos == '*' || *pos == '+' || *pos == '?') { // repetition operator + if (last_sym_start == out_elements.size()) { + throw std::runtime_error(std::string("expecting preceding item to */+/? at ") + pos); + } + + // apply transformation to previous symbol (last_sym_start to end) according to + // rewrite rules: + // S* --> S' ::= S S' | + // S+ --> S' ::= S S' | S + // S? --> S' ::= S | + uint32_t sub_rule_id = generate_symbol_id(state, rule_name); + std::vector sub_rule; + // add preceding symbol to generated rule + sub_rule.insert( + sub_rule.end(), out_elements.begin() + last_sym_start, out_elements.end()); + if (*pos == '*' || *pos == '+') { + // cause generated rule to recurse + sub_rule.push_back({WHISPER_GRETYPE_RULE_REF, sub_rule_id}); + } + // mark start of alternate def + sub_rule.push_back({WHISPER_GRETYPE_ALT, 0}); + if (*pos == '+') { + // add preceding symbol as alternate only for '+' (otherwise empty) + sub_rule.insert( + sub_rule.end(), out_elements.begin() + last_sym_start, out_elements.end()); + } + sub_rule.push_back({WHISPER_GRETYPE_END, 0}); + add_rule(state, sub_rule_id, sub_rule); + + // in original rule, replace previous symbol with reference to generated rule + out_elements.resize(last_sym_start); + out_elements.push_back({WHISPER_GRETYPE_RULE_REF, sub_rule_id}); + + pos = parse_space(pos + 1, is_nested); + } else { + break; + } + } + return pos; + } + + static const char * parse_alternates( + parse_state & state, + const char * src, + const std::string & rule_name, + uint32_t rule_id, + bool is_nested) { + std::vector rule; + const char * pos = parse_sequence(state, src, rule_name, rule, is_nested); + while (*pos == '|') { + rule.push_back({WHISPER_GRETYPE_ALT, 0}); + pos = parse_space(pos + 1, true); + pos = parse_sequence(state, pos, rule_name, rule, is_nested); + } + rule.push_back({WHISPER_GRETYPE_END, 0}); + add_rule(state, rule_id, rule); + return pos; + } + + static const char * parse_rule(parse_state & state, const char * src) { + const char * name_end = parse_name(src); + const char * pos = parse_space(name_end, false); + size_t name_len = name_end - src; + uint32_t rule_id = get_symbol_id(state, src, name_len); + const std::string name(src, name_len); + + if (!(pos[0] == ':' && pos[1] == ':' && pos[2] == '=')) { + throw std::runtime_error(std::string("expecting ::= at ") + pos); + } + pos = parse_space(pos + 3, true); + + pos = parse_alternates(state, pos, name, rule_id, false); + + if (*pos == '\r') { + pos += pos[1] == '\n' ? 2 : 1; + } else if (*pos == '\n') { + pos++; + } else if (*pos) { + throw std::runtime_error(std::string("expecting newline or end at ") + pos); + } + return parse_space(pos, true); + } + + parse_state parse(const char * src) { + try { + parse_state state; + const char * pos = parse_space(src, true); + while (*pos) { + pos = parse_rule(state, pos); + } + return state; + } catch (const std::exception & err) { + fprintf(stderr, "%s: error parsing grammar: %s\n", __func__, err.what()); + return parse_state(); + } + } + + static void print_grammar_char(FILE * file, uint32_t c) { + if (0x20 <= c && c <= 0x7f) { + fprintf(file, "%c", static_cast(c)); + } else { + // cop out of encoding UTF-8 + fprintf(file, "", c); + } + } + + static bool is_char_element(whisper_grammar_element elem) { + switch (elem.type) { + case WHISPER_GRETYPE_CHAR: return true; + case WHISPER_GRETYPE_CHAR_NOT: return true; + case WHISPER_GRETYPE_CHAR_ALT: return true; + case WHISPER_GRETYPE_CHAR_RNG_UPPER: return true; + default: return false; + } + } + + static void print_rule_binary(FILE * file, const std::vector & rule) { + for (auto elem : rule) { + switch (elem.type) { + case WHISPER_GRETYPE_END: fprintf(file, "END"); break; + case WHISPER_GRETYPE_ALT: fprintf(file, "ALT"); break; + case WHISPER_GRETYPE_RULE_REF: fprintf(file, "RULE_REF"); break; + case WHISPER_GRETYPE_CHAR: fprintf(file, "CHAR"); break; + case WHISPER_GRETYPE_CHAR_NOT: fprintf(file, "CHAR_NOT"); break; + case WHISPER_GRETYPE_CHAR_RNG_UPPER: fprintf(file, "CHAR_RNG_UPPER"); break; + case WHISPER_GRETYPE_CHAR_ALT: fprintf(file, "CHAR_ALT"); break; + } + switch (elem.type) { + case WHISPER_GRETYPE_END: + case WHISPER_GRETYPE_ALT: + case WHISPER_GRETYPE_RULE_REF: + fprintf(file, "(%u) ", elem.value); + break; + case WHISPER_GRETYPE_CHAR: + case WHISPER_GRETYPE_CHAR_NOT: + case WHISPER_GRETYPE_CHAR_RNG_UPPER: + case WHISPER_GRETYPE_CHAR_ALT: + fprintf(file, "(\""); + print_grammar_char(file, elem.value); + fprintf(file, "\") "); + break; + } + } + fprintf(file, "\n"); + } + + static void print_rule( + FILE * file, + uint32_t rule_id, + const std::vector & rule, + const std::map & symbol_id_names) { + if (rule.empty() || rule.back().type != WHISPER_GRETYPE_END) { + throw std::runtime_error( + "malformed rule, does not end with WHISPER_GRETYPE_END: " + std::to_string(rule_id)); + } + fprintf(file, "%s ::= ", symbol_id_names.at(rule_id).c_str()); + for (size_t i = 0, end = rule.size() - 1; i < end; i++) { + whisper_grammar_element elem = rule[i]; + switch (elem.type) { + case WHISPER_GRETYPE_END: + throw std::runtime_error( + "unexpected end of rule: " + std::to_string(rule_id) + "," + + std::to_string(i)); + case WHISPER_GRETYPE_ALT: + fprintf(file, "| "); + break; + case WHISPER_GRETYPE_RULE_REF: + fprintf(file, "%s ", symbol_id_names.at(elem.value).c_str()); + break; + case WHISPER_GRETYPE_CHAR: + fprintf(file, "["); + print_grammar_char(file, elem.value); + break; + case WHISPER_GRETYPE_CHAR_NOT: + fprintf(file, "[^"); + print_grammar_char(file, elem.value); + break; + case WHISPER_GRETYPE_CHAR_RNG_UPPER: + if (i == 0 || !is_char_element(rule[i - 1])) { + throw std::runtime_error( + "WHISPER_GRETYPE_CHAR_RNG_UPPER without preceding char: " + + std::to_string(rule_id) + "," + std::to_string(i)); + } + fprintf(file, "-"); + print_grammar_char(file, elem.value); + break; + case WHISPER_GRETYPE_CHAR_ALT: + if (i == 0 || !is_char_element(rule[i - 1])) { + throw std::runtime_error( + "WHISPER_GRETYPE_CHAR_ALT without preceding char: " + + std::to_string(rule_id) + "," + std::to_string(i)); + } + print_grammar_char(file, elem.value); + break; + } + if (is_char_element(elem)) { + switch (rule[i + 1].type) { + case WHISPER_GRETYPE_CHAR_ALT: + case WHISPER_GRETYPE_CHAR_RNG_UPPER: + break; + default: + fprintf(file, "] "); + } + } + } + fprintf(file, "\n"); + } + + void print_grammar(FILE * file, const parse_state & state) { + try { + std::map symbol_id_names; + for (auto kv : state.symbol_ids) { + symbol_id_names[kv.second] = kv.first; + } + for (size_t i = 0, end = state.rules.size(); i < end; i++) { + // fprintf(file, "%zu: ", i); + // print_rule_binary(file, state.rules[i]); + print_rule(file, uint32_t(i), state.rules[i], symbol_id_names); + // fprintf(file, "\n"); + } + } catch (const std::exception & err) { + fprintf(stderr, "\n%s: error printing grammar: %s\n", __func__, err.what()); + } + } + + std::vector parse_state::c_rules() const { + std::vector ret; + for (const auto & rule : rules) { + ret.push_back(rule.data()); + } + return ret; + } +} diff --git a/examples/grammar-parser.h b/examples/grammar-parser.h new file mode 100644 index 0000000..47d019c --- /dev/null +++ b/examples/grammar-parser.h @@ -0,0 +1,29 @@ +// Implements a parser for an extended Backus-Naur form (BNF), producing the +// binary context-free grammar format specified by whisper.h. Supports character +// ranges, grouping, and repetition operators. As an example, a grammar for +// arithmetic might look like: +// +// root ::= expr +// expr ::= term ([-+*/] term)* +// term ::= num | "(" space expr ")" space +// num ::= [0-9]+ space +// space ::= [ \t\n]* + +#pragma once +#include "whisper.h" +#include +#include +#include +#include + +namespace grammar_parser { + struct parse_state { + std::map symbol_ids; + std::vector> rules; + + std::vector c_rules() const; + }; + + parse_state parse(const char * src); + void print_grammar(FILE * file, const parse_state & state); +} diff --git a/examples/helpers.js b/examples/helpers.js new file mode 100644 index 0000000..423bada --- /dev/null +++ b/examples/helpers.js @@ -0,0 +1,191 @@ +// Common Javascript functions used by the examples + +function convertTypedArray(src, type) { + var buffer = new ArrayBuffer(src.byteLength); + var baseView = new src.constructor(buffer).set(src); + return new type(buffer); +} + +var printTextarea = (function() { + var element = document.getElementById('output'); + if (element) element.value = ''; // clear browser cache + return function(text) { + if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' '); + console.log(text); + if (element) { + element.value += text + "\n"; + element.scrollTop = element.scrollHeight; // focus on bottom + } + }; +})(); + +async function clearCache() { + if (confirm('Are you sure you want to clear the cache?\nAll the models will be downloaded again.')) { + indexedDB.deleteDatabase(dbName); + location.reload(); + } +} + +// fetch a remote file from remote URL using the Fetch API +async function fetchRemote(url, cbProgress, cbPrint) { + cbPrint('fetchRemote: downloading with fetch()...'); + + const response = await fetch( + url, + { + method: 'GET', + } + ); + + if (!response.ok) { + cbPrint('fetchRemote: failed to fetch ' + url); + return; + } + + const contentLength = response.headers.get('content-length'); + const total = parseInt(contentLength, 10); + const reader = response.body.getReader(); + + var chunks = []; + var receivedLength = 0; + var progressLast = -1; + + while (true) { + const { done, value } = await reader.read(); + + if (done) { + break; + } + + chunks.push(value); + receivedLength += value.length; + + if (contentLength) { + cbProgress(receivedLength/total); + + var progressCur = Math.round((receivedLength / total) * 10); + if (progressCur != progressLast) { + cbPrint('fetchRemote: fetching ' + 10*progressCur + '% ...'); + progressLast = progressCur; + } + } + } + + var position = 0; + var chunksAll = new Uint8Array(receivedLength); + + for (var chunk of chunks) { + chunksAll.set(chunk, position); + position += chunk.length; + } + + return chunksAll; +} + +// load remote data +// - check if the data is already in the IndexedDB +// - if not, fetch it from the remote URL and store it in the IndexedDB +function loadRemote(url, dst, size_mb, cbProgress, cbReady, cbCancel, cbPrint) { + if (!navigator.storage || !navigator.storage.estimate) { + cbPrint('loadRemote: navigator.storage.estimate() is not supported'); + } else { + // query the storage quota and print it + navigator.storage.estimate().then(function (estimate) { + cbPrint('loadRemote: storage quota: ' + estimate.quota + ' bytes'); + cbPrint('loadRemote: storage usage: ' + estimate.usage + ' bytes'); + }); + } + + // check if the data is already in the IndexedDB + var rq = indexedDB.open(dbName, dbVersion); + + rq.onupgradeneeded = function (event) { + var db = event.target.result; + if (db.version == 1) { + var os = db.createObjectStore('models', { autoIncrement: false }); + cbPrint('loadRemote: created IndexedDB ' + db.name + ' version ' + db.version); + } else { + // clear the database + var os = event.currentTarget.transaction.objectStore('models'); + os.clear(); + cbPrint('loadRemote: cleared IndexedDB ' + db.name + ' version ' + db.version); + } + }; + + rq.onsuccess = function (event) { + var db = event.target.result; + var tx = db.transaction(['models'], 'readonly'); + var os = tx.objectStore('models'); + var rq = os.get(url); + + rq.onsuccess = function (event) { + if (rq.result) { + cbPrint('loadRemote: "' + url + '" is already in the IndexedDB'); + cbReady(dst, rq.result); + } else { + // data is not in the IndexedDB + cbPrint('loadRemote: "' + url + '" is not in the IndexedDB'); + + // alert and ask the user to confirm + if (!confirm( + 'You are about to download ' + size_mb + ' MB of data.\n' + + 'The model data will be cached in the browser for future use.\n\n' + + 'Press OK to continue.')) { + cbCancel(); + return; + } + + fetchRemote(url, cbProgress, cbPrint).then(function (data) { + if (data) { + // store the data in the IndexedDB + var rq = indexedDB.open(dbName, dbVersion); + rq.onsuccess = function (event) { + var db = event.target.result; + var tx = db.transaction(['models'], 'readwrite'); + var os = tx.objectStore('models'); + + var rq = null; + try { + var rq = os.put(data, url); + } catch (e) { + cbPrint('loadRemote: failed to store "' + url + '" in the IndexedDB: \n' + e); + cbCancel(); + return; + } + + rq.onsuccess = function (event) { + cbPrint('loadRemote: "' + url + '" stored in the IndexedDB'); + cbReady(dst, data); + }; + + rq.onerror = function (event) { + cbPrint('loadRemote: failed to store "' + url + '" in the IndexedDB'); + cbCancel(); + }; + }; + } + }); + } + }; + + rq.onerror = function (event) { + cbPrint('loadRemote: failed to get data from the IndexedDB'); + cbCancel(); + }; + }; + + rq.onerror = function (event) { + cbPrint('loadRemote: failed to open IndexedDB'); + cbCancel(); + }; + + rq.onblocked = function (event) { + cbPrint('loadRemote: failed to open IndexedDB: blocked'); + cbCancel(); + }; + + rq.onabort = function (event) { + cbPrint('loadRemote: failed to open IndexedDB: abort'); + cbCancel(); + }; +} diff --git a/examples/json.hpp b/examples/json.hpp new file mode 100644 index 0000000..4d1a37a --- /dev/null +++ b/examples/json.hpp @@ -0,0 +1,24596 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + +/****************************************************************************\ + * Note on documentation: The source files contain links to the online * + * documentation of the public API at https://json.nlohmann.me. This URL * + * contains the most recent documentation and should also be applicable to * + * previous versions; documentation for deprecated functions is not * + * removed, but marked deprecated. See "Generate documentation" section in * + * file docs/README.md. * +\****************************************************************************/ + +#ifndef INCLUDE_NLOHMANN_JSON_HPP_ +#define INCLUDE_NLOHMANN_JSON_HPP_ + +#include // all_of, find, for_each +#include // nullptr_t, ptrdiff_t, size_t +#include // hash, less +#include // initializer_list +#ifndef JSON_NO_IO + #include // istream, ostream +#endif // JSON_NO_IO +#include // random_access_iterator_tag +#include // unique_ptr +#include // accumulate +#include // string, stoi, to_string +#include // declval, forward, move, pair, swap +#include // vector + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// This file contains all macro definitions affecting or depending on the ABI + +#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK + #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH) + #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 2 + #warning "Already included a different version of the library!" + #endif + #endif +#endif + +#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_MINOR 11 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_PATCH 2 // NOLINT(modernize-macro-to-enum) + +#ifndef JSON_DIAGNOSTICS + #define JSON_DIAGNOSTICS 0 +#endif + +#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON + #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0 +#endif + +#if JSON_DIAGNOSTICS + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag +#else + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS +#endif + +#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON + #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp +#else + #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION + #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0 +#endif + +// Construct the namespace ABI tags component +#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b +#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \ + NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) + +#define NLOHMANN_JSON_ABI_TAGS \ + NLOHMANN_JSON_ABI_TAGS_CONCAT( \ + NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \ + NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON) + +// Construct the namespace version component +#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \ + _v ## major ## _ ## minor ## _ ## patch +#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \ + NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) + +#if NLOHMANN_JSON_NAMESPACE_NO_VERSION +#define NLOHMANN_JSON_NAMESPACE_VERSION +#else +#define NLOHMANN_JSON_NAMESPACE_VERSION \ + NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \ + NLOHMANN_JSON_VERSION_MINOR, \ + NLOHMANN_JSON_VERSION_PATCH) +#endif + +// Combine namespace components +#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b +#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \ + NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) + +#ifndef NLOHMANN_JSON_NAMESPACE +#define NLOHMANN_JSON_NAMESPACE \ + nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \ + NLOHMANN_JSON_ABI_TAGS, \ + NLOHMANN_JSON_NAMESPACE_VERSION) +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN +#define NLOHMANN_JSON_NAMESPACE_BEGIN \ + namespace nlohmann \ + { \ + inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \ + NLOHMANN_JSON_ABI_TAGS, \ + NLOHMANN_JSON_NAMESPACE_VERSION) \ + { +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_END +#define NLOHMANN_JSON_NAMESPACE_END \ + } /* namespace (inline namespace) NOLINT(readability/namespace) */ \ + } // namespace nlohmann +#endif + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // transform +#include // array +#include // forward_list +#include // inserter, front_inserter, end +#include // map +#include // string +#include // tuple, make_tuple +#include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible +#include // unordered_map +#include // pair, declval +#include // valarray + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // nullptr_t +#include // exception +#include // runtime_error +#include // to_string +#include // vector + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // array +#include // size_t +#include // uint8_t +#include // string + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // declval, pair +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +template struct make_void +{ + using type = void; +}; +template using void_t = typename make_void::type; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +// https://en.cppreference.com/w/cpp/experimental/is_detected +struct nonesuch +{ + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + nonesuch(nonesuch const&&) = delete; + void operator=(nonesuch const&) = delete; + void operator=(nonesuch&&) = delete; +}; + +template class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op; +}; + +template class Op, class... Args> +using is_detected = typename detector::value_t; + +template class Op, class... Args> +struct is_detected_lazy : is_detected { }; + +template class Op, class... Args> +using detected_t = typename detector::type; + +template class Op, class... Args> +using detected_or = detector; + +template class Op, class... Args> +using detected_or_t = typename detected_or::type; + +template class Op, class... Args> +using is_detected_exact = std::is_same>; + +template class Op, class... Args> +using is_detected_convertible = + std::is_convertible, To>; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + + +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-FileCopyrightText: 2016-2021 Evan Nemerson +// SPDX-License-Identifier: MIT + +/* Hedley - https://nemequ.github.io/hedley + * Created by Evan Nemerson + */ + +#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15) +#if defined(JSON_HEDLEY_VERSION) + #undef JSON_HEDLEY_VERSION +#endif +#define JSON_HEDLEY_VERSION 15 + +#if defined(JSON_HEDLEY_STRINGIFY_EX) + #undef JSON_HEDLEY_STRINGIFY_EX +#endif +#define JSON_HEDLEY_STRINGIFY_EX(x) #x + +#if defined(JSON_HEDLEY_STRINGIFY) + #undef JSON_HEDLEY_STRINGIFY +#endif +#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x) + +#if defined(JSON_HEDLEY_CONCAT_EX) + #undef JSON_HEDLEY_CONCAT_EX +#endif +#define JSON_HEDLEY_CONCAT_EX(a,b) a##b + +#if defined(JSON_HEDLEY_CONCAT) + #undef JSON_HEDLEY_CONCAT +#endif +#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b) + +#if defined(JSON_HEDLEY_CONCAT3_EX) + #undef JSON_HEDLEY_CONCAT3_EX +#endif +#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c + +#if defined(JSON_HEDLEY_CONCAT3) + #undef JSON_HEDLEY_CONCAT3 +#endif +#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c) + +#if defined(JSON_HEDLEY_VERSION_ENCODE) + #undef JSON_HEDLEY_VERSION_ENCODE +#endif +#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision)) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR) + #undef JSON_HEDLEY_VERSION_DECODE_MAJOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR) + #undef JSON_HEDLEY_VERSION_DECODE_MINOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION) + #undef JSON_HEDLEY_VERSION_DECODE_REVISION +#endif +#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000) + +#if defined(JSON_HEDLEY_GNUC_VERSION) + #undef JSON_HEDLEY_GNUC_VERSION +#endif +#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#elif defined(__GNUC__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) +#endif + +#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK) + #undef JSON_HEDLEY_GNUC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GNUC_VERSION) + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION) + #undef JSON_HEDLEY_MSVC_VERSION +#endif +#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) +#elif defined(_MSC_FULL_VER) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) +#elif defined(_MSC_VER) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK) + #undef JSON_HEDLEY_MSVC_VERSION_CHECK +#endif +#if !defined(JSON_HEDLEY_MSVC_VERSION) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0) +#elif defined(_MSC_VER) && (_MSC_VER >= 1400) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) +#elif defined(_MSC_VER) && (_MSC_VER >= 1200) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) +#else + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor))) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION) + #undef JSON_HEDLEY_INTEL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) +#elif defined(__INTEL_COMPILER) && !defined(__ICL) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_VERSION) + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_INTEL_CL_VERSION) + #undef JSON_HEDLEY_INTEL_CL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL) + #define JSON_HEDLEY_INTEL_CL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_CL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_CL_VERSION) + #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_CL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION) + #undef JSON_HEDLEY_PGI_VERSION +#endif +#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) + #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION_CHECK) + #undef JSON_HEDLEY_PGI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PGI_VERSION) + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #undef JSON_HEDLEY_SUNPRO_VERSION +#endif +#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10) +#elif defined(__SUNPRO_C) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf) +#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10) +#elif defined(__SUNPRO_CC) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK) + #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION +#endif +#if defined(__EMSCRIPTEN__) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION) + #undef JSON_HEDLEY_ARM_VERSION +#endif +#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100) +#elif defined(__CC_ARM) && defined(__ARMCC_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION_CHECK) + #undef JSON_HEDLEY_ARM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_ARM_VERSION) + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION) + #undef JSON_HEDLEY_IBM_VERSION +#endif +#if defined(__ibmxl__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) +#elif defined(__xlC__) && defined(__xlC_ver__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) +#elif defined(__xlC__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION_CHECK) + #undef JSON_HEDLEY_IBM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IBM_VERSION) + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_VERSION) + #undef JSON_HEDLEY_TI_VERSION +#endif +#if \ + defined(__TI_COMPILER_VERSION__) && \ + ( \ + defined(__TMS470__) || defined(__TI_ARM__) || \ + defined(__MSP430__) || \ + defined(__TMS320C2000__) \ + ) +#if (__TI_COMPILER_VERSION__ >= 16000000) + #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif +#endif + +#if defined(JSON_HEDLEY_TI_VERSION_CHECK) + #undef JSON_HEDLEY_TI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_VERSION) + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #undef JSON_HEDLEY_TI_CL2000_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__) + #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #undef JSON_HEDLEY_TI_CL430_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__) + #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #undef JSON_HEDLEY_TI_ARMCL_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__)) + #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK) + #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #undef JSON_HEDLEY_TI_CL6X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__) + #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #undef JSON_HEDLEY_TI_CL7X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__) + #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #undef JSON_HEDLEY_TI_CLPRU_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__) + #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION) + #undef JSON_HEDLEY_CRAY_VERSION +#endif +#if defined(_CRAYC) + #if defined(_RELEASE_PATCHLEVEL) + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) + #else + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK) + #undef JSON_HEDLEY_CRAY_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_CRAY_VERSION) + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION) + #undef JSON_HEDLEY_IAR_VERSION +#endif +#if defined(__IAR_SYSTEMS_ICC__) + #if __VER__ > 1000 + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) + #else + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION_CHECK) + #undef JSON_HEDLEY_IAR_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IAR_VERSION) + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION) + #undef JSON_HEDLEY_TINYC_VERSION +#endif +#if defined(__TINYC__) + #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK) + #undef JSON_HEDLEY_TINYC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION) + #undef JSON_HEDLEY_DMC_VERSION +#endif +#if defined(__DMC__) + #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION_CHECK) + #undef JSON_HEDLEY_DMC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_DMC_VERSION) + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #undef JSON_HEDLEY_COMPCERT_VERSION +#endif +#if defined(__COMPCERT_VERSION__) + #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK) + #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION) + #undef JSON_HEDLEY_PELLES_VERSION +#endif +#if defined(__POCC__) + #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK) + #undef JSON_HEDLEY_PELLES_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PELLES_VERSION) + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_MCST_LCC_VERSION) + #undef JSON_HEDLEY_MCST_LCC_VERSION +#endif +#if defined(__LCC__) && defined(__LCC_MINOR__) + #define JSON_HEDLEY_MCST_LCC_VERSION JSON_HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__) +#endif + +#if defined(JSON_HEDLEY_MCST_LCC_VERSION_CHECK) + #undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_MCST_LCC_VERSION) + #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_MCST_LCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION) + #undef JSON_HEDLEY_GCC_VERSION +#endif +#if \ + defined(JSON_HEDLEY_GNUC_VERSION) && \ + !defined(__clang__) && \ + !defined(JSON_HEDLEY_INTEL_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_ARM_VERSION) && \ + !defined(JSON_HEDLEY_CRAY_VERSION) && \ + !defined(JSON_HEDLEY_TI_VERSION) && \ + !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL430_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \ + !defined(__COMPCERT__) && \ + !defined(JSON_HEDLEY_MCST_LCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_ATTRIBUTE +#endif +#if \ + defined(__has_attribute) && \ + ( \ + (!defined(JSON_HEDLEY_IAR_VERSION) || JSON_HEDLEY_IAR_VERSION_CHECK(8,5,9)) \ + ) +# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) +#else +# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE +#endif +#if \ + defined(__has_cpp_attribute) && \ + defined(__cplusplus) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#endif +#if !defined(__cplusplus) || !defined(__has_cpp_attribute) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#elif \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \ + (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_BUILTIN) + #undef JSON_HEDLEY_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin) +#else + #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN) + #undef JSON_HEDLEY_GNUC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN) + #undef JSON_HEDLEY_GCC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_FEATURE) + #undef JSON_HEDLEY_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature) +#else + #define JSON_HEDLEY_HAS_FEATURE(feature) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE) + #undef JSON_HEDLEY_GNUC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_FEATURE) + #undef JSON_HEDLEY_GCC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_EXTENSION) + #undef JSON_HEDLEY_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension) +#else + #define JSON_HEDLEY_HAS_EXTENSION(extension) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION) + #undef JSON_HEDLEY_GNUC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION) + #undef JSON_HEDLEY_GCC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_WARNING) + #undef JSON_HEDLEY_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning) +#else + #define JSON_HEDLEY_HAS_WARNING(warning) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_WARNING) + #undef JSON_HEDLEY_GNUC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_WARNING) + #undef JSON_HEDLEY_GCC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + defined(__clang__) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \ + (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR)) + #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_PRAGMA(value) __pragma(value) +#else + #define JSON_HEDLEY_PRAGMA(value) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH) + #undef JSON_HEDLEY_DIAGNOSTIC_PUSH +#endif +#if defined(JSON_HEDLEY_DIAGNOSTIC_POP) + #undef JSON_HEDLEY_DIAGNOSTIC_POP +#endif +#if defined(__clang__) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) + #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) +#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_PUSH + #define JSON_HEDLEY_DIAGNOSTIC_POP +#endif + +/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat") +# if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions") +# if JSON_HEDLEY_HAS_WARNING("-Wc++1z-extensions") +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + _Pragma("clang diagnostic ignored \"-Wc++1z-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# endif +#endif +#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x +#endif + +#if defined(JSON_HEDLEY_CONST_CAST) + #undef JSON_HEDLEY_CONST_CAST +#endif +#if defined(__cplusplus) +# define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) +#elif \ + JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_REINTERPRET_CAST) + #undef JSON_HEDLEY_REINTERPRET_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) +#else + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_STATIC_CAST) + #undef JSON_HEDLEY_STATIC_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) +#else + #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_CPP_CAST) + #undef JSON_HEDLEY_CPP_CAST +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wold-style-cast") +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \ + ((T) (expr)) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0) +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("diag_suppress=Pe137") \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr)) +# endif +#else +# define JSON_HEDLEY_CPP_CAST(T, expr) (expr) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1216,1444,1445") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 161") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292)) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097,1098") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097") +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wcast-qual") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunused-function") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("clang diagnostic ignored \"-Wunused-function\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("GCC diagnostic ignored \"-Wunused-function\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(1,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505)) +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("diag_suppress 3142") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#endif + +#if defined(JSON_HEDLEY_DEPRECATED) + #undef JSON_HEDLEY_DEPRECATED +#endif +#if defined(JSON_HEDLEY_DEPRECATED_FOR) + #undef JSON_HEDLEY_DEPRECATED_FOR +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) +#elif \ + (JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) +#elif defined(__cplusplus) && (__cplusplus >= 201402L) + #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]]) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated") + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") +#else + #define JSON_HEDLEY_DEPRECATED(since) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) +#endif + +#if defined(JSON_HEDLEY_UNAVAILABLE) + #undef JSON_HEDLEY_UNAVAILABLE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) +#else + #define JSON_HEDLEY_UNAVAILABLE(available_since) +#endif + +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT +#endif +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__)) +#elif (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) +#elif defined(_Check_return_) /* SAL */ + #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_ + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_ +#else + #define JSON_HEDLEY_WARN_UNUSED_RESULT + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) +#endif + +#if defined(JSON_HEDLEY_SENTINEL) + #undef JSON_HEDLEY_SENTINEL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) +#else + #define JSON_HEDLEY_SENTINEL(position) +#endif + +#if defined(JSON_HEDLEY_NO_RETURN) + #undef JSON_HEDLEY_NO_RETURN +#endif +#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NO_RETURN __noreturn +#elif \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #define JSON_HEDLEY_NO_RETURN _Noreturn +#elif defined(__cplusplus) && (__cplusplus >= 201103L) + #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NO_RETURN __attribute((noreturn)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#else + #define JSON_HEDLEY_NO_RETURN +#endif + +#if defined(JSON_HEDLEY_NO_ESCAPE) + #undef JSON_HEDLEY_NO_ESCAPE +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape) + #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__)) +#else + #define JSON_HEDLEY_NO_ESCAPE +#endif + +#if defined(JSON_HEDLEY_UNREACHABLE) + #undef JSON_HEDLEY_UNREACHABLE +#endif +#if defined(JSON_HEDLEY_UNREACHABLE_RETURN) + #undef JSON_HEDLEY_UNREACHABLE_RETURN +#endif +#if defined(JSON_HEDLEY_ASSUME) + #undef JSON_HEDLEY_ASSUME +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_ASSUME(expr) __assume(expr) +#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume) + #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr) +#elif \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #if defined(__cplusplus) + #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr) + #else + #define JSON_HEDLEY_ASSUME(expr) _nassert(expr) + #endif +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable() +#elif defined(JSON_HEDLEY_ASSUME) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif +#if !defined(JSON_HEDLEY_ASSUME) + #if defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1))) + #else + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr) + #endif +#endif +#if defined(JSON_HEDLEY_UNREACHABLE) + #if \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value)) + #else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE() + #endif +#else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value) +#endif +#if !defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif + +JSON_HEDLEY_DIAGNOSTIC_PUSH +#if JSON_HEDLEY_HAS_WARNING("-Wpedantic") + #pragma clang diagnostic ignored "-Wpedantic" +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus) + #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif +#if JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0) + #if defined(__clang__) + #pragma clang diagnostic ignored "-Wvariadic-macros" + #elif defined(JSON_HEDLEY_GCC_VERSION) + #pragma GCC diagnostic ignored "-Wvariadic-macros" + #endif +#endif +#if defined(JSON_HEDLEY_NON_NULL) + #undef JSON_HEDLEY_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) +#else + #define JSON_HEDLEY_NON_NULL(...) +#endif +JSON_HEDLEY_DIAGNOSTIC_POP + +#if defined(JSON_HEDLEY_PRINTF_FORMAT) + #undef JSON_HEDLEY_PRINTF_FORMAT +#endif +#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check))) +#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check))) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(format) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) +#else + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) +#endif + +#if defined(JSON_HEDLEY_CONSTEXPR) + #undef JSON_HEDLEY_CONSTEXPR +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr) + #endif +#endif +#if !defined(JSON_HEDLEY_CONSTEXPR) + #define JSON_HEDLEY_CONSTEXPR +#endif + +#if defined(JSON_HEDLEY_PREDICT) + #undef JSON_HEDLEY_PREDICT +#endif +#if defined(JSON_HEDLEY_LIKELY) + #undef JSON_HEDLEY_LIKELY +#endif +#if defined(JSON_HEDLEY_UNLIKELY) + #undef JSON_HEDLEY_UNLIKELY +#endif +#if defined(JSON_HEDLEY_UNPREDICTABLE) + #undef JSON_HEDLEY_UNPREDICTABLE +#endif +#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable) + #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr)) +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(JSON_HEDLEY_PGI_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability( (expr), (value), (probability)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1 , (probability)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0 , (probability)) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect (!!(expr), 1 ) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect (!!(expr), 0 ) +#elif \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PREDICT(expr, expected, probability) \ + (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ + })) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ + })) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) +#else +# define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_LIKELY(expr) (!!(expr)) +# define JSON_HEDLEY_UNLIKELY(expr) (!!(expr)) +#endif +#if !defined(JSON_HEDLEY_UNPREDICTABLE) + #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5) +#endif + +#if defined(JSON_HEDLEY_MALLOC) + #undef JSON_HEDLEY_MALLOC +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_MALLOC __attribute__((__malloc__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_MALLOC __declspec(restrict) +#else + #define JSON_HEDLEY_MALLOC +#endif + +#if defined(JSON_HEDLEY_PURE) + #undef JSON_HEDLEY_PURE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PURE __attribute__((__pure__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) +# define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data") +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \ + ) +# define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;") +#else +# define JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_CONST) + #undef JSON_HEDLEY_CONST +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(const) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_CONST __attribute__((__const__)) +#elif \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_CONST _Pragma("no_side_effect") +#else + #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_RESTRICT) + #undef JSON_HEDLEY_RESTRICT +#endif +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT restrict +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + defined(__clang__) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_RESTRICT __restrict +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT _Restrict +#else + #define JSON_HEDLEY_RESTRICT +#endif + +#if defined(JSON_HEDLEY_INLINE) + #undef JSON_HEDLEY_INLINE +#endif +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + (defined(__cplusplus) && (__cplusplus >= 199711L)) + #define JSON_HEDLEY_INLINE inline +#elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0) + #define JSON_HEDLEY_INLINE __inline__ +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_INLINE __inline +#else + #define JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_ALWAYS_INLINE) + #undef JSON_HEDLEY_ALWAYS_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) +# define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_ALWAYS_INLINE __forceinline +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \ + ) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") +#else +# define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_NEVER_INLINE) + #undef JSON_HEDLEY_NEVER_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#else + #define JSON_HEDLEY_NEVER_INLINE +#endif + +#if defined(JSON_HEDLEY_PRIVATE) + #undef JSON_HEDLEY_PRIVATE +#endif +#if defined(JSON_HEDLEY_PUBLIC) + #undef JSON_HEDLEY_PUBLIC +#endif +#if defined(JSON_HEDLEY_IMPORT) + #undef JSON_HEDLEY_IMPORT +#endif +#if defined(_WIN32) || defined(__CYGWIN__) +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC __declspec(dllexport) +# define JSON_HEDLEY_IMPORT __declspec(dllimport) +#else +# if \ + JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + ( \ + defined(__TI_EABI__) && \ + ( \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \ + ) \ + ) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) +# define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default"))) +# else +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC +# endif +# define JSON_HEDLEY_IMPORT extern +#endif + +#if defined(JSON_HEDLEY_NO_THROW) + #undef JSON_HEDLEY_NO_THROW +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NO_THROW __declspec(nothrow) +#else + #define JSON_HEDLEY_NO_THROW +#endif + +#if defined(JSON_HEDLEY_FALL_THROUGH) + #undef JSON_HEDLEY_FALL_THROUGH +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]]) +#elif defined(__fallthrough) /* SAL */ + #define JSON_HEDLEY_FALL_THROUGH __fallthrough +#else + #define JSON_HEDLEY_FALL_THROUGH +#endif + +#if defined(JSON_HEDLEY_RETURNS_NON_NULL) + #undef JSON_HEDLEY_RETURNS_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) +#elif defined(_Ret_notnull_) /* SAL */ + #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_ +#else + #define JSON_HEDLEY_RETURNS_NON_NULL +#endif + +#if defined(JSON_HEDLEY_ARRAY_PARAM) + #undef JSON_HEDLEY_ARRAY_PARAM +#endif +#if \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__STDC_NO_VLA__) && \ + !defined(__cplusplus) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_ARRAY_PARAM(name) (name) +#else + #define JSON_HEDLEY_ARRAY_PARAM(name) +#endif + +#if defined(JSON_HEDLEY_IS_CONSTANT) + #undef JSON_HEDLEY_IS_CONSTANT +#endif +#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR) + #undef JSON_HEDLEY_REQUIRE_CONSTEXPR +#endif +/* JSON_HEDLEY_IS_CONSTEXPR_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #undef JSON_HEDLEY_IS_CONSTEXPR_ +#endif +#if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) +#endif +#if !defined(__cplusplus) +# if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) +#endif +# elif \ + ( \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ + !defined(JSON_HEDLEY_SUNPRO_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION)) || \ + (JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) +#endif +# elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + defined(JSON_HEDLEY_INTEL_VERSION) || \ + defined(JSON_HEDLEY_TINYC_VERSION) || \ + defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \ + defined(JSON_HEDLEY_TI_CL2000_VERSION) || \ + defined(JSON_HEDLEY_TI_CL6X_VERSION) || \ + defined(JSON_HEDLEY_TI_CL7X_VERSION) || \ + defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \ + defined(__clang__) +# define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \ + sizeof(void) != \ + sizeof(*( \ + 1 ? \ + ((void*) ((expr) * 0L) ) : \ +((struct { char v[sizeof(void) * 2]; } *) 1) \ + ) \ + ) \ + ) +# endif +#endif +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1)) +#else + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) (0) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr) +#endif + +#if defined(JSON_HEDLEY_BEGIN_C_DECLS) + #undef JSON_HEDLEY_BEGIN_C_DECLS +#endif +#if defined(JSON_HEDLEY_END_C_DECLS) + #undef JSON_HEDLEY_END_C_DECLS +#endif +#if defined(JSON_HEDLEY_C_DECL) + #undef JSON_HEDLEY_C_DECL +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_BEGIN_C_DECLS extern "C" { + #define JSON_HEDLEY_END_C_DECLS } + #define JSON_HEDLEY_C_DECL extern "C" +#else + #define JSON_HEDLEY_BEGIN_C_DECLS + #define JSON_HEDLEY_END_C_DECLS + #define JSON_HEDLEY_C_DECL +#endif + +#if defined(JSON_HEDLEY_STATIC_ASSERT) + #undef JSON_HEDLEY_STATIC_ASSERT +#endif +#if \ + !defined(__cplusplus) && ( \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \ + (JSON_HEDLEY_HAS_FEATURE(c_static_assert) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + defined(_Static_assert) \ + ) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) +#elif \ + (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message)) +#else +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) +#endif + +#if defined(JSON_HEDLEY_NULL) + #undef JSON_HEDLEY_NULL +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr) + #elif defined(NULL) + #define JSON_HEDLEY_NULL NULL + #else + #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0) + #endif +#elif defined(NULL) + #define JSON_HEDLEY_NULL NULL +#else + #define JSON_HEDLEY_NULL ((void*) 0) +#endif + +#if defined(JSON_HEDLEY_MESSAGE) + #undef JSON_HEDLEY_MESSAGE +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_MESSAGE(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(message msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg) +#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_WARNING) + #undef JSON_HEDLEY_WARNING +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_WARNING(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(clang warning msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_REQUIRE) + #undef JSON_HEDLEY_REQUIRE +#endif +#if defined(JSON_HEDLEY_REQUIRE_MSG) + #undef JSON_HEDLEY_REQUIRE_MSG +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if) +# if JSON_HEDLEY_HAS_WARNING("-Wgcc-compat") +# define JSON_HEDLEY_REQUIRE(expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), #expr, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), msg, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error"))) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error"))) +# endif +#else +# define JSON_HEDLEY_REQUIRE(expr) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) +#endif + +#if defined(JSON_HEDLEY_FLAGS) + #undef JSON_HEDLEY_FLAGS +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || JSON_HEDLEY_HAS_WARNING("-Wbitfield-enum-conversion")) + #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__)) +#else + #define JSON_HEDLEY_FLAGS +#endif + +#if defined(JSON_HEDLEY_FLAGS_CAST) + #undef JSON_HEDLEY_FLAGS_CAST +#endif +#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0) +# define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("warning(disable:188)") \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr) +#endif + +#if defined(JSON_HEDLEY_EMPTY_BASES) + #undef JSON_HEDLEY_EMPTY_BASES +#endif +#if \ + (JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases) +#else + #define JSON_HEDLEY_EMPTY_BASES +#endif + +/* Remaining macros are deprecated. */ + +#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#endif +#if defined(__clang__) + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0) +#else + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN) + #undef JSON_HEDLEY_CLANG_HAS_BUILTIN +#endif +#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin) + +#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE) + #undef JSON_HEDLEY_CLANG_HAS_FEATURE +#endif +#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature) + +#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION) + #undef JSON_HEDLEY_CLANG_HAS_EXTENSION +#endif +#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension) + +#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_WARNING) + #undef JSON_HEDLEY_CLANG_HAS_WARNING +#endif +#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning) + +#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */ + + +// This file contains all internal macro definitions (except those affecting ABI) +// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them + +// #include + + +// exclude unsupported compilers +#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) + #if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #endif +#endif + +// C++ language standard detection +// if the user manually specified the used c++ version this is skipped +#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) + #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 + #endif + // the cpp 11 flag is always specified because it is the minimal required version + #define JSON_HAS_CPP_11 +#endif + +#ifdef __has_include + #if __has_include() + #include + #endif +#endif + +#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM) + #ifdef JSON_HAS_CPP_17 + #if defined(__cpp_lib_filesystem) + #define JSON_HAS_FILESYSTEM 1 + #elif defined(__cpp_lib_experimental_filesystem) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #elif !defined(__has_include) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #elif __has_include() + #define JSON_HAS_FILESYSTEM 1 + #elif __has_include() + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #endif + + // std::filesystem does not work on MinGW GCC 8: https://sourceforge.net/p/mingw-w64/bugs/737/ + #if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ == 8 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before GCC 8: https://en.cppreference.com/w/cpp/compiler_support + #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before Clang 7: https://en.cppreference.com/w/cpp/compiler_support + #if defined(__clang_major__) && __clang_major__ < 7 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support + #if defined(_MSC_VER) && _MSC_VER < 1914 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before iOS 13 + #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before macOS Catalina + #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + #endif +#endif + +#ifndef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 0 +#endif + +#ifndef JSON_HAS_FILESYSTEM + #define JSON_HAS_FILESYSTEM 0 +#endif + +#ifndef JSON_HAS_THREE_WAY_COMPARISON + #if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L \ + && defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L + #define JSON_HAS_THREE_WAY_COMPARISON 1 + #else + #define JSON_HAS_THREE_WAY_COMPARISON 0 + #endif +#endif + +#ifndef JSON_HAS_RANGES + // ranges header shipping in GCC 11.1.0 (released 2021-04-27) has syntax error + #if defined(__GLIBCXX__) && __GLIBCXX__ == 20210427 + #define JSON_HAS_RANGES 0 + #elif defined(__cpp_lib_ranges) + #define JSON_HAS_RANGES 1 + #else + #define JSON_HAS_RANGES 0 + #endif +#endif + +#ifdef JSON_HAS_CPP_17 + #define JSON_INLINE_VARIABLE inline +#else + #define JSON_INLINE_VARIABLE +#endif + +#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address) + #define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]] +#else + #define JSON_NO_UNIQUE_ADDRESS +#endif + +// disable documentation warnings on clang +#if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdocumentation" + #pragma clang diagnostic ignored "-Wdocumentation-unknown-command" +#endif + +// allow disabling exceptions +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) + #define JSON_INTERNAL_CATCH(exception) catch(exception) +#else + #include + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) + #define JSON_INTERNAL_CATCH(exception) if(false) +#endif + +// override exception macros +#if defined(JSON_THROW_USER) + #undef JSON_THROW + #define JSON_THROW JSON_THROW_USER +#endif +#if defined(JSON_TRY_USER) + #undef JSON_TRY + #define JSON_TRY JSON_TRY_USER +#endif +#if defined(JSON_CATCH_USER) + #undef JSON_CATCH + #define JSON_CATCH JSON_CATCH_USER + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_CATCH_USER +#endif +#if defined(JSON_INTERNAL_CATCH_USER) + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER +#endif + +// allow overriding assert +#if !defined(JSON_ASSERT) + #include // assert + #define JSON_ASSERT(x) assert(x) +#endif + +// allow to access some private functions (needed by the test suite) +#if defined(JSON_TESTS_PRIVATE) + #define JSON_PRIVATE_UNLESS_TESTED public +#else + #define JSON_PRIVATE_UNLESS_TESTED private +#endif + +/*! +@brief macro to briefly define a mapping between an enum and JSON +@def NLOHMANN_JSON_SERIALIZE_ENUM +@since version 3.4.0 +*/ +#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ + template \ + inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template \ + inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [&j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ + } + +// Ugly macros to avoid uglier copy-paste when specializing basic_json. They +// may be removed in the future once the class is split. + +#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ + template class ObjectType, \ + template class ArrayType, \ + class StringType, class BooleanType, class NumberIntegerType, \ + class NumberUnsignedType, class NumberFloatType, \ + template class AllocatorType, \ + template class JSONSerializer, \ + class BinaryType> + +#define NLOHMANN_BASIC_JSON_TPL \ + basic_json + +// Macros to simplify conversion from/to types + +#define NLOHMANN_JSON_EXPAND( x ) x +#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME +#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \ + NLOHMANN_JSON_PASTE64, \ + NLOHMANN_JSON_PASTE63, \ + NLOHMANN_JSON_PASTE62, \ + NLOHMANN_JSON_PASTE61, \ + NLOHMANN_JSON_PASTE60, \ + NLOHMANN_JSON_PASTE59, \ + NLOHMANN_JSON_PASTE58, \ + NLOHMANN_JSON_PASTE57, \ + NLOHMANN_JSON_PASTE56, \ + NLOHMANN_JSON_PASTE55, \ + NLOHMANN_JSON_PASTE54, \ + NLOHMANN_JSON_PASTE53, \ + NLOHMANN_JSON_PASTE52, \ + NLOHMANN_JSON_PASTE51, \ + NLOHMANN_JSON_PASTE50, \ + NLOHMANN_JSON_PASTE49, \ + NLOHMANN_JSON_PASTE48, \ + NLOHMANN_JSON_PASTE47, \ + NLOHMANN_JSON_PASTE46, \ + NLOHMANN_JSON_PASTE45, \ + NLOHMANN_JSON_PASTE44, \ + NLOHMANN_JSON_PASTE43, \ + NLOHMANN_JSON_PASTE42, \ + NLOHMANN_JSON_PASTE41, \ + NLOHMANN_JSON_PASTE40, \ + NLOHMANN_JSON_PASTE39, \ + NLOHMANN_JSON_PASTE38, \ + NLOHMANN_JSON_PASTE37, \ + NLOHMANN_JSON_PASTE36, \ + NLOHMANN_JSON_PASTE35, \ + NLOHMANN_JSON_PASTE34, \ + NLOHMANN_JSON_PASTE33, \ + NLOHMANN_JSON_PASTE32, \ + NLOHMANN_JSON_PASTE31, \ + NLOHMANN_JSON_PASTE30, \ + NLOHMANN_JSON_PASTE29, \ + NLOHMANN_JSON_PASTE28, \ + NLOHMANN_JSON_PASTE27, \ + NLOHMANN_JSON_PASTE26, \ + NLOHMANN_JSON_PASTE25, \ + NLOHMANN_JSON_PASTE24, \ + NLOHMANN_JSON_PASTE23, \ + NLOHMANN_JSON_PASTE22, \ + NLOHMANN_JSON_PASTE21, \ + NLOHMANN_JSON_PASTE20, \ + NLOHMANN_JSON_PASTE19, \ + NLOHMANN_JSON_PASTE18, \ + NLOHMANN_JSON_PASTE17, \ + NLOHMANN_JSON_PASTE16, \ + NLOHMANN_JSON_PASTE15, \ + NLOHMANN_JSON_PASTE14, \ + NLOHMANN_JSON_PASTE13, \ + NLOHMANN_JSON_PASTE12, \ + NLOHMANN_JSON_PASTE11, \ + NLOHMANN_JSON_PASTE10, \ + NLOHMANN_JSON_PASTE9, \ + NLOHMANN_JSON_PASTE8, \ + NLOHMANN_JSON_PASTE7, \ + NLOHMANN_JSON_PASTE6, \ + NLOHMANN_JSON_PASTE5, \ + NLOHMANN_JSON_PASTE4, \ + NLOHMANN_JSON_PASTE3, \ + NLOHMANN_JSON_PASTE2, \ + NLOHMANN_JSON_PASTE1)(__VA_ARGS__)) +#define NLOHMANN_JSON_PASTE2(func, v1) func(v1) +#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2) +#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3) +#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4) +#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5) +#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6) +#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7) +#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8) +#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9) +#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10) +#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) +#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) +#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) +#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) +#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) +#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) +#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) +#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) +#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) +#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) +#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) +#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) +#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) +#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) +#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) +#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) +#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) +#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) +#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) +#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) +#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) +#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) +#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) +#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) +#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) +#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) +#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) +#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) +#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) +#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) +#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) +#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) +#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) +#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) +#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) +#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) +#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) +#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) +#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) +#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) +#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) +#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) +#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) +#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) +#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) +#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) +#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) +#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) +#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) +#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) +#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) +#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) +#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) + +#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; +#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); +#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1); + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { Type nlohmann_json_default_obj; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { Type nlohmann_json_default_obj; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + + +// inspired from https://stackoverflow.com/a/26745591 +// allows to call any std function as if (e.g. with begin): +// using std::begin; begin(x); +// +// it allows using the detected idiom to retrieve the return type +// of such an expression +#define NLOHMANN_CAN_CALL_STD_FUNC_IMPL(std_name) \ + namespace detail { \ + using std::std_name; \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + } \ + \ + namespace detail2 { \ + struct std_name##_tag \ + { \ + }; \ + \ + template \ + std_name##_tag std_name(T&&...); \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + \ + template \ + struct would_call_std_##std_name \ + { \ + static constexpr auto const value = ::nlohmann::detail:: \ + is_detected_exact::value; \ + }; \ + } /* namespace detail2 */ \ + \ + template \ + struct would_call_std_##std_name : detail2::would_call_std_##std_name \ + { \ + } + +#ifndef JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_USE_IMPLICIT_CONVERSIONS 1 +#endif + +#if JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_EXPLICIT +#else + #define JSON_EXPLICIT explicit +#endif + +#ifndef JSON_DISABLE_ENUM_SERIALIZATION + #define JSON_DISABLE_ENUM_SERIALIZATION 0 +#endif + +#ifndef JSON_USE_GLOBAL_UDLS + #define JSON_USE_GLOBAL_UDLS 1 +#endif + +#if JSON_HAS_THREE_WAY_COMPARISON + #include // partial_ordering +#endif + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/////////////////////////// +// JSON type enumeration // +/////////////////////////// + +/*! +@brief the JSON type enumeration + +This enumeration collects the different JSON types. It is internally used to +distinguish the stored values, and the functions @ref basic_json::is_null(), +@ref basic_json::is_object(), @ref basic_json::is_array(), +@ref basic_json::is_string(), @ref basic_json::is_boolean(), +@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), +@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), +@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and +@ref basic_json::is_structured() rely on it. + +@note There are three enumeration entries (number_integer, number_unsigned, and +number_float), because the library distinguishes these three types for numbers: +@ref basic_json::number_unsigned_t is used for unsigned integers, +@ref basic_json::number_integer_t is used for signed integers, and +@ref basic_json::number_float_t is used for floating-point numbers or to +approximate integers which do not fit in the limits of their respective type. + +@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON +value with the default value for a given type + +@since version 1.0.0 +*/ +enum class value_t : std::uint8_t +{ + null, ///< null value + object, ///< object (unordered set of name/value pairs) + array, ///< array (ordered collection of values) + string, ///< string value + boolean, ///< boolean value + number_integer, ///< number value (signed integer) + number_unsigned, ///< number value (unsigned integer) + number_float, ///< number value (floating-point) + binary, ///< binary array (ordered collection of bytes) + discarded ///< discarded by the parser callback function +}; + +/*! +@brief comparison operator for JSON types + +Returns an ordering that is similar to Python: +- order: null < boolean < number < object < array < string < binary +- furthermore, each type is not smaller than itself +- discarded values are not comparable +- binary is represented as a b"" string in python and directly comparable to a + string; however, making a binary array directly comparable with a string would + be surprising behavior in a JSON file. + +@since version 1.0.0 +*/ +#if JSON_HAS_THREE_WAY_COMPARISON + inline std::partial_ordering operator<=>(const value_t lhs, const value_t rhs) noexcept // *NOPAD* +#else + inline bool operator<(const value_t lhs, const value_t rhs) noexcept +#endif +{ + static constexpr std::array order = {{ + 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, + 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, + 6 /* binary */ + } + }; + + const auto l_index = static_cast(lhs); + const auto r_index = static_cast(rhs); +#if JSON_HAS_THREE_WAY_COMPARISON + if (l_index < order.size() && r_index < order.size()) + { + return order[l_index] <=> order[r_index]; // *NOPAD* + } + return std::partial_ordering::unordered; +#else + return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; +#endif +} + +// GCC selects the built-in operator< over an operator rewritten from +// a user-defined spaceship operator +// Clang, MSVC, and ICC select the rewritten candidate +// (see GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105200) +#if JSON_HAS_THREE_WAY_COMPARISON && defined(__GNUC__) +inline bool operator<(const value_t lhs, const value_t rhs) noexcept +{ + return std::is_lt(lhs <=> rhs); // *NOPAD* +} +#endif + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/*! +@brief replace all occurrences of a substring by another string + +@param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t +@param[in] f the substring to replace with @a t +@param[in] t the string to replace @a f + +@pre The search string @a f must not be empty. **This precondition is +enforced with an assertion.** + +@since version 2.0.0 +*/ +template +inline void replace_substring(StringType& s, const StringType& f, + const StringType& t) +{ + JSON_ASSERT(!f.empty()); + for (auto pos = s.find(f); // find first occurrence of f + pos != StringType::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t, and + pos = s.find(f, pos + t.size())) // find next occurrence of f + {} +} + +/*! + * @brief string escaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to escape + * @return escaped string + * + * Note the order of escaping "~" to "~0" and "/" to "~1" is important. + */ +template +inline StringType escape(StringType s) +{ + replace_substring(s, StringType{"~"}, StringType{"~0"}); + replace_substring(s, StringType{"/"}, StringType{"~1"}); + return s; +} + +/*! + * @brief string unescaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to unescape + * @return unescaped string + * + * Note the order of escaping "~1" to "/" and "~0" to "~" is important. + */ +template +static void unescape(StringType& s) +{ + replace_substring(s, StringType{"~1"}, StringType{"/"}); + replace_substring(s, StringType{"~0"}, StringType{"~"}); +} + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // size_t + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/// struct to capture the start position of the current token +struct position_t +{ + /// the total number of characters read + std::size_t chars_read_total = 0; + /// the number of characters read in the current line + std::size_t chars_read_current_line = 0; + /// the number of lines read + std::size_t lines_read = 0; + + /// conversion to size_t to preserve SAX interface + constexpr operator size_t() const + { + return chars_read_total; + } +}; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-FileCopyrightText: 2018 The Abseil Authors +// SPDX-License-Identifier: MIT + + + +#include // array +#include // size_t +#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type +#include // index_sequence, make_index_sequence, index_sequence_for + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +template +using uncvref_t = typename std::remove_cv::type>::type; + +#ifdef JSON_HAS_CPP_14 + +// the following utilities are natively available in C++14 +using std::enable_if_t; +using std::index_sequence; +using std::make_index_sequence; +using std::index_sequence_for; + +#else + +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; + +// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h +// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0. + +//// START OF CODE FROM GOOGLE ABSEIL + +// integer_sequence +// +// Class template representing a compile-time integer sequence. An instantiation +// of `integer_sequence` has a sequence of integers encoded in its +// type through its template arguments (which is a common need when +// working with C++11 variadic templates). `absl::integer_sequence` is designed +// to be a drop-in replacement for C++14's `std::integer_sequence`. +// +// Example: +// +// template< class T, T... Ints > +// void user_function(integer_sequence); +// +// int main() +// { +// // user_function's `T` will be deduced to `int` and `Ints...` +// // will be deduced to `0, 1, 2, 3, 4`. +// user_function(make_integer_sequence()); +// } +template +struct integer_sequence +{ + using value_type = T; + static constexpr std::size_t size() noexcept + { + return sizeof...(Ints); + } +}; + +// index_sequence +// +// A helper template for an `integer_sequence` of `size_t`, +// `absl::index_sequence` is designed to be a drop-in replacement for C++14's +// `std::index_sequence`. +template +using index_sequence = integer_sequence; + +namespace utility_internal +{ + +template +struct Extend; + +// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency. +template +struct Extend, SeqSize, 0> +{ + using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >; +}; + +template +struct Extend, SeqSize, 1> +{ + using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >; +}; + +// Recursion helper for 'make_integer_sequence'. +// 'Gen::type' is an alias for 'integer_sequence'. +template +struct Gen +{ + using type = + typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type; +}; + +template +struct Gen +{ + using type = integer_sequence; +}; + +} // namespace utility_internal + +// Compile-time sequences of integers + +// make_integer_sequence +// +// This template alias is equivalent to +// `integer_sequence`, and is designed to be a drop-in +// replacement for C++14's `std::make_integer_sequence`. +template +using make_integer_sequence = typename utility_internal::Gen::type; + +// make_index_sequence +// +// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`, +// and is designed to be a drop-in replacement for C++14's +// `std::make_index_sequence`. +template +using make_index_sequence = make_integer_sequence; + +// index_sequence_for +// +// Converts a typename pack into an index sequence of the same length, and +// is designed to be a drop-in replacement for C++14's +// `std::index_sequence_for()` +template +using index_sequence_for = make_index_sequence; + +//// END OF CODE FROM GOOGLE ABSEIL + +#endif + +// dispatch utility (taken from ranges-v3) +template struct priority_tag : priority_tag < N - 1 > {}; +template<> struct priority_tag<0> {}; + +// taken from ranges-v3 +template +struct static_const +{ + static JSON_INLINE_VARIABLE constexpr T value{}; +}; + +#ifndef JSON_HAS_CPP_17 + template + constexpr T static_const::value; +#endif + +template +inline constexpr std::array make_array(Args&& ... args) +{ + return std::array {{static_cast(std::forward(args))...}}; +} + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // numeric_limits +#include // false_type, is_constructible, is_integral, is_same, true_type +#include // declval +#include // tuple + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // random_access_iterator_tag + +// #include + +// #include + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +template +struct iterator_types {}; + +template +struct iterator_types < + It, + void_t> +{ + using difference_type = typename It::difference_type; + using value_type = typename It::value_type; + using pointer = typename It::pointer; + using reference = typename It::reference; + using iterator_category = typename It::iterator_category; +}; + +// This is required as some compilers implement std::iterator_traits in a way that +// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. +template +struct iterator_traits +{ +}; + +template +struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> + : iterator_types +{ +}; + +template +struct iterator_traits::value>> +{ + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; +}; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN + +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin); + +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN + +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end); + +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + +#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ + #define INCLUDE_NLOHMANN_JSON_FWD_HPP_ + + #include // int64_t, uint64_t + #include // map + #include // allocator + #include // string + #include // vector + + // #include + + + /*! + @brief namespace for Niels Lohmann + @see https://github.com/nlohmann + @since version 1.0.0 + */ + NLOHMANN_JSON_NAMESPACE_BEGIN + + /*! + @brief default JSONSerializer template argument + + This serializer ignores the template arguments and uses ADL + ([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) + for serialization. + */ + template + struct adl_serializer; + + /// a class to store JSON values + /// @sa https://json.nlohmann.me/api/basic_json/ + template class ObjectType = + std::map, + template class ArrayType = std::vector, + class StringType = std::string, class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator, + template class JSONSerializer = + adl_serializer, + class BinaryType = std::vector> + class basic_json; + + /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document + /// @sa https://json.nlohmann.me/api/json_pointer/ + template + class json_pointer; + + /*! + @brief default specialization + @sa https://json.nlohmann.me/api/json/ + */ + using json = basic_json<>; + + /// @brief a minimal map-like container that preserves insertion order + /// @sa https://json.nlohmann.me/api/ordered_map/ + template + struct ordered_map; + + /// @brief specialization that maintains the insertion order of object keys + /// @sa https://json.nlohmann.me/api/ordered_json/ + using ordered_json = basic_json; + + NLOHMANN_JSON_NAMESPACE_END + +#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ + + +NLOHMANN_JSON_NAMESPACE_BEGIN +/*! +@brief detail namespace with internal helper functions + +This namespace collects functions that should not be exposed, +implementations of some @ref basic_json methods, and meta-programming helpers. + +@since version 2.1.0 +*/ +namespace detail +{ + +///////////// +// helpers // +///////////// + +// Note to maintainers: +// +// Every trait in this file expects a non CV-qualified type. +// The only exceptions are in the 'aliases for detected' section +// (i.e. those of the form: decltype(T::member_function(std::declval()))) +// +// In this case, T has to be properly CV-qualified to constraint the function arguments +// (e.g. to_json(BasicJsonType&, const T&)) + +template struct is_basic_json : std::false_type {}; + +NLOHMANN_BASIC_JSON_TPL_DECLARATION +struct is_basic_json : std::true_type {}; + +// used by exceptions create() member functions +// true_type for pointer to possibly cv-qualified basic_json or std::nullptr_t +// false_type otherwise +template +struct is_basic_json_context : + std::integral_constant < bool, + is_basic_json::type>::type>::value + || std::is_same::value > +{}; + +////////////////////// +// json_ref helpers // +////////////////////// + +template +class json_ref; + +template +struct is_json_ref : std::false_type {}; + +template +struct is_json_ref> : std::true_type {}; + +////////////////////////// +// aliases for detected // +////////////////////////// + +template +using mapped_type_t = typename T::mapped_type; + +template +using key_type_t = typename T::key_type; + +template +using value_type_t = typename T::value_type; + +template +using difference_type_t = typename T::difference_type; + +template +using pointer_t = typename T::pointer; + +template +using reference_t = typename T::reference; + +template +using iterator_category_t = typename T::iterator_category; + +template +using to_json_function = decltype(T::to_json(std::declval()...)); + +template +using from_json_function = decltype(T::from_json(std::declval()...)); + +template +using get_template_function = decltype(std::declval().template get()); + +// trait checking if JSONSerializer::from_json(json const&, udt&) exists +template +struct has_from_json : std::false_type {}; + +// trait checking if j.get is valid +// use this trait instead of std::is_constructible or std::is_convertible, +// both rely on, or make use of implicit conversions, and thus fail when T +// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958) +template +struct is_getable +{ + static constexpr bool value = is_detected::value; +}; + +template +struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +// This trait checks if JSONSerializer::from_json(json const&) exists +// this overload is used for non-default-constructible user-defined-types +template +struct has_non_default_from_json : std::false_type {}; + +template +struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +// This trait checks if BasicJsonType::json_serializer::to_json exists +// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. +template +struct has_to_json : std::false_type {}; + +template +struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +template +using detect_key_compare = typename T::key_compare; + +template +struct has_key_compare : std::integral_constant::value> {}; + +// obtains the actual object key comparator +template +struct actual_object_comparator +{ + using object_t = typename BasicJsonType::object_t; + using object_comparator_t = typename BasicJsonType::default_object_comparator_t; + using type = typename std::conditional < has_key_compare::value, + typename object_t::key_compare, object_comparator_t>::type; +}; + +template +using actual_object_comparator_t = typename actual_object_comparator::type; + +/////////////////// +// is_ functions // +/////////////////// + +// https://en.cppreference.com/w/cpp/types/conjunction +template struct conjunction : std::true_type { }; +template struct conjunction : B { }; +template +struct conjunction +: std::conditional(B::value), conjunction, B>::type {}; + +// https://en.cppreference.com/w/cpp/types/negation +template struct negation : std::integral_constant < bool, !B::value > { }; + +// Reimplementation of is_constructible and is_default_constructible, due to them being broken for +// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367). +// This causes compile errors in e.g. clang 3.5 or gcc 4.9. +template +struct is_default_constructible : std::is_default_constructible {}; + +template +struct is_default_constructible> + : conjunction, is_default_constructible> {}; + +template +struct is_default_constructible> + : conjunction, is_default_constructible> {}; + +template +struct is_default_constructible> + : conjunction...> {}; + +template +struct is_default_constructible> + : conjunction...> {}; + + +template +struct is_constructible : std::is_constructible {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + + +template +struct is_iterator_traits : std::false_type {}; + +template +struct is_iterator_traits> +{ + private: + using traits = iterator_traits; + + public: + static constexpr auto value = + is_detected::value && + is_detected::value && + is_detected::value && + is_detected::value && + is_detected::value; +}; + +template +struct is_range +{ + private: + using t_ref = typename std::add_lvalue_reference::type; + + using iterator = detected_t; + using sentinel = detected_t; + + // to be 100% correct, it should use https://en.cppreference.com/w/cpp/iterator/input_or_output_iterator + // and https://en.cppreference.com/w/cpp/iterator/sentinel_for + // but reimplementing these would be too much work, as a lot of other concepts are used underneath + static constexpr auto is_iterator_begin = + is_iterator_traits>::value; + + public: + static constexpr bool value = !std::is_same::value && !std::is_same::value && is_iterator_begin; +}; + +template +using iterator_t = enable_if_t::value, result_of_begin())>>; + +template +using range_value_t = value_type_t>>; + +// The following implementation of is_complete_type is taken from +// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ +// and is written by Xiang Fan who agreed to using it in this library. + +template +struct is_complete_type : std::false_type {}; + +template +struct is_complete_type : std::true_type {}; + +template +struct is_compatible_object_type_impl : std::false_type {}; + +template +struct is_compatible_object_type_impl < + BasicJsonType, CompatibleObjectType, + enable_if_t < is_detected::value&& + is_detected::value >> +{ + using object_t = typename BasicJsonType::object_t; + + // macOS's is_constructible does not play well with nonesuch... + static constexpr bool value = + is_constructible::value && + is_constructible::value; +}; + +template +struct is_compatible_object_type + : is_compatible_object_type_impl {}; + +template +struct is_constructible_object_type_impl : std::false_type {}; + +template +struct is_constructible_object_type_impl < + BasicJsonType, ConstructibleObjectType, + enable_if_t < is_detected::value&& + is_detected::value >> +{ + using object_t = typename BasicJsonType::object_t; + + static constexpr bool value = + (is_default_constructible::value && + (std::is_move_assignable::value || + std::is_copy_assignable::value) && + (is_constructible::value && + std::is_same < + typename object_t::mapped_type, + typename ConstructibleObjectType::mapped_type >::value)) || + (has_from_json::value || + has_non_default_from_json < + BasicJsonType, + typename ConstructibleObjectType::mapped_type >::value); +}; + +template +struct is_constructible_object_type + : is_constructible_object_type_impl {}; + +template +struct is_compatible_string_type +{ + static constexpr auto value = + is_constructible::value; +}; + +template +struct is_constructible_string_type +{ + // launder type through decltype() to fix compilation failure on ICPC +#ifdef __INTEL_COMPILER + using laundered_type = decltype(std::declval()); +#else + using laundered_type = ConstructibleStringType; +#endif + + static constexpr auto value = + conjunction < + is_constructible, + is_detected_exact>::value; +}; + +template +struct is_compatible_array_type_impl : std::false_type {}; + +template +struct is_compatible_array_type_impl < + BasicJsonType, CompatibleArrayType, + enable_if_t < + is_detected::value&& + is_iterator_traits>>::value&& +// special case for types like std::filesystem::path whose iterator's value_type are themselves +// c.f. https://github.com/nlohmann/json/pull/3073 + !std::is_same>::value >> +{ + static constexpr bool value = + is_constructible>::value; +}; + +template +struct is_compatible_array_type + : is_compatible_array_type_impl {}; + +template +struct is_constructible_array_type_impl : std::false_type {}; + +template +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t::value >> + : std::true_type {}; + +template +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t < !std::is_same::value&& + !is_compatible_string_type::value&& + is_default_constructible::value&& +(std::is_move_assignable::value || + std::is_copy_assignable::value)&& +is_detected::value&& +is_iterator_traits>>::value&& +is_detected::value&& +// special case for types like std::filesystem::path whose iterator's value_type are themselves +// c.f. https://github.com/nlohmann/json/pull/3073 +!std::is_same>::value&& + is_complete_type < + detected_t>::value >> +{ + using value_type = range_value_t; + + static constexpr bool value = + std::is_same::value || + has_from_json::value || + has_non_default_from_json < + BasicJsonType, + value_type >::value; +}; + +template +struct is_constructible_array_type + : is_constructible_array_type_impl {}; + +template +struct is_compatible_integer_type_impl : std::false_type {}; + +template +struct is_compatible_integer_type_impl < + RealIntegerType, CompatibleNumberIntegerType, + enable_if_t < std::is_integral::value&& + std::is_integral::value&& + !std::is_same::value >> +{ + // is there an assert somewhere on overflows? + using RealLimits = std::numeric_limits; + using CompatibleLimits = std::numeric_limits; + + static constexpr auto value = + is_constructible::value && + CompatibleLimits::is_integer && + RealLimits::is_signed == CompatibleLimits::is_signed; +}; + +template +struct is_compatible_integer_type + : is_compatible_integer_type_impl {}; + +template +struct is_compatible_type_impl: std::false_type {}; + +template +struct is_compatible_type_impl < + BasicJsonType, CompatibleType, + enable_if_t::value >> +{ + static constexpr bool value = + has_to_json::value; +}; + +template +struct is_compatible_type + : is_compatible_type_impl {}; + +template +struct is_constructible_tuple : std::false_type {}; + +template +struct is_constructible_tuple> : conjunction...> {}; + +template +struct is_json_iterator_of : std::false_type {}; + +template +struct is_json_iterator_of : std::true_type {}; + +template +struct is_json_iterator_of : std::true_type +{}; + +// checks if a given type T is a template specialization of Primary +template