mirror of
https://github.com/ggerganov/whisper.cpp.git
synced 2025-06-26 18:03:21 +00:00
Compare commits
2 Commits
gg/fix-ext
...
fix-bench
Author | SHA1 | Date | |
---|---|---|---|
09a6325de5 | |||
39c4fc59dd |
@ -1,28 +0,0 @@
|
|||||||
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
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
# Set nvcc architecture
|
|
||||||
ENV CUDA_DOCKER_ARCH=${CUDA_DOCKER_ARCH}
|
|
||||||
# Enable cuBLAS
|
|
||||||
ENV WHISPER_CUBLAS=1
|
|
||||||
|
|
||||||
RUN make
|
|
||||||
|
|
||||||
ENTRYPOINT ["/app/main"]
|
|
@ -1,38 +0,0 @@
|
|||||||
ARG UBUNTU_VERSION=22.04
|
|
||||||
# This needs to generally match the container host's environment.
|
|
||||||
ARG CUDA_VERSION=12.3.1
|
|
||||||
# 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}
|
|
||||||
# Enable cuBLAS
|
|
||||||
ENV WHISPER_CUBLAS=1
|
|
||||||
|
|
||||||
RUN apt-get update && \
|
|
||||||
apt-get install -y build-essential \
|
|
||||||
&& rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*
|
|
||||||
|
|
||||||
# Ref: https://stackoverflow.com/a/53464012
|
|
||||||
ENV CUDA_MAIN_VERSION=12.3
|
|
||||||
ENV LD_LIBRARY_PATH /usr/local/cuda-${CUDA_MAIN_VERSION}/compat:$LD_LIBRARY_PATH
|
|
||||||
|
|
||||||
COPY .. .
|
|
||||||
RUN make
|
|
||||||
|
|
||||||
FROM ${BASE_CUDA_RUN_CONTAINER} AS runtime
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
RUN apt-get update && \
|
|
||||||
apt-get install -y curl ffmpeg \
|
|
||||||
&& rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*
|
|
||||||
|
|
||||||
COPY --from=build /app /app
|
|
||||||
ENTRYPOINT [ "bash", "-c" ]
|
|
@ -1,19 +0,0 @@
|
|||||||
FROM ubuntu:22.04 AS build
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
RUN apt-get update && \
|
|
||||||
apt-get install -y build-essential \
|
|
||||||
&& rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*
|
|
||||||
|
|
||||||
COPY .. .
|
|
||||||
RUN make
|
|
||||||
|
|
||||||
FROM ubuntu:22.04 AS runtime
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
RUN apt-get update && \
|
|
||||||
apt-get install -y curl ffmpeg \
|
|
||||||
&& rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*
|
|
||||||
|
|
||||||
COPY --from=build /app /app
|
|
||||||
ENTRYPOINT [ "bash", "-c" ]
|
|
117
.github/workflows/build.yml
vendored
117
.github/workflows/build.yml
vendored
@ -25,7 +25,6 @@ jobs:
|
|||||||
docker run --platform ${{ matrix.arch }} --rm \
|
docker run --platform ${{ matrix.arch }} --rm \
|
||||||
-v ${{ github.workspace }}:/workspace \
|
-v ${{ github.workspace }}:/workspace \
|
||||||
-w /workspace ${{ env.ubuntu_image }} /bin/sh -c '
|
-w /workspace ${{ env.ubuntu_image }} /bin/sh -c '
|
||||||
set -e
|
|
||||||
apt update
|
apt update
|
||||||
apt install -y build-essential libsdl2-dev
|
apt install -y build-essential libsdl2-dev
|
||||||
make
|
make
|
||||||
@ -87,10 +86,9 @@ jobs:
|
|||||||
docker run --platform ${{ matrix.arch }} --rm \
|
docker run --platform ${{ matrix.arch }} --rm \
|
||||||
-v ${{ github.workspace }}:/workspace \
|
-v ${{ github.workspace }}:/workspace \
|
||||||
-w /workspace ${{ env.ubuntu_image }} /bin/sh -c '
|
-w /workspace ${{ env.ubuntu_image }} /bin/sh -c '
|
||||||
set -e
|
|
||||||
apt update
|
apt update
|
||||||
apt install -y build-essential cmake libsdl2-dev
|
apt install -y build-essential cmake libsdl2-dev
|
||||||
cmake . -DWHISPER_SDL2=ON -DCMAKE_BUILD_TYPE=${{ matrix.build }}
|
cmake . -DWHISPER_SUPPORT_SDL2=ON -DCMAKE_BUILD_TYPE=${{ matrix.build }}
|
||||||
make
|
make
|
||||||
ctest -L gh --output-on-failure'
|
ctest -L gh --output-on-failure'
|
||||||
|
|
||||||
@ -115,10 +113,9 @@ jobs:
|
|||||||
docker run --platform ${{ matrix.arch }} --rm \
|
docker run --platform ${{ matrix.arch }} --rm \
|
||||||
-v ${{ github.workspace }}:/workspace \
|
-v ${{ github.workspace }}:/workspace \
|
||||||
-w /workspace ${{ env.ubuntu_image }} /bin/sh -c '
|
-w /workspace ${{ env.ubuntu_image }} /bin/sh -c '
|
||||||
set -e
|
|
||||||
apt update
|
apt update
|
||||||
apt install -y clang build-essential cmake libsdl2-dev
|
apt install -y build-essential cmake libsdl2-dev
|
||||||
cmake . -DWHISPER_SDL2=ON -DCMAKE_BUILD_TYPE=${{ matrix.build }} -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang
|
cmake . -DWHISPER_SUPPORT_SDL2=ON -DCMAKE_BUILD_TYPE=${{ matrix.build }} -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang
|
||||||
make
|
make
|
||||||
ctest -L gh --output-on-failure'
|
ctest -L gh --output-on-failure'
|
||||||
|
|
||||||
@ -143,7 +140,6 @@ jobs:
|
|||||||
docker run --platform ${{ matrix.arch }} --rm \
|
docker run --platform ${{ matrix.arch }} --rm \
|
||||||
-v ${{ github.workspace }}:/workspace \
|
-v ${{ github.workspace }}:/workspace \
|
||||||
-w /workspace ${{ env.ubuntu_image }} /bin/sh -c '
|
-w /workspace ${{ env.ubuntu_image }} /bin/sh -c '
|
||||||
set -e
|
|
||||||
apt update
|
apt update
|
||||||
apt install -y build-essential cmake
|
apt install -y build-essential cmake
|
||||||
cmake . -DCMAKE_BUILD_TYPE=Debug -DWHISPER_SANITIZE_${{ matrix.sanitizer }}=ON
|
cmake . -DCMAKE_BUILD_TYPE=Debug -DWHISPER_SANITIZE_${{ matrix.sanitizer }}=ON
|
||||||
@ -166,7 +162,7 @@ jobs:
|
|||||||
s2arc: x64
|
s2arc: x64
|
||||||
jnaPath: win32-x86-64
|
jnaPath: win32-x86-64
|
||||||
- sdl2: ON
|
- sdl2: ON
|
||||||
s2ver: 2.28.5
|
s2ver: 2.26.0
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Clone
|
- name: Clone
|
||||||
@ -186,7 +182,7 @@ jobs:
|
|||||||
run: >
|
run: >
|
||||||
cmake -S . -B ./build -A ${{ matrix.arch }}
|
cmake -S . -B ./build -A ${{ matrix.arch }}
|
||||||
-DCMAKE_BUILD_TYPE=${{ matrix.build }}
|
-DCMAKE_BUILD_TYPE=${{ matrix.build }}
|
||||||
-DWHISPER_SDL2=${{ matrix.sdl2 }}
|
-DWHISPER_SUPPORT_SDL2=${{ matrix.sdl2 }}
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
@ -221,16 +217,13 @@ jobs:
|
|||||||
sdl2: [ON]
|
sdl2: [ON]
|
||||||
include:
|
include:
|
||||||
- arch: Win32
|
- arch: Win32
|
||||||
obzip: https://github.com/OpenMathLib/OpenBLAS/releases/download/v0.3.25/OpenBLAS-0.3.25-x86.zip
|
obzip: https://github.com/xianyi/OpenBLAS/releases/download/v0.3.21/OpenBLAS-0.3.21-x86.zip
|
||||||
s2arc: x86
|
s2arc: x86
|
||||||
clblast: OFF
|
|
||||||
- arch: x64
|
- arch: x64
|
||||||
obzip: https://github.com/OpenMathLib/OpenBLAS/releases/download/v0.3.25/OpenBLAS-0.3.25-x64.zip
|
obzip: https://github.com/xianyi/OpenBLAS/releases/download/v0.3.21/OpenBLAS-0.3.21-x64.zip
|
||||||
s2arc: x64
|
s2arc: x64
|
||||||
clblast: ON
|
|
||||||
clver: 1.6.1
|
|
||||||
- sdl2: ON
|
- sdl2: ON
|
||||||
s2ver: 2.28.5
|
s2ver: 2.26.0
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Clone
|
- name: Clone
|
||||||
@ -246,7 +239,7 @@ jobs:
|
|||||||
7z x blas.zip -oblas -y
|
7z x blas.zip -oblas -y
|
||||||
copy blas/include/cblas.h .
|
copy blas/include/cblas.h .
|
||||||
copy blas/include/openblas_config.h .
|
copy blas/include/openblas_config.h .
|
||||||
echo "OPENBLAS_PATH=$env:GITHUB_WORKSPACE/blas" >> $env:GITHUB_ENV
|
echo "blasdir=$env:GITHUB_WORKSPACE/blas" >> $env:GITHUB_ENV
|
||||||
|
|
||||||
- name: Fetch SDL2 and set SDL2_DIR
|
- name: Fetch SDL2 and set SDL2_DIR
|
||||||
if: matrix.sdl2 == 'ON'
|
if: matrix.sdl2 == 'ON'
|
||||||
@ -255,26 +248,13 @@ jobs:
|
|||||||
7z x sdl2.zip
|
7z x sdl2.zip
|
||||||
echo "SDL2_DIR=$env:GITHUB_WORKSPACE/SDL2-${{ matrix.s2ver }}/cmake" >> $env:GITHUB_ENV
|
echo "SDL2_DIR=$env:GITHUB_WORKSPACE/SDL2-${{ matrix.s2ver }}/cmake" >> $env:GITHUB_ENV
|
||||||
|
|
||||||
- name: Install OpenCL
|
|
||||||
if: matrix.clblast == 'ON'
|
|
||||||
run: vcpkg.exe --triplet=${{ matrix.arch }}-windows install opencl
|
|
||||||
|
|
||||||
- name: Fetch CLBlast and set CLBlast_DIR
|
|
||||||
if: matrix.clblast == 'ON'
|
|
||||||
run: |
|
|
||||||
C:/msys64/usr/bin/wget.exe -qO clblast.zip https://github.com/CNugteren/CLBlast/releases/download/${{ matrix.clver }}/CLBlast-${{ matrix.clver }}-windows-x64.zip
|
|
||||||
7z x clblast.zip
|
|
||||||
7z x CLBlast-${{ matrix.clver }}-windows-x64.7z
|
|
||||||
echo "CLBlast_DIR=$env:GITHUB_WORKSPACE/CLBlast-${{ matrix.clver }}-windows-x64/lib/cmake/CLBlast" >> $env:GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Configure
|
- name: Configure
|
||||||
run: >
|
run: >
|
||||||
cmake -S . -B ./build -A ${{ matrix.arch }}
|
cmake -S . -B ./build -A ${{ matrix.arch }}
|
||||||
-DCMAKE_BUILD_TYPE=${{ matrix.build }}
|
-DCMAKE_BUILD_TYPE=${{ matrix.build }}
|
||||||
-DWHISPER_OPENBLAS=${{ matrix.blas }}
|
-DWHISPER_SUPPORT_OPENBLAS=${{ matrix.blas }}
|
||||||
-DCMAKE_LIBRARY_PATH="$env:OPENBLAS_PATH/lib"
|
-DCMAKE_LIBRARY_PATH="$env:blasdir/lib"
|
||||||
-DWHISPER_SDL2=${{ matrix.sdl2 }}
|
-DWHISPER_SUPPORT_SDL2=${{ matrix.sdl2 }}
|
||||||
-DWHISPER_CLBLAST=${{ matrix.clblast }}
|
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
@ -283,21 +263,17 @@ jobs:
|
|||||||
|
|
||||||
- name: Copy libopenblas.dll
|
- name: Copy libopenblas.dll
|
||||||
if: matrix.blas == 'ON'
|
if: matrix.blas == 'ON'
|
||||||
run: copy "$env:OPENBLAS_PATH/bin/libopenblas.dll" build/bin/${{ matrix.build }}
|
run: copy "$env:blasdir/bin/libopenblas.dll" build/bin/${{ matrix.build }}
|
||||||
|
|
||||||
- name: Copy SDL2.dll
|
- name: Copy SDL2.dll
|
||||||
if: matrix.sdl2 == 'ON'
|
if: matrix.sdl2 == 'ON'
|
||||||
run: copy "$env:SDL2_DIR/../lib/${{ matrix.s2arc }}/SDL2.dll" build/bin/${{ matrix.build }}
|
run: copy "$env:SDL2_DIR/../lib/${{ matrix.s2arc }}/SDL2.dll" build/bin/${{ matrix.build }}
|
||||||
|
|
||||||
- name: Copy clblast.dll
|
|
||||||
if: matrix.clblast == 'ON'
|
|
||||||
run: copy "$env:CLBlast_DIR/../../clblast.dll" build/bin/${{ matrix.build }}
|
|
||||||
|
|
||||||
- name: Upload binaries
|
- name: Upload binaries
|
||||||
if: matrix.blas == 'ON' && matrix.sdl2 == 'ON'
|
if: matrix.blas == 'ON' && matrix.sdl2 == 'ON'
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v1
|
||||||
with:
|
with:
|
||||||
name: whisper-blas${{ matrix.clblast == 'ON' && '-clblast' || ''}}-bin-${{ matrix.arch }}
|
name: whisper-blas-bin-${{ matrix.arch }}
|
||||||
path: build/bin/${{ matrix.build }}
|
path: build/bin/${{ matrix.build }}
|
||||||
|
|
||||||
windows-cublas:
|
windows-cublas:
|
||||||
@ -309,12 +285,11 @@ jobs:
|
|||||||
arch: [x64]
|
arch: [x64]
|
||||||
cublas: [ON]
|
cublas: [ON]
|
||||||
sdl2: [ON]
|
sdl2: [ON]
|
||||||
cuda-toolkit: [12.2.0, 11.8.0]
|
|
||||||
include:
|
include:
|
||||||
- arch: x64
|
- arch: x64
|
||||||
s2arc: x64
|
s2arc: x64
|
||||||
- sdl2: ON
|
- sdl2: ON
|
||||||
s2ver: 2.28.5
|
s2ver: 2.26.0
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Clone
|
- name: Clone
|
||||||
@ -325,9 +300,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Install CUDA Toolkit
|
- name: Install CUDA Toolkit
|
||||||
id: cuda-toolkit
|
id: cuda-toolkit
|
||||||
uses: Jimver/cuda-toolkit@v0.2.11
|
uses: Jimver/cuda-toolkit@v0.2.10
|
||||||
with:
|
|
||||||
cuda: '${{ matrix.cuda-toolkit }}'
|
|
||||||
|
|
||||||
- name: Fetch SDL2 and set SDL2_DIR
|
- name: Fetch SDL2 and set SDL2_DIR
|
||||||
if: matrix.sdl2 == 'ON'
|
if: matrix.sdl2 == 'ON'
|
||||||
@ -340,20 +313,12 @@ jobs:
|
|||||||
run: >
|
run: >
|
||||||
cmake -S . -B ./build -A ${{ matrix.arch }}
|
cmake -S . -B ./build -A ${{ matrix.arch }}
|
||||||
-DCMAKE_BUILD_TYPE=${{ matrix.build }}
|
-DCMAKE_BUILD_TYPE=${{ matrix.build }}
|
||||||
-DWHISPER_CUBLAS=${{ matrix.cublas }}
|
-DWHISPER_CUBLAS=1
|
||||||
-DWHISPER_SDL2=${{ matrix.sdl2 }}
|
|
||||||
|
|
||||||
- name: Build ${{ matrix.cuda-toolkit }}
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
cd ./build
|
cd ./build
|
||||||
cmake --build . --config ${{ matrix.build }}
|
msbuild ALL_BUILD.vcxproj -t:build -p:configuration=${{ matrix.build }} -p:platform=${{ matrix.arch }}
|
||||||
|
|
||||||
- name: Copy CUDA DLLs
|
|
||||||
run: >
|
|
||||||
Copy-Item -PassThru
|
|
||||||
-Path "${{ steps.cuda-toolkit.outputs.CUDA_PATH }}/bin/*.dll"
|
|
||||||
-Include cudart64_*,cublas64_*,cublasLt64_*
|
|
||||||
-Destination build/bin/${{ matrix.build }}
|
|
||||||
|
|
||||||
- name: Copy SDL2.dll
|
- name: Copy SDL2.dll
|
||||||
if: matrix.sdl2 == 'ON'
|
if: matrix.sdl2 == 'ON'
|
||||||
@ -363,7 +328,7 @@ jobs:
|
|||||||
if: matrix.sdl2 == 'ON'
|
if: matrix.sdl2 == 'ON'
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v1
|
||||||
with:
|
with:
|
||||||
name: whisper-cublas-${{ matrix.cuda-toolkit }}-bin-${{ matrix.arch }}
|
name: whisper-cublas-bin-${{ matrix.arch }}
|
||||||
path: build/bin/${{ matrix.build }}
|
path: build/bin/${{ matrix.build }}
|
||||||
|
|
||||||
emscripten:
|
emscripten:
|
||||||
@ -416,14 +381,6 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Clone
|
- name: Clone
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
|
||||||
path: whisper
|
|
||||||
|
|
||||||
- name: Clone
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
repository: ggerganov/ggml
|
|
||||||
path: ggml
|
|
||||||
|
|
||||||
- name: Install Java
|
- name: Install Java
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v3
|
||||||
@ -436,41 +393,9 @@ jobs:
|
|||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
cd whisper/examples/whisper.android
|
cd examples/whisper.android
|
||||||
./gradlew assembleRelease --no-daemon
|
./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 -PGGML_HOME=$PATH_TO_GGML
|
|
||||||
|
|
||||||
android_java:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Clone
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: set up JDK 11
|
|
||||||
uses: actions/setup-java@v3
|
|
||||||
with:
|
|
||||||
java-version: '11'
|
|
||||||
distribution: 'temurin'
|
|
||||||
cache: gradle
|
|
||||||
|
|
||||||
- name: Setup Android SDK
|
|
||||||
uses: android-actions/setup-android@v2
|
|
||||||
with:
|
|
||||||
api-level: 30
|
|
||||||
build-tools-version: 30.0.3
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
cd examples/whisper.android.java
|
|
||||||
chmod +x ./gradlew
|
|
||||||
./gradlew assembleRelease
|
|
||||||
|
|
||||||
java:
|
java:
|
||||||
needs: [ 'windows' ]
|
needs: [ 'windows' ]
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
|
57
.github/workflows/docker.yml
vendored
57
.github/workflows/docker.yml
vendored
@ -1,57 +0,0 @@
|
|||||||
name: Publish Docker image
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
push_to_registry:
|
|
||||||
name: Push Docker image to Docker Hub
|
|
||||||
if: github.event.pull_request.draft == false
|
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
env:
|
|
||||||
COMMIT_SHA: ${{ github.sha }}
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
config:
|
|
||||||
- { tag: "main", dockerfile: ".devops/main.Dockerfile", platform: "linux/amd64,linux/arm64" }
|
|
||||||
- { tag: "main-cuda", dockerfile: ".devops/main-cuda.Dockerfile", platform: "linux/amd64" }
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Check out the repo
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Log in to Docker Hub
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.repository_owner }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Build and push Docker image (versioned)
|
|
||||||
if: github.event_name == 'push'
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
push: true
|
|
||||||
platforms: ${{ matrix.config.platforms }}
|
|
||||||
tags: "ghcr.io/${{ github.repository }}:${{ matrix.config.tag }}-${{ env.COMMIT_SHA }}"
|
|
||||||
file: ${{ matrix.config.dockerfile }}
|
|
||||||
|
|
||||||
- name: Build and push Docker image (tagged)
|
|
||||||
uses: docker/build-push-action@v4
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
push: ${{ github.event_name == 'push' }}
|
|
||||||
platforms: ${{ matrix.config.platforms }}
|
|
||||||
tags: "ghcr.io/${{ github.repository }}:${{ matrix.config.tag }}"
|
|
||||||
file: ${{ matrix.config.dockerfile }}
|
|
13
.gitignore
vendored
13
.gitignore
vendored
@ -8,7 +8,6 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
build/
|
build/
|
||||||
build-coreml/
|
|
||||||
build-em/
|
build-em/
|
||||||
build-debug/
|
build-debug/
|
||||||
build-release/
|
build-release/
|
||||||
@ -19,11 +18,6 @@ build-no-accel/
|
|||||||
build-sanitize-addr/
|
build-sanitize-addr/
|
||||||
build-sanitize-thread/
|
build-sanitize-thread/
|
||||||
|
|
||||||
# SPM
|
|
||||||
.build/
|
|
||||||
.swiftpm
|
|
||||||
*.metallib
|
|
||||||
|
|
||||||
/main
|
/main
|
||||||
/stream
|
/stream
|
||||||
/command
|
/command
|
||||||
@ -31,7 +25,6 @@ build-sanitize-thread/
|
|||||||
/talk-llama
|
/talk-llama
|
||||||
/bench
|
/bench
|
||||||
/quantize
|
/quantize
|
||||||
/server
|
|
||||||
/lsp
|
/lsp
|
||||||
|
|
||||||
arm_neon.h
|
arm_neon.h
|
||||||
@ -53,9 +46,3 @@ models/*.mlpackage
|
|||||||
bindings/java/.gradle/
|
bindings/java/.gradle/
|
||||||
bindings/java/.idea/
|
bindings/java/.idea/
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
benchmark_results.csv
|
|
||||||
cmake-build-debug/
|
|
||||||
.cxx/
|
|
||||||
.gradle/
|
|
||||||
local.properties
|
|
131
CMakeLists.txt
131
CMakeLists.txt
@ -1,6 +1,6 @@
|
|||||||
cmake_minimum_required (VERSION 3.5)
|
cmake_minimum_required (VERSION 3.0)
|
||||||
|
|
||||||
project(whisper.cpp VERSION 1.5.4)
|
project(whisper.cpp VERSION 1.4.2)
|
||||||
|
|
||||||
# Add path to modules
|
# Add path to modules
|
||||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/")
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/")
|
||||||
@ -35,12 +35,6 @@ endif()
|
|||||||
|
|
||||||
# options
|
# options
|
||||||
|
|
||||||
if (APPLE)
|
|
||||||
set(WHISPER_METAL_DEFAULT ON)
|
|
||||||
else()
|
|
||||||
set(WHISPER_METAL_DEFAULT OFF)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
option(BUILD_SHARED_LIBS "whisper: build shared libs" ${BUILD_SHARED_LIBS_DEFAULT})
|
option(BUILD_SHARED_LIBS "whisper: build shared libs" ${BUILD_SHARED_LIBS_DEFAULT})
|
||||||
|
|
||||||
option(WHISPER_ALL_WARNINGS "whisper: enable all compiler warnings" ON)
|
option(WHISPER_ALL_WARNINGS "whisper: enable all compiler warnings" ON)
|
||||||
@ -64,11 +58,8 @@ option(WHISPER_OPENVINO "whisper: support for OpenVINO" OFF)
|
|||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
option(WHISPER_NO_ACCELERATE "whisper: disable Accelerate framework" OFF)
|
option(WHISPER_NO_ACCELERATE "whisper: disable Accelerate framework" OFF)
|
||||||
option(WHISPER_METAL "whisper: use Metal" ${WHISPER_METAL_DEFAULT})
|
|
||||||
option(WHISPER_METAL_NDEBUG "whisper: disable Metal debugging" OFF)
|
|
||||||
option(WHISPER_COREML "whisper: enable Core ML framework" OFF)
|
option(WHISPER_COREML "whisper: enable Core ML framework" OFF)
|
||||||
option(WHISPER_COREML_ALLOW_FALLBACK "whisper: allow non-CoreML fallback" OFF)
|
option(WHISPER_COREML_ALLOW_FALLBACK "whisper: allow non-CoreML fallback" OFF)
|
||||||
option(WHISPER_METAL_EMBED_LIBRARY "whisper: embed Metal library" OFF)
|
|
||||||
else()
|
else()
|
||||||
option(WHISPER_BLAS "whisper: use BLAS libraries" OFF)
|
option(WHISPER_BLAS "whisper: use BLAS libraries" OFF)
|
||||||
option(WHISPER_BLAS_VENDOR "whisper: BLAS library vendor" Generic)
|
option(WHISPER_BLAS_VENDOR "whisper: BLAS library vendor" Generic)
|
||||||
@ -118,59 +109,7 @@ if (APPLE)
|
|||||||
set(WHISPER_EXTRA_LIBS ${WHISPER_EXTRA_LIBS} ${ACCELERATE_FRAMEWORK})
|
set(WHISPER_EXTRA_LIBS ${WHISPER_EXTRA_LIBS} ${ACCELERATE_FRAMEWORK})
|
||||||
set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -DGGML_USE_ACCELERATE)
|
set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -DGGML_USE_ACCELERATE)
|
||||||
else()
|
else()
|
||||||
message(FATAL_ERROR "Accelerate framework not found")
|
message(WARNING "Accelerate framework not found")
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (WHISPER_METAL)
|
|
||||||
find_library(FOUNDATION_LIBRARY Foundation REQUIRED)
|
|
||||||
find_library(METAL_FRAMEWORK Metal REQUIRED)
|
|
||||||
find_library(METALKIT_FRAMEWORK MetalKit REQUIRED)
|
|
||||||
|
|
||||||
if (METAL_FRAMEWORK)
|
|
||||||
message(STATUS "Metal framework found")
|
|
||||||
|
|
||||||
set(WHISPER_EXTRA_LIBS ${WHISPER_EXTRA_LIBS}
|
|
||||||
${FOUNDATION_LIBRARY}
|
|
||||||
${METAL_FRAMEWORK}
|
|
||||||
${METALKIT_FRAMEWORK}
|
|
||||||
)
|
|
||||||
set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -DGGML_USE_METAL)
|
|
||||||
|
|
||||||
if (WHISPER_METAL_NDEBUG)
|
|
||||||
set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -DGGML_METAL_NDEBUG)
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR "Metal framework not found")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(GGML_SOURCES_METAL ggml-metal.m ggml-metal.h)
|
|
||||||
|
|
||||||
# copy ggml-metal.metal to bin directory
|
|
||||||
configure_file(ggml-metal.metal bin/ggml-metal.metal COPYONLY)
|
|
||||||
|
|
||||||
if (WHISPER_METAL_EMBED_LIBRARY)
|
|
||||||
enable_language(ASM)
|
|
||||||
set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -DGGML_METAL_EMBED_LIBRARY)
|
|
||||||
|
|
||||||
set(METALLIB_SOURCE "${CMAKE_SOURCE_DIR}/ggml-metal.metal")
|
|
||||||
|
|
||||||
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/autogenerated")
|
|
||||||
set(EMBED_METALLIB_ASSEMBLY "${CMAKE_BINARY_DIR}/autogenerated/ggml-embed-metallib.s")
|
|
||||||
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT ${EMBED_METALLIB_ASSEMBLY}
|
|
||||||
COMMAND echo ".section __DATA,__ggml_metallib" > ${EMBED_METALLIB_ASSEMBLY}
|
|
||||||
COMMAND echo ".globl _ggml_metallib_start" >> ${EMBED_METALLIB_ASSEMBLY}
|
|
||||||
COMMAND echo "_ggml_metallib_start:" >> ${EMBED_METALLIB_ASSEMBLY}
|
|
||||||
COMMAND echo ".incbin \\\"${METALLIB_SOURCE}\\\"" >> ${EMBED_METALLIB_ASSEMBLY}
|
|
||||||
COMMAND echo ".globl _ggml_metallib_end" >> ${EMBED_METALLIB_ASSEMBLY}
|
|
||||||
COMMAND echo "_ggml_metallib_end:" >> ${EMBED_METALLIB_ASSEMBLY}
|
|
||||||
DEPENDS ${METALLIB_SOURCE}
|
|
||||||
COMMENT "Generate assembly for embedded Metal library"
|
|
||||||
)
|
|
||||||
|
|
||||||
set(GGML_SOURCES_METAL ${GGML_SOURCES_METAL} ${EMBED_METALLIB_ASSEMBLY})
|
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -183,7 +122,7 @@ if (APPLE)
|
|||||||
|
|
||||||
set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -DWHISPER_USE_COREML)
|
set(WHISPER_EXTRA_FLAGS ${WHISPER_EXTRA_FLAGS} -DWHISPER_USE_COREML)
|
||||||
else()
|
else()
|
||||||
message(FATAL_ERROR "CoreML framework not found")
|
message(WARNING "CoreML framework not found")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (WHISPER_COREML_ALLOW_FALLBACK)
|
if (WHISPER_COREML_ALLOW_FALLBACK)
|
||||||
@ -206,13 +145,13 @@ if (WHISPER_BLAS)
|
|||||||
include_directories($ENV{OPENBLAS_PATH}/include)
|
include_directories($ENV{OPENBLAS_PATH}/include)
|
||||||
set(WHISPER_EXTRA_LIBS ${WHISPER_EXTRA_LIBS} ${BLAS_LIBRARIES})
|
set(WHISPER_EXTRA_LIBS ${WHISPER_EXTRA_LIBS} ${BLAS_LIBRARIES})
|
||||||
else ()
|
else ()
|
||||||
message(FATAL_ERROR "BLAS library was not found. Environment variable OPENBLAS_PATH not defined.")
|
message(WARNING "BLAS library was not found. Environment variable OPENBLAS_PATH not defined.")
|
||||||
endif ()
|
endif ()
|
||||||
else ()
|
else ()
|
||||||
set(BLA_STATIC 1)
|
set(BLA_STATIC 1)
|
||||||
set(BLA_VENDOR ${WHISPER_BLAS_VENDOR})
|
set(BLA_VENDOR ${WHISPER_BLAS_VENDOR})
|
||||||
|
# set(BLA_PREFER_PKGCONFIG 1)
|
||||||
set(BLA_SIZEOF_INTEGER 8)
|
set(BLA_SIZEOF_INTEGER 8)
|
||||||
set(BLA_PREFER_PKGCONFIG 1)
|
|
||||||
find_package(BLAS)
|
find_package(BLAS)
|
||||||
|
|
||||||
if(BLAS_FOUND)
|
if(BLAS_FOUND)
|
||||||
@ -223,7 +162,7 @@ if (WHISPER_BLAS)
|
|||||||
include_directories(${BLAS_INCLUDE_DIRS})
|
include_directories(${BLAS_INCLUDE_DIRS})
|
||||||
set(WHISPER_EXTRA_LIBS ${WHISPER_EXTRA_LIBS} ${BLAS_LIBRARIES})
|
set(WHISPER_EXTRA_LIBS ${WHISPER_EXTRA_LIBS} ${BLAS_LIBRARIES})
|
||||||
else()
|
else()
|
||||||
message(FATAL_ERROR "BLAS library was not found")
|
message(WARNING "BLAS library was not found")
|
||||||
endif()
|
endif()
|
||||||
endif ()
|
endif ()
|
||||||
endif ()
|
endif ()
|
||||||
@ -238,24 +177,18 @@ if (WHISPER_CUBLAS)
|
|||||||
|
|
||||||
enable_language(CUDA)
|
enable_language(CUDA)
|
||||||
|
|
||||||
set(GGML_SOURCES_CUDA ggml-cuda.cu ggml-cuda.h)
|
set(GGML_CUDA_SOURCES ggml-cuda.cu ggml-cuda.h)
|
||||||
|
|
||||||
add_compile_definitions(GGML_USE_CUBLAS)
|
add_compile_definitions(GGML_USE_CUBLAS)
|
||||||
|
|
||||||
if (WHISPER_STATIC)
|
if (WHISPER_STATIC)
|
||||||
if (WIN32)
|
|
||||||
# As of 12.3.1 CUDA Tookit for Windows does not offer a static cublas library
|
|
||||||
set(WHISPER_EXTRA_LIBS ${WHISPER_EXTRA_LIBS} CUDA::cudart_static CUDA::cublas CUDA::cublasLt)
|
|
||||||
else ()
|
|
||||||
set(WHISPER_EXTRA_LIBS ${WHISPER_EXTRA_LIBS} CUDA::cudart_static CUDA::cublas_static CUDA::cublasLt_static)
|
set(WHISPER_EXTRA_LIBS ${WHISPER_EXTRA_LIBS} CUDA::cudart_static CUDA::cublas_static CUDA::cublasLt_static)
|
||||||
endif()
|
|
||||||
else()
|
else()
|
||||||
set(WHISPER_EXTRA_LIBS ${WHISPER_EXTRA_LIBS} CUDA::cudart CUDA::cublas CUDA::cublasLt)
|
set(WHISPER_EXTRA_LIBS ${WHISPER_EXTRA_LIBS} CUDA::cudart CUDA::cublas CUDA::cublasLt)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(WHISPER_EXTRA_LIBS ${WHISPER_EXTRA_LIBS} CUDA::cuda_driver)
|
|
||||||
else()
|
else()
|
||||||
message(FATAL_ERROR "cuBLAS not found")
|
message(WARNING "cuBLAS not found")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -286,7 +219,7 @@ if (WHISPER_HIPBLAS)
|
|||||||
endif()
|
endif()
|
||||||
set(WHISPER_EXTRA_LIBS ${WHISPER_EXTRA_LIBS} ggml-rocm)
|
set(WHISPER_EXTRA_LIBS ${WHISPER_EXTRA_LIBS} ggml-rocm)
|
||||||
else()
|
else()
|
||||||
message(FATAL_ERROR "hipBLAS or HIP not found. Try setting CMAKE_PREFIX_PATH=/opt/rocm")
|
message(WARNING "hipBLAS or HIP not found. Try setting CMAKE_PREFIX_PATH=/opt/rocm")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -295,13 +228,13 @@ if (WHISPER_CLBLAST)
|
|||||||
if (CLBlast_FOUND)
|
if (CLBlast_FOUND)
|
||||||
message(STATUS "CLBlast found")
|
message(STATUS "CLBlast found")
|
||||||
|
|
||||||
set(GGML_SOURCES_OPENCL ggml-opencl.cpp ggml-opencl.h)
|
set(GGML_OPENCL_SOURCES ggml-opencl.cpp ggml-opencl.h)
|
||||||
|
|
||||||
add_compile_definitions(GGML_USE_CLBLAST)
|
add_compile_definitions(GGML_USE_CLBLAST)
|
||||||
|
|
||||||
set(WHISPER_EXTRA_LIBS ${WHISPER_EXTRA_LIBS} clblast)
|
set(WHISPER_EXTRA_LIBS ${WHISPER_EXTRA_LIBS} clblast)
|
||||||
else()
|
else()
|
||||||
message(FATAL_ERROR "CLBlast not found")
|
message(WARNING "CLBlast not found")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -340,8 +273,7 @@ if (WHISPER_ALL_WARNINGS)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (NOT MSVC)
|
if (NOT MSVC)
|
||||||
# TODO: temporary disabled until we figure out ggml-metal.m
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror=vla")
|
||||||
#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror=vla")
|
|
||||||
#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-math-errno -ffinite-math-only -funsafe-math-optimizations")
|
#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-math-errno -ffinite-math-only -funsafe-math-optimizations")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -370,8 +302,8 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
if (EMSCRIPTEN)
|
if (EMSCRIPTEN)
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread -s TOTAL_STACK=5242880")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -s TOTAL_STACK=5242880")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
|
||||||
else()
|
else()
|
||||||
if(NOT WHISPER_NO_AVX)
|
if(NOT WHISPER_NO_AVX)
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mavx")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mavx")
|
||||||
@ -494,15 +426,8 @@ set(TARGET whisper)
|
|||||||
add_library(${TARGET}
|
add_library(${TARGET}
|
||||||
ggml.h
|
ggml.h
|
||||||
ggml.c
|
ggml.c
|
||||||
ggml-alloc.h
|
${GGML_CUDA_SOURCES}
|
||||||
ggml-alloc.c
|
${GGML_OPENCL_SOURCES}
|
||||||
ggml-backend.h
|
|
||||||
ggml-backend.c
|
|
||||||
ggml-quants.h
|
|
||||||
ggml-quants.c
|
|
||||||
${GGML_SOURCES_METAL}
|
|
||||||
${GGML_SOURCES_CUDA}
|
|
||||||
${GGML_SOURCES_OPENCL}
|
|
||||||
whisper.h
|
whisper.h
|
||||||
whisper.cpp
|
whisper.cpp
|
||||||
)
|
)
|
||||||
@ -530,7 +455,6 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (BUILD_SHARED_LIBS)
|
if (BUILD_SHARED_LIBS)
|
||||||
set_target_properties(${TARGET} PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
|
||||||
target_link_libraries(${TARGET} PUBLIC
|
target_link_libraries(${TARGET} PUBLIC
|
||||||
${CMAKE_DL_LIBS}
|
${CMAKE_DL_LIBS}
|
||||||
)
|
)
|
||||||
@ -544,23 +468,11 @@ if (BUILD_SHARED_LIBS)
|
|||||||
WHISPER_BUILD
|
WHISPER_BUILD
|
||||||
GGML_BUILD
|
GGML_BUILD
|
||||||
)
|
)
|
||||||
|
|
||||||
if (WHISPER_METAL)
|
|
||||||
# TODO: I think this should make ggml-metal.m "see" the ggml-metal.metal file from the "bin" directory
|
|
||||||
# but for some reason it does not work here like it does in llama.cpp
|
|
||||||
set_target_properties(${TARGET} PROPERTIES RESOURCE "${CMAKE_CURRENT_SOURCE_DIR}/ggml-metal.metal")
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (GGML_SOURCES_CUDA)
|
if (GGML_CUDA_SOURCES)
|
||||||
message(STATUS "GGML CUDA sources found, configuring CUDA architecture")
|
message(STATUS "GGML CUDA sources found, configuring CUDA architecture")
|
||||||
# Only configure gmml CUDA architectures is not globally set
|
set_property(TARGET whisper PROPERTY CUDA_ARCHITECTURES OFF)
|
||||||
if (NOT DEFINED GGML_CUDA_ARCHITECTURES)
|
|
||||||
# Not overriden by user, so set defaults
|
|
||||||
set(GGML_CUDA_ARCHITECTURES 52 61 70)
|
|
||||||
endif()
|
|
||||||
message(STATUS "GGML Configuring CUDA architectures ${GGML_CUDA_ARCHITECTURES}")
|
|
||||||
set_property(TARGET whisper PROPERTY CUDA_ARCHITECTURES ${GGML_CUDA_ARCHITECTURES})
|
|
||||||
set_property(TARGET whisper PROPERTY CUDA_SELECT_NVCC_ARCH_FLAGS "Auto")
|
set_property(TARGET whisper PROPERTY CUDA_SELECT_NVCC_ARCH_FLAGS "Auto")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -572,15 +484,12 @@ target_compile_definitions(${TARGET} PUBLIC
|
|||||||
${WHISPER_EXTRA_FLAGS}
|
${WHISPER_EXTRA_FLAGS}
|
||||||
)
|
)
|
||||||
|
|
||||||
set_target_properties(${TARGET} PROPERTIES PUBLIC_HEADER "ggml.h;whisper.h")
|
set_target_properties(${TARGET} PROPERTIES PUBLIC_HEADER "whisper.h")
|
||||||
|
|
||||||
include(GNUInstallDirs)
|
|
||||||
|
|
||||||
install(TARGETS ${TARGET}
|
install(TARGETS ${TARGET}
|
||||||
LIBRARY DESTINATION lib
|
LIBRARY DESTINATION lib
|
||||||
ARCHIVE DESTINATION lib/static
|
ARCHIVE DESTINATION lib/static
|
||||||
RUNTIME DESTINATION bin
|
RUNTIME DESTINATION bin
|
||||||
RESOURCE DESTINATION bin
|
|
||||||
PUBLIC_HEADER DESTINATION include
|
PUBLIC_HEADER DESTINATION include
|
||||||
)
|
)
|
||||||
|
|
||||||
|
122
Makefile
122
Makefile
@ -1,4 +1,4 @@
|
|||||||
default: main bench quantize server
|
default: main bench quantize
|
||||||
|
|
||||||
ifndef UNAME_S
|
ifndef UNAME_S
|
||||||
UNAME_S := $(shell uname -s)
|
UNAME_S := $(shell uname -s)
|
||||||
@ -42,12 +42,6 @@ CFLAGS = -I. -O3 -DNDEBUG -std=c11 -fPIC
|
|||||||
CXXFLAGS = -I. -I./examples -O3 -DNDEBUG -std=c++11 -fPIC
|
CXXFLAGS = -I. -I./examples -O3 -DNDEBUG -std=c++11 -fPIC
|
||||||
LDFLAGS =
|
LDFLAGS =
|
||||||
|
|
||||||
ifdef MACOSX_DEPLOYMENT_TARGET
|
|
||||||
CFLAGS += -mmacosx-version-min=$(MACOSX_DEPLOYMENT_TARGET)
|
|
||||||
CXXFLAGS += -mmacosx-version-min=$(MACOSX_DEPLOYMENT_TARGET)
|
|
||||||
LDFLAGS += -mmacosx-version-min=$(MACOSX_DEPLOYMENT_TARGET)
|
|
||||||
endif
|
|
||||||
|
|
||||||
# clock_gettime came in POSIX.1b (1993)
|
# clock_gettime came in POSIX.1b (1993)
|
||||||
# CLOCK_MONOTONIC came in POSIX.1-2001 / SUSv3 as optional
|
# CLOCK_MONOTONIC came in POSIX.1-2001 / SUSv3 as optional
|
||||||
# posix_memalign came in POSIX.1-2001 / SUSv3
|
# posix_memalign came in POSIX.1-2001 / SUSv3
|
||||||
@ -105,16 +99,6 @@ ifeq ($(filter $(UNAME_S),Linux Darwin DragonFly FreeBSD NetBSD OpenBSD Haiku),$
|
|||||||
CXXFLAGS += -pthread
|
CXXFLAGS += -pthread
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# detect Windows
|
|
||||||
ifneq ($(findstring _NT,$(UNAME_S)),)
|
|
||||||
_WIN32 := 1
|
|
||||||
endif
|
|
||||||
|
|
||||||
# Windows Sockets 2 (Winsock) for network-capable apps
|
|
||||||
ifeq ($(_WIN32),1)
|
|
||||||
LWINSOCK2 := -lws2_32
|
|
||||||
endif
|
|
||||||
|
|
||||||
# Architecture specific
|
# Architecture specific
|
||||||
# TODO: probably these flags need to be tweaked on some architectures
|
# TODO: probably these flags need to be tweaked on some architectures
|
||||||
# feel free to update the Makefile for your architecture and send a pull request or issue
|
# feel free to update the Makefile for your architecture and send a pull request or issue
|
||||||
@ -123,7 +107,7 @@ ifeq ($(UNAME_M),$(filter $(UNAME_M),x86_64 i686 amd64))
|
|||||||
CPUINFO_CMD := sysctl machdep.cpu.features machdep.cpu.leaf7_features
|
CPUINFO_CMD := sysctl machdep.cpu.features machdep.cpu.leaf7_features
|
||||||
else ifeq ($(UNAME_S),Linux)
|
else ifeq ($(UNAME_S),Linux)
|
||||||
CPUINFO_CMD := cat /proc/cpuinfo
|
CPUINFO_CMD := cat /proc/cpuinfo
|
||||||
else ifneq (,$(filter MINGW32_NT% MINGW64_NT% MSYS_NT%,$(UNAME_S)))
|
else ifneq (,$(filter MINGW32_NT% MINGW64_NT%,$(UNAME_S)))
|
||||||
CPUINFO_CMD := cat /proc/cpuinfo
|
CPUINFO_CMD := cat /proc/cpuinfo
|
||||||
else ifneq (,$(filter DragonFly FreeBSD,$(UNAME_S)))
|
else ifneq (,$(filter DragonFly FreeBSD,$(UNAME_S)))
|
||||||
CPUINFO_CMD := grep Features /var/run/dmesg.boot
|
CPUINFO_CMD := grep Features /var/run/dmesg.boot
|
||||||
@ -198,16 +182,6 @@ ifdef WHISPER_COREML_ALLOW_FALLBACK
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifndef WHISPER_NO_METAL
|
|
||||||
ifeq ($(UNAME_S),Darwin)
|
|
||||||
WHISPER_METAL := 1
|
|
||||||
|
|
||||||
CFLAGS += -DGGML_USE_METAL
|
|
||||||
CXXFLAGS += -DGGML_USE_METAL
|
|
||||||
LDFLAGS += -framework Foundation -framework Metal -framework MetalKit
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifdef WHISPER_OPENBLAS
|
ifdef WHISPER_OPENBLAS
|
||||||
CFLAGS += -DGGML_USE_OPENBLAS -I/usr/local/include/openblas -I/usr/include/openblas
|
CFLAGS += -DGGML_USE_OPENBLAS -I/usr/local/include/openblas -I/usr/include/openblas
|
||||||
LDFLAGS += -lopenblas
|
LDFLAGS += -lopenblas
|
||||||
@ -215,14 +189,14 @@ endif
|
|||||||
|
|
||||||
ifdef WHISPER_CUBLAS
|
ifdef WHISPER_CUBLAS
|
||||||
ifeq ($(shell expr $(NVCC_VERSION) \>= 11.6), 1)
|
ifeq ($(shell expr $(NVCC_VERSION) \>= 11.6), 1)
|
||||||
CUDA_ARCH_FLAG ?= native
|
CUDA_ARCH_FLAG=native
|
||||||
else
|
else
|
||||||
CUDA_ARCH_FLAG ?= all
|
CUDA_ARCH_FLAG=all
|
||||||
endif
|
endif
|
||||||
|
|
||||||
CFLAGS += -DGGML_USE_CUBLAS -I/usr/local/cuda/include -I/opt/cuda/include -I$(CUDA_PATH)/targets/$(UNAME_M)-linux/include
|
CFLAGS += -DGGML_USE_CUBLAS -I/usr/local/cuda/include -I/opt/cuda/include -I$(CUDA_PATH)/targets/$(UNAME_M)-linux/include
|
||||||
CXXFLAGS += -DGGML_USE_CUBLAS -I/usr/local/cuda/include -I/opt/cuda/include -I$(CUDA_PATH)/targets/$(UNAME_M)-linux/include
|
CXXFLAGS += -DGGML_USE_CUBLAS -I/usr/local/cuda/include -I/opt/cuda/include -I$(CUDA_PATH)/targets/$(UNAME_M)-linux/include
|
||||||
LDFLAGS += -lcuda -lcublas -lculibos -lcudart -lcublasLt -lpthread -ldl -lrt -L/usr/local/cuda/lib64 -L/opt/cuda/lib64 -L$(CUDA_PATH)/targets/$(UNAME_M)-linux/lib
|
LDFLAGS += -lcublas -lculibos -lcudart -lcublasLt -lpthread -ldl -lrt -L/usr/local/cuda/lib64 -L/opt/cuda/lib64 -L$(CUDA_PATH)/targets/$(UNAME_M)-linux/lib
|
||||||
WHISPER_OBJ += ggml-cuda.o
|
WHISPER_OBJ += ggml-cuda.o
|
||||||
NVCC = nvcc
|
NVCC = nvcc
|
||||||
NVCCFLAGS = --forward-unknown-to-host-compiler -arch=$(CUDA_ARCH_FLAG)
|
NVCCFLAGS = --forward-unknown-to-host-compiler -arch=$(CUDA_ARCH_FLAG)
|
||||||
@ -314,17 +288,6 @@ $(info )
|
|||||||
ggml.o: ggml.c ggml.h ggml-cuda.h
|
ggml.o: ggml.c ggml.h ggml-cuda.h
|
||||||
$(CC) $(CFLAGS) -c $< -o $@
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
ggml-alloc.o: ggml-alloc.c ggml.h ggml-alloc.h
|
|
||||||
$(CC) $(CFLAGS) -c $< -o $@
|
|
||||||
|
|
||||||
ggml-backend.o: ggml-backend.c ggml.h ggml-backend.h
|
|
||||||
$(CC) $(CFLAGS) -c $< -o $@
|
|
||||||
|
|
||||||
ggml-quants.o: ggml-quants.c ggml.h ggml-quants.h
|
|
||||||
$(CC) $(CFLAGS) -c $< -o $@
|
|
||||||
|
|
||||||
WHISPER_OBJ += ggml.o ggml-alloc.o ggml-backend.o ggml-quants.o
|
|
||||||
|
|
||||||
whisper.o: whisper.cpp whisper.h ggml.h ggml-cuda.h
|
whisper.o: whisper.cpp whisper.h ggml.h ggml-cuda.h
|
||||||
$(CXX) $(CXXFLAGS) -c $< -o $@
|
$(CXX) $(CXXFLAGS) -c $< -o $@
|
||||||
|
|
||||||
@ -340,39 +303,14 @@ whisper-encoder-impl.o: coreml/whisper-encoder-impl.m coreml/whisper-encoder-imp
|
|||||||
WHISPER_OBJ += whisper.o whisper-encoder.o whisper-encoder-impl.o
|
WHISPER_OBJ += whisper.o whisper-encoder.o whisper-encoder-impl.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifdef WHISPER_METAL
|
libwhisper.a: ggml.o $(WHISPER_OBJ)
|
||||||
ggml-metal.o: ggml-metal.m ggml-metal.h
|
$(AR) rcs libwhisper.a ggml.o $(WHISPER_OBJ)
|
||||||
$(CC) $(CFLAGS) -c $< -o $@
|
|
||||||
|
|
||||||
WHISPER_OBJ += ggml-metal.o
|
libwhisper.so: ggml.o $(WHISPER_OBJ)
|
||||||
|
$(CXX) $(CXXFLAGS) -shared -o libwhisper.so ggml.o $(WHISPER_OBJ) $(LDFLAGS)
|
||||||
ifdef WHISPER_METAL_EMBED_LIBRARY
|
|
||||||
CFLAGS += -DGGML_METAL_EMBED_LIBRARY
|
|
||||||
|
|
||||||
ggml-metal-embed.o: ggml-metal.metal
|
|
||||||
@echo "Embedding Metal library"
|
|
||||||
$(eval TEMP_ASSEMBLY=$(shell mktemp))
|
|
||||||
@echo ".section __DATA, __ggml_metallib" > $(TEMP_ASSEMBLY)
|
|
||||||
@echo ".globl _ggml_metallib_start" >> $(TEMP_ASSEMBLY)
|
|
||||||
@echo "_ggml_metallib_start:" >> $(TEMP_ASSEMBLY)
|
|
||||||
@echo ".incbin \"$<\"" >> $(TEMP_ASSEMBLY)
|
|
||||||
@echo ".globl _ggml_metallib_end" >> $(TEMP_ASSEMBLY)
|
|
||||||
@echo "_ggml_metallib_end:" >> $(TEMP_ASSEMBLY)
|
|
||||||
@$(AS) $(TEMP_ASSEMBLY) -o $@
|
|
||||||
@rm -f ${TEMP_ASSEMBLY}
|
|
||||||
|
|
||||||
WHISPER_OBJ += ggml-metal-embed.o
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
libwhisper.a: $(WHISPER_OBJ)
|
|
||||||
$(AR) rcs libwhisper.a $(WHISPER_OBJ)
|
|
||||||
|
|
||||||
libwhisper.so: $(WHISPER_OBJ)
|
|
||||||
$(CXX) $(CXXFLAGS) -shared -o libwhisper.so $(WHISPER_OBJ) $(LDFLAGS)
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *.o main stream command talk talk-llama bench quantize server lsp libwhisper.a libwhisper.so
|
rm -f *.o main stream command talk talk-llama bench quantize lsp libwhisper.a libwhisper.so
|
||||||
|
|
||||||
#
|
#
|
||||||
# Examples
|
# Examples
|
||||||
@ -383,33 +321,30 @@ CC_SDL=`sdl2-config --cflags --libs`
|
|||||||
SRC_COMMON = examples/common.cpp examples/common-ggml.cpp
|
SRC_COMMON = examples/common.cpp examples/common-ggml.cpp
|
||||||
SRC_COMMON_SDL = examples/common-sdl.cpp
|
SRC_COMMON_SDL = examples/common-sdl.cpp
|
||||||
|
|
||||||
main: examples/main/main.cpp $(SRC_COMMON) $(WHISPER_OBJ)
|
main: examples/main/main.cpp $(SRC_COMMON) ggml.o $(WHISPER_OBJ)
|
||||||
$(CXX) $(CXXFLAGS) examples/main/main.cpp $(SRC_COMMON) $(WHISPER_OBJ) -o main $(LDFLAGS)
|
$(CXX) $(CXXFLAGS) examples/main/main.cpp $(SRC_COMMON) ggml.o $(WHISPER_OBJ) -o main $(LDFLAGS)
|
||||||
./main -h
|
./main -h
|
||||||
|
|
||||||
bench: examples/bench/bench.cpp $(WHISPER_OBJ)
|
bench: examples/bench/bench.cpp ggml.o $(WHISPER_OBJ)
|
||||||
$(CXX) $(CXXFLAGS) examples/bench/bench.cpp $(WHISPER_OBJ) -o bench $(LDFLAGS)
|
$(CXX) $(CXXFLAGS) examples/bench/bench.cpp ggml.o $(WHISPER_OBJ) -o bench $(LDFLAGS)
|
||||||
|
|
||||||
quantize: examples/quantize/quantize.cpp $(WHISPER_OBJ) $(SRC_COMMON)
|
quantize: examples/quantize/quantize.cpp ggml.o $(WHISPER_OBJ) $(SRC_COMMON)
|
||||||
$(CXX) $(CXXFLAGS) examples/quantize/quantize.cpp $(SRC_COMMON) $(WHISPER_OBJ) -o quantize $(LDFLAGS)
|
$(CXX) $(CXXFLAGS) examples/quantize/quantize.cpp $(SRC_COMMON) ggml.o $(WHISPER_OBJ) -o quantize $(LDFLAGS)
|
||||||
|
|
||||||
server: examples/server/server.cpp $(SRC_COMMON) $(WHISPER_OBJ)
|
stream: examples/stream/stream.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) ggml.o $(WHISPER_OBJ)
|
||||||
$(CXX) $(CXXFLAGS) examples/server/server.cpp $(SRC_COMMON) $(WHISPER_OBJ) -o server $(LDFLAGS) $(LWINSOCK2)
|
$(CXX) $(CXXFLAGS) examples/stream/stream.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) ggml.o $(WHISPER_OBJ) -o stream $(CC_SDL) $(LDFLAGS)
|
||||||
|
|
||||||
stream: examples/stream/stream.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) $(WHISPER_OBJ)
|
command: examples/command/command.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) ggml.o $(WHISPER_OBJ)
|
||||||
$(CXX) $(CXXFLAGS) examples/stream/stream.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) $(WHISPER_OBJ) -o stream $(CC_SDL) $(LDFLAGS)
|
$(CXX) $(CXXFLAGS) examples/command/command.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) ggml.o $(WHISPER_OBJ) -o command $(CC_SDL) $(LDFLAGS)
|
||||||
|
|
||||||
command: examples/command/command.cpp examples/grammar-parser.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) $(WHISPER_OBJ)
|
lsp: examples/lsp/lsp.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) ggml.o $(WHISPER_OBJ)
|
||||||
$(CXX) $(CXXFLAGS) examples/command/command.cpp examples/grammar-parser.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) $(WHISPER_OBJ) -o command $(CC_SDL) $(LDFLAGS)
|
$(CXX) $(CXXFLAGS) examples/lsp/lsp.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) ggml.o $(WHISPER_OBJ) -o lsp $(CC_SDL) $(LDFLAGS)
|
||||||
|
|
||||||
lsp: examples/lsp/lsp.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) $(WHISPER_OBJ)
|
talk: examples/talk/talk.cpp examples/talk/gpt-2.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) ggml.o $(WHISPER_OBJ)
|
||||||
$(CXX) $(CXXFLAGS) examples/lsp/lsp.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) $(WHISPER_OBJ) -o lsp $(CC_SDL) $(LDFLAGS)
|
$(CXX) $(CXXFLAGS) examples/talk/talk.cpp examples/talk/gpt-2.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) ggml.o $(WHISPER_OBJ) -o talk $(CC_SDL) $(LDFLAGS)
|
||||||
|
|
||||||
talk: examples/talk/talk.cpp examples/talk/gpt-2.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) $(WHISPER_OBJ)
|
talk-llama: examples/talk-llama/talk-llama.cpp examples/talk-llama/llama.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) ggml.o $(WHISPER_OBJ)
|
||||||
$(CXX) $(CXXFLAGS) examples/talk/talk.cpp examples/talk/gpt-2.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) $(WHISPER_OBJ) -o talk $(CC_SDL) $(LDFLAGS)
|
$(CXX) $(CXXFLAGS) examples/talk-llama/talk-llama.cpp examples/talk-llama/llama.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) ggml.o $(WHISPER_OBJ) -o talk-llama $(CC_SDL) $(LDFLAGS)
|
||||||
|
|
||||||
talk-llama: examples/talk-llama/talk-llama.cpp examples/talk-llama/llama.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) $(WHISPER_OBJ)
|
|
||||||
$(CXX) $(CXXFLAGS) examples/talk-llama/talk-llama.cpp examples/talk-llama/llama.cpp $(SRC_COMMON) $(SRC_COMMON_SDL) $(WHISPER_OBJ) -o talk-llama $(CC_SDL) $(LDFLAGS)
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Audio samples
|
# Audio samples
|
||||||
@ -454,10 +389,9 @@ samples:
|
|||||||
.PHONY: medium.en
|
.PHONY: medium.en
|
||||||
.PHONY: medium
|
.PHONY: medium
|
||||||
.PHONY: large-v1
|
.PHONY: large-v1
|
||||||
.PHONY: large-v2
|
.PHONY: large
|
||||||
.PHONY: large-v3
|
|
||||||
|
|
||||||
tiny.en tiny base.en base small.en small medium.en medium large-v1 large-v2 large-v3: main
|
tiny.en tiny base.en base small.en small medium.en medium large-v1 large: main
|
||||||
bash ./models/download-ggml-model.sh $@
|
bash ./models/download-ggml-model.sh $@
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo "==============================================="
|
@echo "==============================================="
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
// swift-tools-version:5.5
|
|
||||||
|
|
||||||
import PackageDescription
|
|
||||||
|
|
||||||
let package = Package(
|
|
||||||
name: "whisper",
|
|
||||||
platforms: [
|
|
||||||
.macOS(.v12),
|
|
||||||
.iOS(.v14),
|
|
||||||
.watchOS(.v4),
|
|
||||||
.tvOS(.v14)
|
|
||||||
],
|
|
||||||
products: [
|
|
||||||
.library(name: "whisper", targets: ["whisper"]),
|
|
||||||
],
|
|
||||||
dependencies: [
|
|
||||||
.package(url: "https://github.com/ggerganov/ggml.git", .branch("release"))
|
|
||||||
],
|
|
||||||
targets: [
|
|
||||||
.target(
|
|
||||||
name: "whisper",
|
|
||||||
dependencies: ["ggml"],
|
|
||||||
path: ".",
|
|
||||||
exclude: [
|
|
||||||
"bindings",
|
|
||||||
"cmake",
|
|
||||||
"coreml",
|
|
||||||
"examples",
|
|
||||||
"extra",
|
|
||||||
"models",
|
|
||||||
"samples",
|
|
||||||
"tests",
|
|
||||||
"CMakeLists.txt",
|
|
||||||
"ggml-cuda.cu",
|
|
||||||
"ggml-cuda.h",
|
|
||||||
"Makefile"
|
|
||||||
],
|
|
||||||
sources: [
|
|
||||||
"whisper.cpp",
|
|
||||||
],
|
|
||||||
publicHeadersPath: "spm-headers",
|
|
||||||
cSettings: [
|
|
||||||
.unsafeFlags(["-Wno-shorten-64-to-32", "-O3", "-DNDEBUG"]),
|
|
||||||
.define("GGML_USE_ACCELERATE"),
|
|
||||||
.unsafeFlags(["-fno-objc-arc"]),
|
|
||||||
.define("GGML_USE_METAL")
|
|
||||||
// NOTE: NEW_LAPACK will required iOS version 16.4+
|
|
||||||
// We should consider add this in the future when we drop support for iOS 14
|
|
||||||
// (ref: ref: https://developer.apple.com/documentation/accelerate/1513264-cblas_sgemm?language=objc)
|
|
||||||
// .define("ACCELERATE_NEW_LAPACK"),
|
|
||||||
// .define("ACCELERATE_LAPACK_ILP64")
|
|
||||||
],
|
|
||||||
linkerSettings: [
|
|
||||||
.linkedFramework("Accelerate")
|
|
||||||
]
|
|
||||||
)
|
|
||||||
],
|
|
||||||
cxxLanguageStandard: .cxx11
|
|
||||||
)
|
|
196
README.md
196
README.md
@ -6,20 +6,22 @@
|
|||||||
[](https://opensource.org/licenses/MIT)
|
[](https://opensource.org/licenses/MIT)
|
||||||
[](https://www.npmjs.com/package/whisper.cpp/)
|
[](https://www.npmjs.com/package/whisper.cpp/)
|
||||||
|
|
||||||
Stable: [v1.5.4](https://github.com/ggerganov/whisper.cpp/releases/tag/v1.5.4) / [Roadmap | F.A.Q.](https://github.com/ggerganov/whisper.cpp/discussions/126)
|
Beta: [v1.4.2](https://github.com/ggerganov/whisper.cpp/releases/tag/v1.4.2) / Stable: [v1.2.1](https://github.com/ggerganov/whisper.cpp/releases/tag/v1.2.1) / [Roadmap | F.A.Q.](https://github.com/ggerganov/whisper.cpp/discussions/126)
|
||||||
|
|
||||||
High-performance inference of [OpenAI's Whisper](https://github.com/openai/whisper) automatic speech recognition (ASR) model:
|
High-performance inference of [OpenAI's Whisper](https://github.com/openai/whisper) automatic speech recognition (ASR) model:
|
||||||
|
|
||||||
- Plain C/C++ implementation without dependencies
|
- Plain C/C++ implementation without dependencies
|
||||||
- Apple Silicon first-class citizen - optimized via ARM NEON, Accelerate framework, Metal and [Core ML](https://github.com/ggerganov/whisper.cpp#core-ml-support)
|
- Apple silicon first-class citizen - optimized via ARM NEON, Accelerate framework and [Core ML](https://github.com/ggerganov/whisper.cpp#core-ml-support)
|
||||||
- AVX intrinsics support for x86 architectures
|
- AVX intrinsics support for x86 architectures
|
||||||
- VSX intrinsics support for POWER architectures
|
- VSX intrinsics support for POWER architectures
|
||||||
- Mixed F16 / F32 precision
|
- Mixed F16 / F32 precision
|
||||||
- [4-bit and 5-bit integer quantization support](https://github.com/ggerganov/whisper.cpp#quantization)
|
- [4-bit and 5-bit integer quantization support](https://github.com/ggerganov/whisper.cpp#quantization)
|
||||||
|
- Low memory usage (Flash Attention)
|
||||||
- Zero memory allocations at runtime
|
- Zero memory allocations at runtime
|
||||||
- Support for CPU-only inference
|
- Runs on the CPU
|
||||||
- [Efficient GPU support for NVIDIA](https://github.com/ggerganov/whisper.cpp#nvidia-gpu-support-via-cublas)
|
- [Partial GPU support for NVIDIA via cuBLAS](https://github.com/ggerganov/whisper.cpp#nvidia-gpu-support-via-cublas)
|
||||||
- [Partial OpenCL GPU support via CLBlast](https://github.com/ggerganov/whisper.cpp#opencl-gpu-support-via-clblast)
|
- [Partial OpenCL GPU support via CLBlast](https://github.com/ggerganov/whisper.cpp#opencl-gpu-support-via-clblast)
|
||||||
|
- [BLAS CPU support via OpenBLAS](https://github.com/ggerganov/whisper.cpp#blas-cpu-support-via-openblas)
|
||||||
- [OpenVINO Support](https://github.com/ggerganov/whisper.cpp#openvino-support)
|
- [OpenVINO Support](https://github.com/ggerganov/whisper.cpp#openvino-support)
|
||||||
- [C-style API](https://github.com/ggerganov/whisper.cpp/blob/master/whisper.h)
|
- [C-style API](https://github.com/ggerganov/whisper.cpp/blob/master/whisper.h)
|
||||||
|
|
||||||
@ -33,10 +35,11 @@ Supported platforms:
|
|||||||
- [x] [WebAssembly](examples/whisper.wasm)
|
- [x] [WebAssembly](examples/whisper.wasm)
|
||||||
- [x] Windows ([MSVC](https://github.com/ggerganov/whisper.cpp/blob/master/.github/workflows/build.yml#L117-L144) and [MinGW](https://github.com/ggerganov/whisper.cpp/issues/168)]
|
- [x] Windows ([MSVC](https://github.com/ggerganov/whisper.cpp/blob/master/.github/workflows/build.yml#L117-L144) and [MinGW](https://github.com/ggerganov/whisper.cpp/issues/168)]
|
||||||
- [x] [Raspberry Pi](https://github.com/ggerganov/whisper.cpp/discussions/166)
|
- [x] [Raspberry Pi](https://github.com/ggerganov/whisper.cpp/discussions/166)
|
||||||
- [x] [docker](https://github.com/ggerganov/whisper.cpp/pkgs/container/whisper.cpp)
|
|
||||||
|
|
||||||
The entire high-level implementation of the model is contained in [whisper.h](whisper.h) and [whisper.cpp](whisper.cpp).
|
The entire implementation of the model is contained in 2 source files:
|
||||||
The rest of the code is part of the [`ggml`](https://github.com/ggerganov/ggml) machine learning library.
|
|
||||||
|
- Tensor operations: [ggml.h](ggml.h) / [ggml.c](ggml.c)
|
||||||
|
- Transformer inference: [whisper.h](whisper.h) / [whisper.cpp](whisper.cpp)
|
||||||
|
|
||||||
Having such a lightweight implementation of the model allows to easily integrate it in different platforms and applications.
|
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)
|
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)
|
||||||
@ -47,10 +50,6 @@ You can also easily make your own offline voice assistant application: [command]
|
|||||||
|
|
||||||
https://user-images.githubusercontent.com/1991296/204038393-2f846eae-c255-4099-a76d-5735c25c49da.mp4
|
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/ggerganov/whisper.cpp/assets/1991296/c82e8f86-60dc-49f2-b048-d2fdbd6b5225
|
|
||||||
|
|
||||||
Or you can even run it straight in the browser: [talk.wasm](examples/talk.wasm)
|
Or you can even run it straight in the browser: [talk.wasm](examples/talk.wasm)
|
||||||
|
|
||||||
## Implementation details
|
## Implementation details
|
||||||
@ -61,22 +60,22 @@ Or you can even run it straight in the browser: [talk.wasm](examples/talk.wasm)
|
|||||||
- Sample real-time audio transcription from the microphone is demonstrated in [stream.cpp](examples/stream)
|
- Sample real-time audio transcription from the microphone is demonstrated in [stream.cpp](examples/stream)
|
||||||
- Various other examples are available in the [examples](examples) folder
|
- Various other examples are available in the [examples](examples) folder
|
||||||
|
|
||||||
The tensor operators are optimized heavily for Apple silicon CPUs. Depending on the computation size, Arm Neon SIMD intrinsics or CBLAS Accelerate framework routines are used. The latter are especially effective for bigger sizes since the Accelerate framework utilizes the special-purpose AMX coprocessor available in modern Apple products.
|
The tensor operators are optimized heavily for Apple silicon CPUs. Depending on the computation size, Arm Neon SIMD
|
||||||
|
intrinsics or CBLAS Accelerate framework routines are used. The latter are especially effective for bigger sizes since
|
||||||
|
the Accelerate framework utilizes the special-purpose AMX coprocessor available in modern Apple products.
|
||||||
|
|
||||||
## Quick start
|
## Quick start
|
||||||
|
|
||||||
First clone the repository:
|
First clone the repository.
|
||||||
|
|
||||||
```bash
|
Then, download one of the Whisper models converted in [ggml format](models). For example:
|
||||||
git clone https://github.com/ggerganov/whisper.cpp.git
|
|
||||||
```
|
|
||||||
|
|
||||||
Then, download one of the Whisper [models](models/README.md) converted in [`ggml` format](#ggml-format). For example:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bash ./models/download-ggml-model.sh base.en
|
bash ./models/download-ggml-model.sh base.en
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you wish to convert the Whisper models to ggml format yourself, instructions are in [models/README.md](models/README.md).
|
||||||
|
|
||||||
Now build the [main](examples/main) example and transcribe an audio file like this:
|
Now build the [main](examples/main) example and transcribe an audio file like this:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@ -91,7 +90,7 @@ make
|
|||||||
|
|
||||||
For a quick demo, simply run `make base.en`:
|
For a quick demo, simply run `make base.en`:
|
||||||
|
|
||||||
```text
|
```java
|
||||||
$ make base.en
|
$ make base.en
|
||||||
|
|
||||||
cc -I. -O3 -std=c11 -pthread -DGGML_USE_ACCELERATE -c ggml.c -o ggml.o
|
cc -I. -O3 -std=c11 -pthread -DGGML_USE_ACCELERATE -c ggml.c -o ggml.o
|
||||||
@ -110,39 +109,30 @@ options:
|
|||||||
-d N, --duration N [0 ] duration of audio to process in milliseconds
|
-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
|
-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
|
-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
|
-bo N, --best-of N [5 ] number of best candidates to keep
|
||||||
-bs N, --beam-size N [5 ] beam size for beam search
|
-bs N, --beam-size N [-1 ] beam size for beam search
|
||||||
-wt N, --word-thold N [0.01 ] word timestamp probability threshold
|
-wt N, --word-thold N [0.01 ] word timestamp probability threshold
|
||||||
-et N, --entropy-thold N [2.40 ] entropy threshold for decoder fail
|
-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
|
-lpt N, --logprob-thold N [-1.00 ] log probability threshold for decoder fail
|
||||||
-debug, --debug-mode [false ] enable debug mode (eg. dump log_mel)
|
-su, --speed-up [false ] speed up audio by x2 (reduced accuracy)
|
||||||
-tr, --translate [false ] translate from source language to english
|
-tr, --translate [false ] translate from source language to english
|
||||||
-di, --diarize [false ] stereo audio diarization
|
|
||||||
-tdrz, --tinydiarize [false ] enable tinydiarize (requires a tdrz model)
|
-tdrz, --tinydiarize [false ] enable tinydiarize (requires a tdrz model)
|
||||||
|
-di, --diarize [false ] stereo audio diarization
|
||||||
-nf, --no-fallback [false ] do not use temperature fallback while decoding
|
-nf, --no-fallback [false ] do not use temperature fallback while decoding
|
||||||
-otxt, --output-txt [false ] output result in a text file
|
-otxt, --output-txt [false ] output result in a text file
|
||||||
-ovtt, --output-vtt [false ] output result in a vtt file
|
-ovtt, --output-vtt [false ] output result in a vtt file
|
||||||
-osrt, --output-srt [false ] output result in a srt 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
|
-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
|
-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)
|
-of FNAME, --output-file FNAME [ ] output file path (without file extension)
|
||||||
-ps, --print-special [false ] print special tokens
|
-ps, --print-special [false ] print special tokens
|
||||||
-pc, --print-colors [false ] print colors
|
-pc, --print-colors [false ] print colors
|
||||||
-pp, --print-progress [false ] print progress
|
-pp, --print-progress [false ] print progress
|
||||||
-nt, --no-timestamps [false ] do not print timestamps
|
-nt, --no-timestamps [true ] do not print timestamps
|
||||||
-l LANG, --language LANG [en ] spoken language ('auto' for auto-detect)
|
-l LANG, --language LANG [en ] spoken language ('auto' for auto-detect)
|
||||||
-dl, --detect-language [false ] exit after automatically detecting language
|
|
||||||
--prompt PROMPT [ ] initial prompt
|
--prompt PROMPT [ ] initial prompt
|
||||||
-m FNAME, --model FNAME [models/ggml-base.en.bin] model path
|
-m FNAME, --model FNAME [models/ggml-base.en.bin] model path
|
||||||
-f FNAME, --file FNAME [ ] input WAV file path
|
-f FNAME, --file FNAME [ ] input WAV file path
|
||||||
-oved D, --ov-e-device DNAME [CPU ] the OpenVINO device used for encode inference
|
|
||||||
-ls, --log-score [false ] log best decoder scores of tokens
|
|
||||||
-ng, --no-gpu [false ] disable GPU
|
|
||||||
|
|
||||||
|
|
||||||
bash ./models/download-ggml-model.sh base.en
|
bash ./models/download-ggml-model.sh base.en
|
||||||
@ -207,7 +197,7 @@ For detailed usage instructions, run: `./main -h`
|
|||||||
Note that the [main](examples/main) example currently runs only with 16-bit WAV files, so make sure to convert your input before running the tool.
|
Note that the [main](examples/main) 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:
|
For example, you can use `ffmpeg` like this:
|
||||||
|
|
||||||
```bash
|
```java
|
||||||
ffmpeg -i input.mp3 -ar 16000 -ac 1 -c:a pcm_s16le output.wav
|
ffmpeg -i input.mp3 -ar 16000 -ac 1 -c:a pcm_s16le output.wav
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -233,19 +223,18 @@ make small
|
|||||||
make medium.en
|
make medium.en
|
||||||
make medium
|
make medium
|
||||||
make large-v1
|
make large-v1
|
||||||
make large-v2
|
make large
|
||||||
make large-v3
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Memory usage
|
## Memory usage
|
||||||
|
|
||||||
| Model | Disk | Mem |
|
| Model | Disk | Mem | SHA |
|
||||||
| ------ | ------- | ------- |
|
| --- | --- | --- | --- |
|
||||||
| tiny | 75 MiB | ~273 MB |
|
| tiny | 75 MB | ~125 MB | `bd577a113a864445d4c299885e0cb97d4ba92b5f` |
|
||||||
| base | 142 MiB | ~388 MB |
|
| base | 142 MB | ~210 MB | `465707469ff3a37a2b9b8d8f89f2f99de7299dac` |
|
||||||
| small | 466 MiB | ~852 MB |
|
| small | 466 MB | ~600 MB | `55356645c2b361a969dfd0ef2c5a50d530afd8d5` |
|
||||||
| medium | 1.5 GiB | ~2.1 GB |
|
| medium | 1.5 GB | ~1.7 GB | `fd9727b6e1217c2f614f9b698455c4ffd82463b4` |
|
||||||
| large | 2.9 GiB | ~3.9 GB |
|
| large | 2.9 GB | ~3.3 GB | `0f4c8e34f21cf1a914c59d8b3ce882345ad349d6` |
|
||||||
|
|
||||||
## Quantization
|
## Quantization
|
||||||
|
|
||||||
@ -304,8 +293,8 @@ speed-up - more than x3 faster compared with CPU-only execution. Here are the in
|
|||||||
|
|
||||||
- Run the examples as usual. For example:
|
- Run the examples as usual. For example:
|
||||||
|
|
||||||
```text
|
```bash
|
||||||
$ ./main -m models/ggml-base.en.bin -f samples/jfk.wav
|
./main -m models/ggml-base.en.bin -f samples/jfk.wav
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
@ -333,8 +322,7 @@ This can result in significant speedup in encoder performance. Here are the inst
|
|||||||
- First, setup python virtual env. and install python dependencies. Python 3.10 is recommended.
|
- First, setup python virtual env. and install python dependencies. Python 3.10 is recommended.
|
||||||
|
|
||||||
Windows:
|
Windows:
|
||||||
|
```
|
||||||
```powershell
|
|
||||||
cd models
|
cd models
|
||||||
python -m venv openvino_conv_env
|
python -m venv openvino_conv_env
|
||||||
openvino_conv_env\Scripts\activate
|
openvino_conv_env\Scripts\activate
|
||||||
@ -343,8 +331,7 @@ This can result in significant speedup in encoder performance. Here are the inst
|
|||||||
```
|
```
|
||||||
|
|
||||||
Linux and macOS:
|
Linux and macOS:
|
||||||
|
```
|
||||||
```bash
|
|
||||||
cd models
|
cd models
|
||||||
python3 -m venv openvino_conv_env
|
python3 -m venv openvino_conv_env
|
||||||
source openvino_conv_env/bin/activate
|
source openvino_conv_env/bin/activate
|
||||||
@ -358,7 +345,7 @@ This can result in significant speedup in encoder performance. Here are the inst
|
|||||||
python convert-whisper-to-openvino.py --model base.en
|
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
|
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.
|
is the default location that the OpenVINO extension will search at runtime.
|
||||||
|
|
||||||
- Build `whisper.cpp` with OpenVINO support:
|
- Build `whisper.cpp` with OpenVINO support:
|
||||||
@ -368,28 +355,24 @@ This can result in significant speedup in encoder performance. Here are the inst
|
|||||||
After downloading & extracting package onto your development system, set up required environment by sourcing setupvars script. For example:
|
After downloading & extracting package onto your development system, set up required environment by sourcing setupvars script. For example:
|
||||||
|
|
||||||
Linux:
|
Linux:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
source /path/to/l_openvino_toolkit_ubuntu22_2023.0.0.10926.b4452d56304_x86_64/setupvars.sh
|
source /path/to/l_openvino_toolkit_ubuntu22_2023.0.0.10926.b4452d56304_x86_64/setupvars.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
Windows (cmd):
|
Windows (cmd):
|
||||||
|
```
|
||||||
```powershell
|
|
||||||
C:\Path\To\w_openvino_toolkit_windows_2023.0.0.10926.b4452d56304_x86_64\setupvars.bat
|
C:\Path\To\w_openvino_toolkit_windows_2023.0.0.10926.b4452d56304_x86_64\setupvars.bat
|
||||||
```
|
```
|
||||||
|
|
||||||
And then build the project using cmake:
|
And then build the project using cmake:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cmake -B build -DWHISPER_OPENVINO=1
|
cmake -B build -DWHISPER_OPENVINO=1
|
||||||
cmake --build build -j --config Release
|
cmake --build build -j --config Release
|
||||||
```
|
```
|
||||||
|
|
||||||
- Run the examples as usual. For example:
|
- Run the examples as usual. For example:
|
||||||
|
```bash
|
||||||
```text
|
./main -m models/ggml-base.en.bin -f samples/jfk.wav
|
||||||
$ ./main -m models/ggml-base.en.bin -f samples/jfk.wav
|
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
@ -408,9 +391,9 @@ This can result in significant speedup in encoder performance. Here are the inst
|
|||||||
|
|
||||||
For more information about the Core ML implementation please refer to PR [#1037](https://github.com/ggerganov/whisper.cpp/pull/1037).
|
For more information about the Core ML implementation please refer to PR [#1037](https://github.com/ggerganov/whisper.cpp/pull/1037).
|
||||||
|
|
||||||
## NVIDIA GPU support
|
## NVIDIA GPU support via cuBLAS
|
||||||
|
|
||||||
With NVIDIA cards the processing of the models is done efficiently on the GPU via cuBLAS and custom CUDA kernels.
|
With NVIDIA cards the Encoder processing can to a large extent be offloaded to the GPU through cuBLAS.
|
||||||
First, make sure you have installed `cuda`: https://developer.nvidia.com/cuda-downloads
|
First, make sure you have installed `cuda`: https://developer.nvidia.com/cuda-downloads
|
||||||
|
|
||||||
Now build `whisper.cpp` with cuBLAS support:
|
Now build `whisper.cpp` with cuBLAS support:
|
||||||
@ -440,6 +423,7 @@ cmake -B build -DWHISPER_CLBLAST=ON
|
|||||||
cmake --build build -j --config Release
|
cmake --build build -j --config Release
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
Run all the examples as usual.
|
Run all the examples as usual.
|
||||||
|
|
||||||
## BLAS CPU support via OpenBLAS
|
## BLAS CPU support via OpenBLAS
|
||||||
@ -454,38 +438,6 @@ make clean
|
|||||||
WHISPER_OPENBLAS=1 make -j
|
WHISPER_OPENBLAS=1 make -j
|
||||||
```
|
```
|
||||||
|
|
||||||
## 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 two Docker images available for this project:
|
|
||||||
|
|
||||||
1. `ghcr.io/ggerganov/whisper.cpp:main`: This image includes the main executable file as well as `curl` and `ffmpeg`. (platforms: `linux/amd64`, `linux/arm64`)
|
|
||||||
2. `ghcr.io/ggerganov/whisper.cpp:main-cuda`: Same as `main` but compiled with CUDA 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 "./main -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 "./main -m /models/ggml-base.bin -f ./samples/jfk.wav"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Limitations
|
## Limitations
|
||||||
|
|
||||||
- Inference only
|
- Inference only
|
||||||
@ -498,7 +450,7 @@ in about half a minute on a MacBook M1 Pro, using `medium.en` model:
|
|||||||
<details>
|
<details>
|
||||||
<summary>Expand to see the result</summary>
|
<summary>Expand to see the result</summary>
|
||||||
|
|
||||||
```text
|
```java
|
||||||
$ ./main -m models/ggml-medium.en.bin -f samples/gb1.wav -t 8
|
$ ./main -m models/ggml-medium.en.bin -f samples/gb1.wav -t 8
|
||||||
|
|
||||||
whisper_init_from_file: loading model from 'models/ggml-medium.en.bin'
|
whisper_init_from_file: loading model from 'models/ggml-medium.en.bin'
|
||||||
@ -570,7 +522,6 @@ whisper_print_timings: encode time = 18665.10 ms / 9 runs ( 2073.90 ms per
|
|||||||
whisper_print_timings: decode time = 13090.93 ms / 549 runs ( 23.85 ms per run)
|
whisper_print_timings: decode time = 13090.93 ms / 549 runs ( 23.85 ms per run)
|
||||||
whisper_print_timings: total time = 32733.52 ms
|
whisper_print_timings: total time = 32733.52 ms
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
## Real-time audio input example
|
## Real-time audio input example
|
||||||
@ -579,7 +530,7 @@ This is a naive example of performing real-time inference on audio from your mic
|
|||||||
The [stream](examples/stream) tool samples the audio every half a second and runs the transcription continuously.
|
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/ggerganov/whisper.cpp/issues/10).
|
More info is available in [issue #10](https://github.com/ggerganov/whisper.cpp/issues/10).
|
||||||
|
|
||||||
```bash
|
```java
|
||||||
make stream
|
make stream
|
||||||
./stream -m ./models/ggml-base.en.bin -t 8 --step 500 --length 5000
|
./stream -m ./models/ggml-base.en.bin -t 8 --step 500 --length 5000
|
||||||
```
|
```
|
||||||
@ -591,7 +542,7 @@ https://user-images.githubusercontent.com/1991296/194935793-76afede7-cfa8-48d8-a
|
|||||||
Adding the `--print-colors` argument will print the transcribed text using an experimental color coding strategy
|
Adding the `--print-colors` argument will print the transcribed text using an experimental color coding strategy
|
||||||
to highlight words with high or low confidence:
|
to highlight words with high or low confidence:
|
||||||
|
|
||||||
```bash
|
```java
|
||||||
./main -m models/ggml-base.en.bin -f samples/gb0.wav --print-colors
|
./main -m models/ggml-base.en.bin -f samples/gb0.wav --print-colors
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -601,8 +552,8 @@ to highlight words with high or low confidence:
|
|||||||
|
|
||||||
For example, to limit the line length to a maximum of 16 characters, simply add `-ml 16`:
|
For example, to limit the line length to a maximum of 16 characters, simply add `-ml 16`:
|
||||||
|
|
||||||
```text
|
```java
|
||||||
$ ./main -m ./models/ggml-base.en.bin -f ./samples/jfk.wav -ml 16
|
./main -m ./models/ggml-base.en.bin -f ./samples/jfk.wav -ml 16
|
||||||
|
|
||||||
whisper_model_load: loading model from './models/ggml-base.en.bin'
|
whisper_model_load: loading model from './models/ggml-base.en.bin'
|
||||||
...
|
...
|
||||||
@ -625,8 +576,8 @@ main: processing './samples/jfk.wav' (176000 samples, 11.0 sec), 4 threads, 1 pr
|
|||||||
|
|
||||||
The `--max-len` argument can be used to obtain word-level timestamps. Simply use `-ml 1`:
|
The `--max-len` argument can be used to obtain word-level timestamps. Simply use `-ml 1`:
|
||||||
|
|
||||||
```text
|
```java
|
||||||
$ ./main -m ./models/ggml-base.en.bin -f ./samples/jfk.wav -ml 1
|
./main -m ./models/ggml-base.en.bin -f ./samples/jfk.wav -ml 1
|
||||||
|
|
||||||
whisper_model_load: loading model from './models/ggml-base.en.bin'
|
whisper_model_load: loading model from './models/ggml-base.en.bin'
|
||||||
...
|
...
|
||||||
@ -696,7 +647,7 @@ This requires to have `ffmpeg` installed.
|
|||||||
|
|
||||||
Here are a few *"typical"* examples:
|
Here are a few *"typical"* examples:
|
||||||
|
|
||||||
```bash
|
```java
|
||||||
./main -m ./models/ggml-base.en.bin -f ./samples/jfk.wav -owts
|
./main -m ./models/ggml-base.en.bin -f ./samples/jfk.wav -owts
|
||||||
source ./samples/jfk.wav.wts
|
source ./samples/jfk.wav.wts
|
||||||
ffplay ./samples/jfk.wav.mp4
|
ffplay ./samples/jfk.wav.mp4
|
||||||
@ -706,7 +657,7 @@ https://user-images.githubusercontent.com/1991296/199337465-dbee4b5e-9aeb-48a3-b
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
```bash
|
```java
|
||||||
./main -m ./models/ggml-base.en.bin -f ./samples/mm0.wav -owts
|
./main -m ./models/ggml-base.en.bin -f ./samples/mm0.wav -owts
|
||||||
source ./samples/mm0.wav.wts
|
source ./samples/mm0.wav.wts
|
||||||
ffplay ./samples/mm0.wav.mp4
|
ffplay ./samples/mm0.wav.mp4
|
||||||
@ -716,7 +667,7 @@ https://user-images.githubusercontent.com/1991296/199337504-cc8fd233-0cb7-4920-9
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
```bash
|
```java
|
||||||
./main -m ./models/ggml-base.en.bin -f ./samples/gb0.wav -owts
|
./main -m ./models/ggml-base.en.bin -f ./samples/gb0.wav -owts
|
||||||
source ./samples/gb0.wav.wts
|
source ./samples/gb0.wav.wts
|
||||||
ffplay ./samples/gb0.wav.mp4
|
ffplay ./samples/gb0.wav.mp4
|
||||||
@ -730,7 +681,7 @@ https://user-images.githubusercontent.com/1991296/199337538-b7b0c7a3-2753-4a88-a
|
|||||||
|
|
||||||
Use the [extra/bench-wts.sh](https://github.com/ggerganov/whisper.cpp/blob/master/extra/bench-wts.sh) script to generate a video in the following format:
|
Use the [extra/bench-wts.sh](https://github.com/ggerganov/whisper.cpp/blob/master/extra/bench-wts.sh) script to generate a video in the following format:
|
||||||
|
|
||||||
```bash
|
```java
|
||||||
./extra/bench-wts.sh samples/jfk.wav
|
./extra/bench-wts.sh samples/jfk.wav
|
||||||
ffplay ./samples/jfk.wav.all.mp4
|
ffplay ./samples/jfk.wav.all.mp4
|
||||||
```
|
```
|
||||||
@ -747,19 +698,7 @@ took to execute it. The results are summarized in the following Github issue:
|
|||||||
|
|
||||||
[Benchmark results](https://github.com/ggerganov/whisper.cpp/issues/89)
|
[Benchmark results](https://github.com/ggerganov/whisper.cpp/issues/89)
|
||||||
|
|
||||||
Additionally a script to run whisper.cpp with different models and audio files is provided [bench.py](bench.py).
|
## ggml format
|
||||||
|
|
||||||
You can run it with the following command, by default it will run against any standard model in the models folder.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python3 extra/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:
|
The original models are converted to a custom binary format. This allows to pack everything needed into a single file:
|
||||||
|
|
||||||
@ -774,27 +713,28 @@ or manually from here:
|
|||||||
- https://huggingface.co/ggerganov/whisper.cpp
|
- https://huggingface.co/ggerganov/whisper.cpp
|
||||||
- https://ggml.ggerganov.com
|
- https://ggml.ggerganov.com
|
||||||
|
|
||||||
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).
|
For more details, see the conversion script [models/convert-pt-to-ggml.py](models/convert-pt-to-ggml.py) or the README
|
||||||
|
in [models](models).
|
||||||
|
|
||||||
## [Bindings](https://github.com/ggerganov/whisper.cpp/discussions/categories/bindings)
|
## [Bindings](https://github.com/ggerganov/whisper.cpp/discussions/categories/bindings)
|
||||||
|
|
||||||
- [x] Rust: [tazz4843/whisper-rs](https://github.com/tazz4843/whisper-rs) | [#310](https://github.com/ggerganov/whisper.cpp/discussions/310)
|
- [X] Rust: [tazz4843/whisper-rs](https://github.com/tazz4843/whisper-rs) | [#310](https://github.com/ggerganov/whisper.cpp/discussions/310)
|
||||||
- [x] JavaScript: [bindings/javascript](bindings/javascript) | [#309](https://github.com/ggerganov/whisper.cpp/discussions/309)
|
- [X] Javascript: [bindings/javascript](bindings/javascript) | [#309](https://github.com/ggerganov/whisper.cpp/discussions/309)
|
||||||
- React Native (iOS / Android): [whisper.rn](https://github.com/mybigday/whisper.rn)
|
- React Native (iOS / Android): [whisper.rn](https://github.com/mybigday/whisper.rn)
|
||||||
- [x] Go: [bindings/go](bindings/go) | [#312](https://github.com/ggerganov/whisper.cpp/discussions/312)
|
- [X] Go: [bindings/go](bindings/go) | [#312](https://github.com/ggerganov/whisper.cpp/discussions/312)
|
||||||
- [x] Java:
|
- [X] Java:
|
||||||
- [GiviMAD/whisper-jni](https://github.com/GiviMAD/whisper-jni)
|
- [GiviMAD/whisper-jni](https://github.com/GiviMAD/whisper-jni)
|
||||||
- [x] Ruby: [bindings/ruby](bindings/ruby) | [#507](https://github.com/ggerganov/whisper.cpp/discussions/507)
|
- [X] Ruby: [bindings/ruby](bindings/ruby) | [#507](https://github.com/ggerganov/whisper.cpp/discussions/507)
|
||||||
- [x] Objective-C / Swift: [ggerganov/whisper.spm](https://github.com/ggerganov/whisper.spm) | [#313](https://github.com/ggerganov/whisper.cpp/discussions/313)
|
- [X] Objective-C / Swift: [ggerganov/whisper.spm](https://github.com/ggerganov/whisper.spm) | [#313](https://github.com/ggerganov/whisper.cpp/discussions/313)
|
||||||
- [exPHAT/SwiftWhisper](https://github.com/exPHAT/SwiftWhisper)
|
- [exPHAT/SwiftWhisper](https://github.com/exPHAT/SwiftWhisper)
|
||||||
- [x] .NET: | [#422](https://github.com/ggerganov/whisper.cpp/discussions/422)
|
- [X] .NET: | [#422](https://github.com/ggerganov/whisper.cpp/discussions/422)
|
||||||
- [sandrohanea/whisper.net](https://github.com/sandrohanea/whisper.net)
|
- [sandrohanea/whisper.net](https://github.com/sandrohanea/whisper.net)
|
||||||
- [NickDarvey/whisper](https://github.com/NickDarvey/whisper)
|
- [NickDarvey/whisper](https://github.com/NickDarvey/whisper)
|
||||||
- [x] Python: | [#9](https://github.com/ggerganov/whisper.cpp/issues/9)
|
- [X] Python: | [#9](https://github.com/ggerganov/whisper.cpp/issues/9)
|
||||||
- [stlukey/whispercpp.py](https://github.com/stlukey/whispercpp.py) (Cython)
|
- [stlukey/whispercpp.py](https://github.com/stlukey/whispercpp.py) (Cython)
|
||||||
- [aarnphm/whispercpp](https://github.com/aarnphm/whispercpp) (Pybind11)
|
- [aarnphm/whispercpp](https://github.com/aarnphm/whispercpp) (Pybind11)
|
||||||
- [x] R: [bnosac/audio.whisper](https://github.com/bnosac/audio.whisper)
|
- [X] R: [bnosac/audio.whisper](https://github.com/bnosac/audio.whisper)
|
||||||
- [x] Unity: [macoron/whisper.unity](https://github.com/Macoron/whisper.unity)
|
- [X] Unity: [macoron/whisper.unity](https://github.com/Macoron/whisper.unity)
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
@ -802,12 +742,11 @@ There are various examples of using the library for different projects in the [e
|
|||||||
Some of the examples are even ported to run in the browser using WebAssembly. Check them out!
|
Some of the examples are even ported to run in the browser using WebAssembly. Check them out!
|
||||||
|
|
||||||
| Example | Web | Description |
|
| Example | Web | Description |
|
||||||
| --------------------------------------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
| --- | --- | --- |
|
||||||
| [main](examples/main) | [whisper.wasm](examples/whisper.wasm) | Tool for translating and transcribing audio using Whisper |
|
| [main](examples/main) | [whisper.wasm](examples/whisper.wasm) | Tool for translating and transcribing audio using Whisper |
|
||||||
| [bench](examples/bench) | [bench.wasm](examples/bench.wasm) | Benchmark the performance of Whisper on your machine |
|
| [bench](examples/bench) | [bench.wasm](examples/bench.wasm) | Benchmark the performance of Whisper on your machine |
|
||||||
| [stream](examples/stream) | [stream.wasm](examples/stream.wasm) | Real-time transcription of raw microphone capture |
|
| [stream](examples/stream) | [stream.wasm](examples/stream.wasm) | Real-time transcription of raw microphone capture |
|
||||||
| [command](examples/command) | [command.wasm](examples/command.wasm) | Basic voice assistant example for receiving voice commands from the mic |
|
| [command](examples/command) | [command.wasm](examples/command.wasm) | Basic voice assistant example for receiving voice commands from the mic |
|
||||||
| [wchess](examples/wchess) | [wchess.wasm](examples/wchess) | Voice-controlled chess |
|
|
||||||
| [talk](examples/talk) | [talk.wasm](examples/talk.wasm) | Talk with a GPT-2 bot |
|
| [talk](examples/talk) | [talk.wasm](examples/talk.wasm) | Talk with a GPT-2 bot |
|
||||||
| [talk-llama](examples/talk-llama) | | Talk with a LLaMA bot |
|
| [talk-llama](examples/talk-llama) | | Talk with a LLaMA bot |
|
||||||
| [whisper.objc](examples/whisper.objc) | | iOS mobile application using whisper.cpp |
|
| [whisper.objc](examples/whisper.objc) | | iOS mobile application using whisper.cpp |
|
||||||
@ -817,7 +756,6 @@ Some of the examples are even ported to run in the browser using WebAssembly. Ch
|
|||||||
| [generate-karaoke.sh](examples/generate-karaoke.sh) | | Helper script to easily [generate a karaoke video](https://youtu.be/uj7hVta4blM) of raw audio capture |
|
| [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/ggerganov/whisper.cpp/issues/185) |
|
| [livestream.sh](examples/livestream.sh) | | [Livestream audio transcription](https://github.com/ggerganov/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) |
|
| [yt-wsp.sh](examples/yt-wsp.sh) | | Download + transcribe and/or translate any VOD [(original)](https://gist.github.com/DaniruKun/96f763ec1a037cc92fe1a059b643b818) |
|
||||||
| [server](examples/server) | | HTTP transcription server with OAI-like API |
|
|
||||||
|
|
||||||
## [Discussions](https://github.com/ggerganov/whisper.cpp/discussions)
|
## [Discussions](https://github.com/ggerganov/whisper.cpp/discussions)
|
||||||
|
|
||||||
|
@ -1,26 +1,9 @@
|
|||||||
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
|
BUILD_DIR := build
|
||||||
MODELS_DIR := models
|
MODELS_DIR := models
|
||||||
EXAMPLES_DIR := $(wildcard examples/*)
|
EXAMPLES_DIR := $(wildcard examples/*)
|
||||||
INCLUDE_PATH := $(abspath ../..)
|
INCLUDE_PATH := $(abspath ../..)
|
||||||
LIBRARY_PATH := $(abspath ../..)
|
LIBRARY_PATH := $(abspath ../..)
|
||||||
|
|
||||||
ifeq ($(UNAME_S),Darwin)
|
|
||||||
EXT_LDFLAGS := -framework Foundation -framework Metal -framework MetalKit
|
|
||||||
endif
|
|
||||||
|
|
||||||
all: clean whisper examples
|
all: clean whisper examples
|
||||||
|
|
||||||
whisper: mkdir
|
whisper: mkdir
|
||||||
@ -28,13 +11,8 @@ whisper: mkdir
|
|||||||
@${MAKE} -C ../.. libwhisper.a
|
@${MAKE} -C ../.. libwhisper.a
|
||||||
|
|
||||||
test: model-small whisper modtidy
|
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 .
|
||||||
@C_INCLUDE_PATH=${INCLUDE_PATH} LIBRARY_PATH=${LIBRARY_PATH} go test -v ./pkg/whisper/...
|
@C_INCLUDE_PATH=${INCLUDE_PATH} LIBRARY_PATH=${LIBRARY_PATH} go test -v ./pkg/whisper/...
|
||||||
endif
|
|
||||||
|
|
||||||
examples: $(EXAMPLES_DIR)
|
examples: $(EXAMPLES_DIR)
|
||||||
|
|
||||||
@ -43,11 +21,7 @@ model-small: mkdir examples/go-model-download
|
|||||||
|
|
||||||
$(EXAMPLES_DIR): mkdir whisper modtidy
|
$(EXAMPLES_DIR): mkdir whisper modtidy
|
||||||
@echo Build example $(notdir $@)
|
@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 $@) ./$@
|
@C_INCLUDE_PATH=${INCLUDE_PATH} LIBRARY_PATH=${LIBRARY_PATH} go build ${BUILD_FLAGS} -o ${BUILD_DIR}/$(notdir $@) ./$@
|
||||||
endif
|
|
||||||
|
|
||||||
mkdir:
|
mkdir:
|
||||||
@echo Mkdir ${BUILD_DIR}
|
@echo Mkdir ${BUILD_DIR}
|
||||||
|
@ -24,7 +24,7 @@ const (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
// The models which will be downloaded, if no model is specified as an argument
|
// The models which will be downloaded, if no model is specified as an argument
|
||||||
modelNames = []string{"ggml-tiny.en", "ggml-tiny", "ggml-base.en", "ggml-base", "ggml-small.en", "ggml-small", "ggml-medium.en", "ggml-medium", "ggml-large-v1", "ggml-large-v2", "ggml-large-v3"}
|
modelNames = []string{"ggml-tiny.en", "ggml-tiny", "ggml-base.en", "ggml-base", "ggml-small.en", "ggml-small", "ggml-medium.en", "ggml-medium", "ggml-large-v1", "ggml-large"}
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -118,16 +118,6 @@ func (p *Params) SetMaxTokensPerSegment(n int) {
|
|||||||
p.max_tokens = C.int(n)
|
p.max_tokens = C.int(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set audio encoder context
|
|
||||||
func (p *Params) SetAudioCtx(n int) {
|
|
||||||
p.audio_ctx = C.int(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set initial prompt
|
|
||||||
func (p *Params) SetInitialPrompt(prompt string) {
|
|
||||||
p.initial_prompt = C.CString(prompt)
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// PRIVATE METHODS
|
// PRIVATE METHODS
|
||||||
|
|
||||||
@ -151,8 +141,6 @@ func (p *Params) String() string {
|
|||||||
str += fmt.Sprintf(" n_max_text_ctx=%d", p.n_max_text_ctx)
|
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(" offset_ms=%d", p.offset_ms)
|
||||||
str += fmt.Sprintf(" duration_ms=%d", p.duration_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))
|
|
||||||
if p.translate {
|
if p.translate {
|
||||||
str += " translate"
|
str += " translate"
|
||||||
}
|
}
|
||||||
|
@ -125,16 +125,6 @@ func (context *context) SetMaxTokensPerSegment(n uint) {
|
|||||||
context.params.SetMaxTokensPerSegment(int(n))
|
context.params.SetMaxTokensPerSegment(int(n))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set audio encoder context
|
|
||||||
func (context *context) SetAudioCtx(n uint) {
|
|
||||||
context.params.SetAudioCtx(int(n))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set initial prompt
|
|
||||||
func (context *context) SetInitialPrompt(prompt string) {
|
|
||||||
context.params.SetInitialPrompt(prompt)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResetTimings resets the mode timings. Should be called before processing
|
// ResetTimings resets the mode timings. Should be called before processing
|
||||||
func (context *context) ResetTimings() {
|
func (context *context) ResetTimings() {
|
||||||
context.model.ctx.Whisper_reset_timings()
|
context.model.ctx.Whisper_reset_timings()
|
||||||
|
@ -48,8 +48,6 @@ type Context interface {
|
|||||||
SetMaxSegmentLength(uint) // Set max segment length in characters
|
SetMaxSegmentLength(uint) // Set max segment length in characters
|
||||||
SetTokenTimestamps(bool) // Set token timestamps flag
|
SetTokenTimestamps(bool) // Set token timestamps flag
|
||||||
SetMaxTokensPerSegment(uint) // Set max tokens per segment (0 = no limit)
|
SetMaxTokensPerSegment(uint) // Set max tokens per segment (0 = no limit)
|
||||||
SetAudioCtx(uint) // Set audio encoder context
|
|
||||||
SetInitialPrompt(prompt string) // Set initial prompt
|
|
||||||
|
|
||||||
// Process mono audio data and return any errors.
|
// Process mono audio data and return any errors.
|
||||||
// If defined, newly generated segments are passed to the
|
// If defined, newly generated segments are passed to the
|
||||||
|
@ -83,6 +83,7 @@ const (
|
|||||||
SampleRate = C.WHISPER_SAMPLE_RATE // Expected sample rate, samples per second
|
SampleRate = C.WHISPER_SAMPLE_RATE // Expected sample rate, samples per second
|
||||||
SampleBits = uint16(unsafe.Sizeof(C.float(0))) * 8 // Sample size in bits
|
SampleBits = uint16(unsafe.Sizeof(C.float(0))) * 8 // Sample size in bits
|
||||||
NumFFT = C.WHISPER_N_FFT
|
NumFFT = C.WHISPER_N_FFT
|
||||||
|
NumMEL = C.WHISPER_N_MEL
|
||||||
HopLength = C.WHISPER_HOP_LENGTH
|
HopLength = C.WHISPER_HOP_LENGTH
|
||||||
ChunkSize = C.WHISPER_CHUNK_SIZE
|
ChunkSize = C.WHISPER_CHUNK_SIZE
|
||||||
)
|
)
|
||||||
@ -102,7 +103,7 @@ var (
|
|||||||
func Whisper_init(path string) *Context {
|
func Whisper_init(path string) *Context {
|
||||||
cPath := C.CString(path)
|
cPath := C.CString(path)
|
||||||
defer C.free(unsafe.Pointer(cPath))
|
defer C.free(unsafe.Pointer(cPath))
|
||||||
if ctx := C.whisper_init_from_file_with_params(cPath, C.whisper_context_default_params()); ctx != nil {
|
if ctx := C.whisper_init_from_file(cPath); ctx != nil {
|
||||||
return (*Context)(ctx)
|
return (*Context)(ctx)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
|
Submodule bindings/ios updated: b21b6ff325...de46d9e781
@ -9,7 +9,6 @@ archivesBaseName = 'whispercpp'
|
|||||||
group = 'io.github.ggerganov'
|
group = 'io.github.ggerganov'
|
||||||
version = '1.4.0'
|
version = '1.4.0'
|
||||||
|
|
||||||
|
|
||||||
sourceCompatibility = 1.8
|
sourceCompatibility = 1.8
|
||||||
targetCompatibility = 1.8
|
targetCompatibility = 1.8
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@ import com.sun.jna.Structure;
|
|||||||
import com.sun.jna.ptr.PointerByReference;
|
import com.sun.jna.ptr.PointerByReference;
|
||||||
import io.github.ggerganov.whispercpp.ggml.GgmlType;
|
import io.github.ggerganov.whispercpp.ggml.GgmlType;
|
||||||
import io.github.ggerganov.whispercpp.WhisperModel;
|
import io.github.ggerganov.whispercpp.WhisperModel;
|
||||||
import io.github.ggerganov.whispercpp.params.WhisperContextParams;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -24,9 +23,8 @@ public class WhisperContext extends Structure {
|
|||||||
public PointerByReference vocab;
|
public PointerByReference vocab;
|
||||||
public PointerByReference state;
|
public PointerByReference state;
|
||||||
|
|
||||||
/** populated by whisper_init_from_file_with_params() */
|
/** populated by whisper_init_from_file() */
|
||||||
String path_model;
|
String path_model;
|
||||||
WhisperContextParams params;
|
|
||||||
|
|
||||||
// public static class ByReference extends WhisperContext implements Structure.ByReference {
|
// public static class ByReference extends WhisperContext implements Structure.ByReference {
|
||||||
// }
|
// }
|
||||||
|
@ -2,16 +2,12 @@ package io.github.ggerganov.whispercpp;
|
|||||||
|
|
||||||
import com.sun.jna.Native;
|
import com.sun.jna.Native;
|
||||||
import com.sun.jna.Pointer;
|
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.WhisperFullParams;
|
||||||
import io.github.ggerganov.whispercpp.params.WhisperSamplingStrategy;
|
import io.github.ggerganov.whispercpp.params.WhisperSamplingStrategy;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
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.
|
* Before calling most methods, you must call `initContext(modelPath)` to initialise the `ctx` Pointer.
|
||||||
@ -19,9 +15,8 @@ import java.util.List;
|
|||||||
public class WhisperCpp implements AutoCloseable {
|
public class WhisperCpp implements AutoCloseable {
|
||||||
private WhisperCppJnaLibrary lib = WhisperCppJnaLibrary.instance;
|
private WhisperCppJnaLibrary lib = WhisperCppJnaLibrary.instance;
|
||||||
private Pointer ctx = null;
|
private Pointer ctx = null;
|
||||||
private Pointer paramsPointer = null;
|
private Pointer greedyPointer = null;
|
||||||
private Pointer greedyParamsPointer = null;
|
private Pointer beamPointer = null;
|
||||||
private Pointer beamParamsPointer = null;
|
|
||||||
|
|
||||||
public File modelDir() {
|
public File modelDir() {
|
||||||
String modelDirPath = System.getenv("XDG_CACHE_HOME");
|
String modelDirPath = System.getenv("XDG_CACHE_HOME");
|
||||||
@ -36,18 +31,6 @@ public class WhisperCpp implements AutoCloseable {
|
|||||||
* @param modelPath - absolute path, or just the name (eg: "base", "base-en" or "base.en")
|
* @param modelPath - absolute path, or just the name (eg: "base", "base-en" or "base.en")
|
||||||
*/
|
*/
|
||||||
public void initContext(String modelPath) throws FileNotFoundException {
|
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 params) throws FileNotFoundException {
|
|
||||||
initContextImpl(modelPath, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initContextImpl(String modelPath, WhisperContextParams params) throws FileNotFoundException {
|
|
||||||
if (ctx != null) {
|
if (ctx != null) {
|
||||||
lib.whisper_free(ctx);
|
lib.whisper_free(ctx);
|
||||||
}
|
}
|
||||||
@ -60,26 +43,13 @@ public class WhisperCpp implements AutoCloseable {
|
|||||||
modelPath = new File(modelDir(), modelPath).getAbsolutePath();
|
modelPath = new File(modelDir(), modelPath).getAbsolutePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx = lib.whisper_init_from_file_with_params(modelPath, params);
|
ctx = lib.whisper_init_from_file(modelPath);
|
||||||
|
|
||||||
if (ctx == null) {
|
if (ctx == null) {
|
||||||
throw new FileNotFoundException(modelPath);
|
throw new FileNotFoundException(modelPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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));`
|
|
||||||
*/
|
|
||||||
public WhisperContextParams getContextDefaultParams() {
|
|
||||||
paramsPointer = lib.whisper_context_default_params_by_ref();
|
|
||||||
WhisperContextParams params = new WhisperContextParams(paramsPointer);
|
|
||||||
params.read();
|
|
||||||
return params;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides default params which can be used with `whisper_full()` etc.
|
* Provides default params which can be used with `whisper_full()` etc.
|
||||||
* Because this function allocates memory for the params, the caller must call either:
|
* Because this function allocates memory for the params, the caller must call either:
|
||||||
@ -93,15 +63,15 @@ public class WhisperCpp implements AutoCloseable {
|
|||||||
|
|
||||||
// whisper_full_default_params_by_ref allocates memory which we need to delete, so only create max 1 pointer for each strategy.
|
// 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 (strategy == WhisperSamplingStrategy.WHISPER_SAMPLING_GREEDY) {
|
||||||
if (greedyParamsPointer == null) {
|
if (greedyPointer == null) {
|
||||||
greedyParamsPointer = lib.whisper_full_default_params_by_ref(strategy.ordinal());
|
greedyPointer = lib.whisper_full_default_params_by_ref(strategy.ordinal());
|
||||||
}
|
}
|
||||||
pointer = greedyParamsPointer;
|
pointer = greedyPointer;
|
||||||
} else {
|
} else {
|
||||||
if (beamParamsPointer == null) {
|
if (beamPointer == null) {
|
||||||
beamParamsPointer = lib.whisper_full_default_params_by_ref(strategy.ordinal());
|
beamPointer = lib.whisper_full_default_params_by_ref(strategy.ordinal());
|
||||||
}
|
}
|
||||||
pointer = beamParamsPointer;
|
pointer = beamPointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
WhisperFullParams params = new WhisperFullParams(pointer);
|
WhisperFullParams params = new WhisperFullParams(pointer);
|
||||||
@ -123,17 +93,13 @@ public class WhisperCpp implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void freeParams() {
|
private void freeParams() {
|
||||||
if (paramsPointer != null) {
|
if (greedyPointer != null) {
|
||||||
Native.free(Pointer.nativeValue(paramsPointer));
|
Native.free(Pointer.nativeValue(greedyPointer));
|
||||||
paramsPointer = null;
|
greedyPointer = null;
|
||||||
}
|
}
|
||||||
if (greedyParamsPointer != null) {
|
if (beamPointer != null) {
|
||||||
Native.free(Pointer.nativeValue(greedyParamsPointer));
|
Native.free(Pointer.nativeValue(beamPointer));
|
||||||
greedyParamsPointer = null;
|
beamPointer = null;
|
||||||
}
|
|
||||||
if (beamParamsPointer != null) {
|
|
||||||
Native.free(Pointer.nativeValue(beamParamsPointer));
|
|
||||||
beamParamsPointer = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,28 +129,6 @@ public class WhisperCpp implements AutoCloseable {
|
|||||||
|
|
||||||
return str.toString().trim();
|
return str.toString().trim();
|
||||||
}
|
}
|
||||||
public List<WhisperSegment> fullTranscribeWithTime(WhisperFullParams 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) {
|
// public int getTextSegmentCount(Pointer ctx) {
|
||||||
// return lib.whisper_full_n_segments(ctx);
|
// return lib.whisper_full_n_segments(ctx);
|
||||||
|
@ -5,7 +5,6 @@ import com.sun.jna.Native;
|
|||||||
import com.sun.jna.Pointer;
|
import com.sun.jna.Pointer;
|
||||||
import io.github.ggerganov.whispercpp.model.WhisperModelLoader;
|
import io.github.ggerganov.whispercpp.model.WhisperModelLoader;
|
||||||
import io.github.ggerganov.whispercpp.model.WhisperTokenData;
|
import io.github.ggerganov.whispercpp.model.WhisperTokenData;
|
||||||
import io.github.ggerganov.whispercpp.params.WhisperContextParams;
|
|
||||||
import io.github.ggerganov.whispercpp.params.WhisperFullParams;
|
import io.github.ggerganov.whispercpp.params.WhisperFullParams;
|
||||||
|
|
||||||
public interface WhisperCppJnaLibrary extends Library {
|
public interface WhisperCppJnaLibrary extends Library {
|
||||||
@ -14,32 +13,13 @@ public interface WhisperCppJnaLibrary extends Library {
|
|||||||
String whisper_print_system_info();
|
String whisper_print_system_info();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DEPRECATED. Allocate (almost) all memory needed for the model by loading from a file.
|
* Allocate (almost) all memory needed for the model by loading from a file.
|
||||||
*
|
*
|
||||||
* @param path_model Path to the model file
|
* @param path_model Path to the model file
|
||||||
* @return Whisper context on success, null on failure
|
* @return Whisper context on success, null on failure
|
||||||
*/
|
*/
|
||||||
Pointer whisper_init_from_file(String path_model);
|
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 params);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocate (almost) all memory needed for the model by loading from a buffer.
|
* Allocate (almost) all memory needed for the model by loading from a buffer.
|
||||||
*
|
*
|
||||||
|
@ -1,47 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Use GPU for inference Number (default = true) */
|
|
||||||
public CBool use_gpu;
|
|
||||||
|
|
||||||
/** Use GPU for inference Number (default = true) */
|
|
||||||
public void useGpu(boolean enable) {
|
|
||||||
use_gpu = enable ? CBool.TRUE : CBool.FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected List<String> getFieldOrder() {
|
|
||||||
return Arrays.asList("use_gpu");
|
|
||||||
}
|
|
||||||
}
|
|
@ -58,9 +58,6 @@ public class WhisperFullParams extends Structure {
|
|||||||
no_context = enable ? CBool.FALSE : CBool.TRUE;
|
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) */
|
/** Flag to force single segment output (useful for streaming). (default = false) */
|
||||||
public CBool single_segment;
|
public CBool single_segment;
|
||||||
|
|
||||||
@ -307,16 +304,10 @@ public class WhisperFullParams extends Structure {
|
|||||||
logits_filter_callback = CallbackReference.getFunctionPointer(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
|
@Override
|
||||||
protected List<String> getFieldOrder() {
|
protected List<String> getFieldOrder() {
|
||||||
return Arrays.asList("strategy", "n_threads", "n_max_text_ctx", "offset_ms", "duration_ms", "translate",
|
return Arrays.asList("strategy", "n_threads", "n_max_text_ctx", "offset_ms", "duration_ms", "translate",
|
||||||
"no_context", "single_segment", "no_timestamps",
|
"no_context", "single_segment",
|
||||||
"print_special", "print_progress", "print_realtime", "print_timestamps", "token_timestamps",
|
"print_special", "print_progress", "print_realtime", "print_timestamps", "token_timestamps",
|
||||||
"thold_pt", "thold_ptsum", "max_len", "split_on_word", "max_tokens", "speed_up", "audio_ctx",
|
"thold_pt", "thold_ptsum", "max_len", "split_on_word", "max_tokens", "speed_up", "audio_ctx",
|
||||||
"tdrz_enable", "initial_prompt", "prompt_tokens", "prompt_n_tokens", "language", "detect_language",
|
"tdrz_enable", "initial_prompt", "prompt_tokens", "prompt_n_tokens", "language", "detect_language",
|
||||||
@ -325,7 +316,6 @@ public class WhisperFullParams extends Structure {
|
|||||||
"new_segment_callback", "new_segment_callback_user_data",
|
"new_segment_callback", "new_segment_callback_user_data",
|
||||||
"progress_callback", "progress_callback_user_data",
|
"progress_callback", "progress_callback_user_data",
|
||||||
"encoder_begin_callback", "encoder_begin_callback_user_data",
|
"encoder_begin_callback", "encoder_begin_callback_user_data",
|
||||||
"logits_filter_callback", "logits_filter_callback_user_data",
|
"logits_filter_callback", "logits_filter_callback_user_data");
|
||||||
"grammar_rules", "n_grammar_rules", "i_start_rule", "grammar_penalty");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package io.github.ggerganov.whispercpp;
|
|||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
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.CBool;
|
||||||
import io.github.ggerganov.whispercpp.params.WhisperFullParams;
|
import io.github.ggerganov.whispercpp.params.WhisperFullParams;
|
||||||
import io.github.ggerganov.whispercpp.params.WhisperSamplingStrategy;
|
import io.github.ggerganov.whispercpp.params.WhisperSamplingStrategy;
|
||||||
@ -12,7 +11,6 @@ import javax.sound.sampled.AudioInputStream;
|
|||||||
import javax.sound.sampled.AudioSystem;
|
import javax.sound.sampled.AudioSystem;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
class WhisperCppTest {
|
class WhisperCppTest {
|
||||||
private static WhisperCpp whisper = new WhisperCpp();
|
private static WhisperCpp whisper = new WhisperCpp();
|
||||||
@ -22,12 +20,11 @@ class WhisperCppTest {
|
|||||||
static void init() throws FileNotFoundException {
|
static void init() throws FileNotFoundException {
|
||||||
// By default, models are loaded from ~/.cache/whisper/ and are usually named "ggml-${name}.bin"
|
// 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.
|
// or you can provide the absolute path to the model file.
|
||||||
//String modelName = "../../models/ggml-tiny.bin";
|
|
||||||
String modelName = "../../models/ggml-tiny.en.bin";
|
String modelName = "../../models/ggml-tiny.en.bin";
|
||||||
try {
|
try {
|
||||||
whisper.initContext(modelName);
|
whisper.initContext(modelName);
|
||||||
//whisper.getFullDefaultParams(WhisperSamplingStrategy.WHISPER_SAMPLING_GREEDY);
|
// whisper.getFullDefaultParams(WhisperSamplingStrategy.WHISPER_SAMPLING_GREEDY);
|
||||||
//whisper.getJavaDefaultParams(WhisperSamplingStrategy.WHISPER_SAMPLING_BEAM_SEARCH);
|
// whisper.getJavaDefaultParams(WhisperSamplingStrategy.WHISPER_SAMPLING_BEAM_SEARCH);
|
||||||
modelInitialised = true;
|
modelInitialised = true;
|
||||||
} catch (FileNotFoundException ex) {
|
} catch (FileNotFoundException ex) {
|
||||||
System.out.println("Model " + modelName + " not found");
|
System.out.println("Model " + modelName + " not found");
|
||||||
@ -45,7 +42,7 @@ class WhisperCppTest {
|
|||||||
assertEquals(16384, params.n_max_text_ctx);
|
assertEquals(16384, params.n_max_text_ctx);
|
||||||
assertFalse(params.translate);
|
assertFalse(params.translate);
|
||||||
assertEquals(0.01f, params.thold_pt);
|
assertEquals(0.01f, params.thold_pt);
|
||||||
assertEquals(5, params.beam_search.beam_size);
|
assertEquals(2, params.beam_search.beam_size);
|
||||||
assertEquals(-1.0f, params.beam_search.patience);
|
assertEquals(-1.0f, params.beam_search.patience);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,7 +55,7 @@ class WhisperCppTest {
|
|||||||
assertEquals(WhisperSamplingStrategy.WHISPER_SAMPLING_GREEDY.ordinal(), params.strategy);
|
assertEquals(WhisperSamplingStrategy.WHISPER_SAMPLING_GREEDY.ordinal(), params.strategy);
|
||||||
assertNotEquals(0, params.n_threads);
|
assertNotEquals(0, params.n_threads);
|
||||||
assertEquals(16384, params.n_max_text_ctx);
|
assertEquals(16384, params.n_max_text_ctx);
|
||||||
assertEquals(5, params.greedy.best_of);
|
assertEquals(2, params.greedy.best_of);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -75,11 +72,11 @@ class WhisperCppTest {
|
|||||||
byte[] b = new byte[audioInputStream.available()];
|
byte[] b = new byte[audioInputStream.available()];
|
||||||
float[] floats = new float[b.length / 2];
|
float[] floats = new float[b.length / 2];
|
||||||
|
|
||||||
//WhisperFullParams params = whisper.getFullDefaultParams(WhisperSamplingStrategy.WHISPER_SAMPLING_GREEDY);
|
// WhisperFullParams params = whisper.getFullDefaultParams(WhisperSamplingStrategy.WHISPER_SAMPLING_GREEDY);
|
||||||
WhisperFullParams params = whisper.getFullDefaultParams(WhisperSamplingStrategy.WHISPER_SAMPLING_BEAM_SEARCH);
|
WhisperFullParams params = whisper.getFullDefaultParams(WhisperSamplingStrategy.WHISPER_SAMPLING_BEAM_SEARCH);
|
||||||
params.setProgressCallback((ctx, state, progress, user_data) -> System.out.println("progress: " + progress));
|
params.setProgressCallback((ctx, state, progress, user_data) -> System.out.println("progress: " + progress));
|
||||||
params.print_progress = CBool.FALSE;
|
params.print_progress = CBool.FALSE;
|
||||||
//params.initial_prompt = "and so my fellow Americans um, like";
|
// params.initial_prompt = "and so my fellow Americans um, like";
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -102,43 +99,4 @@ class WhisperCppTest {
|
|||||||
audioInputStream.close();
|
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 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ make publish-npm
|
|||||||
|
|
||||||
## Sample run
|
## Sample run
|
||||||
|
|
||||||
```text
|
```java
|
||||||
$ node --experimental-wasm-threads --experimental-wasm-simd ../tests/test-whisper.js
|
$ node --experimental-wasm-threads --experimental-wasm-simd ../tests/test-whisper.js
|
||||||
|
|
||||||
whisper_model_load: loading model from 'whisper.bin'
|
whisper_model_load: loading model from 'whisper.bin'
|
||||||
|
@ -20,7 +20,7 @@ struct whisper_context * g_context;
|
|||||||
EMSCRIPTEN_BINDINGS(whisper) {
|
EMSCRIPTEN_BINDINGS(whisper) {
|
||||||
emscripten::function("init", emscripten::optional_override([](const std::string & path_model) {
|
emscripten::function("init", emscripten::optional_override([](const std::string & path_model) {
|
||||||
if (g_context == nullptr) {
|
if (g_context == nullptr) {
|
||||||
g_context = whisper_init_from_file_with_params(path_model.c_str(), whisper_context_default_params());
|
g_context = whisper_init_from_file(path_model.c_str());
|
||||||
if (g_context != nullptr) {
|
if (g_context != nullptr) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "whisper.cpp",
|
"name": "whisper.cpp",
|
||||||
"version": "1.5.4",
|
"version": "1.4.2",
|
||||||
"description": "Whisper speech recognition",
|
"description": "Whisper speech recognition",
|
||||||
"main": "whisper.js",
|
"main": "whisper.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
File diff suppressed because one or more lines are too long
2
bindings/ruby/ext/.gitignore
vendored
2
bindings/ruby/ext/.gitignore
vendored
@ -1,8 +1,6 @@
|
|||||||
Makefile
|
Makefile
|
||||||
ggml.c
|
ggml.c
|
||||||
ggml.h
|
ggml.h
|
||||||
ggml-alloc.c
|
|
||||||
ggml-alloc.h
|
|
||||||
whisper.bundle
|
whisper.bundle
|
||||||
whisper.cpp
|
whisper.cpp
|
||||||
whisper.h
|
whisper.h
|
||||||
|
@ -3,14 +3,6 @@ system("cp #{File.join(File.dirname(__FILE__),'..','..','..','whisper.cpp')} .")
|
|||||||
system("cp #{File.join(File.dirname(__FILE__),'..','..','..','whisper.h')} .")
|
system("cp #{File.join(File.dirname(__FILE__),'..','..','..','whisper.h')} .")
|
||||||
system("cp #{File.join(File.dirname(__FILE__),'..','..','..','ggml.h')} .")
|
system("cp #{File.join(File.dirname(__FILE__),'..','..','..','ggml.h')} .")
|
||||||
system("cp #{File.join(File.dirname(__FILE__),'..','..','..','ggml.c')} .")
|
system("cp #{File.join(File.dirname(__FILE__),'..','..','..','ggml.c')} .")
|
||||||
system("cp #{File.join(File.dirname(__FILE__),'..','..','..','ggml-impl.h')} .")
|
|
||||||
system("cp #{File.join(File.dirname(__FILE__),'..','..','..','ggml-alloc.h')} .")
|
|
||||||
system("cp #{File.join(File.dirname(__FILE__),'..','..','..','ggml-alloc.c')} .")
|
|
||||||
system("cp #{File.join(File.dirname(__FILE__),'..','..','..','ggml-backend-impl.h')} .")
|
|
||||||
system("cp #{File.join(File.dirname(__FILE__),'..','..','..','ggml-backend.h')} .")
|
|
||||||
system("cp #{File.join(File.dirname(__FILE__),'..','..','..','ggml-backend.c')} .")
|
|
||||||
system("cp #{File.join(File.dirname(__FILE__),'..','..','..','ggml-quants.h')} .")
|
|
||||||
system("cp #{File.join(File.dirname(__FILE__),'..','..','..','ggml-quants.c')} .")
|
|
||||||
system("cp #{File.join(File.dirname(__FILE__),'..','..','..','examples','dr_wav.h')} .")
|
system("cp #{File.join(File.dirname(__FILE__),'..','..','..','examples','dr_wav.h')} .")
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,87 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// ggml-backend internal header
|
|
||||||
|
|
||||||
#include "ggml-backend.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//
|
|
||||||
// Backend buffer
|
|
||||||
//
|
|
||||||
|
|
||||||
typedef void * ggml_backend_buffer_context_t;
|
|
||||||
|
|
||||||
struct ggml_backend_buffer_i {
|
|
||||||
void (*free_buffer) (ggml_backend_buffer_t buffer);
|
|
||||||
void * (*get_base) (ggml_backend_buffer_t buffer); // get base pointer
|
|
||||||
size_t (*get_alloc_size)(ggml_backend_buffer_t buffer, struct ggml_tensor * tensor); // pre-allocation callback
|
|
||||||
void (*init_tensor) (ggml_backend_buffer_t buffer, struct ggml_tensor * tensor); // post-allocation callback
|
|
||||||
void (*free_tensor) (ggml_backend_buffer_t buffer, struct ggml_tensor * tensor); // pre-free callback
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ggml_backend_buffer {
|
|
||||||
struct ggml_backend_buffer_i iface;
|
|
||||||
|
|
||||||
ggml_backend_t backend;
|
|
||||||
ggml_backend_buffer_context_t context;
|
|
||||||
|
|
||||||
size_t size;
|
|
||||||
};
|
|
||||||
|
|
||||||
GGML_API ggml_backend_buffer_t ggml_backend_buffer_init(
|
|
||||||
struct ggml_backend * backend,
|
|
||||||
struct ggml_backend_buffer_i iface,
|
|
||||||
ggml_backend_buffer_context_t context,
|
|
||||||
size_t size);
|
|
||||||
|
|
||||||
//
|
|
||||||
// Backend
|
|
||||||
//
|
|
||||||
|
|
||||||
typedef void * ggml_backend_context_t;
|
|
||||||
|
|
||||||
struct ggml_backend_i {
|
|
||||||
const char * (*get_name)(ggml_backend_t backend);
|
|
||||||
|
|
||||||
void (*free)(ggml_backend_t backend);
|
|
||||||
|
|
||||||
// buffer allocation
|
|
||||||
ggml_backend_buffer_t (*alloc_buffer)(ggml_backend_t backend, size_t size);
|
|
||||||
|
|
||||||
// get buffer alignment
|
|
||||||
size_t (*get_alignment)(ggml_backend_t backend);
|
|
||||||
|
|
||||||
// tensor data access
|
|
||||||
// these functions can be asynchronous, helper functions are provided for synchronous access that automatically call synchronize
|
|
||||||
void (*set_tensor_async)(ggml_backend_t backend, struct ggml_tensor * tensor, const void * data, size_t offset, size_t size);
|
|
||||||
void (*get_tensor_async)(ggml_backend_t backend, const struct ggml_tensor * tensor, void * data, size_t offset, size_t size);
|
|
||||||
void (*synchronize) (ggml_backend_t backend);
|
|
||||||
|
|
||||||
// (optional) copy tensor between different backends, allow for single-copy tranfers
|
|
||||||
void (*cpy_tensor_from)(ggml_backend_t backend, struct ggml_tensor * src, struct ggml_tensor * dst);
|
|
||||||
void (*cpy_tensor_to) (ggml_backend_t backend, struct ggml_tensor * src, struct ggml_tensor * dst);
|
|
||||||
|
|
||||||
// compute graph with a plan
|
|
||||||
ggml_backend_graph_plan_t (*graph_plan_create) (ggml_backend_t backend, struct ggml_cgraph * cgraph);
|
|
||||||
void (*graph_plan_free) (ggml_backend_t backend, ggml_backend_graph_plan_t plan);
|
|
||||||
void (*graph_plan_compute)(ggml_backend_t backend, ggml_backend_graph_plan_t plan);
|
|
||||||
|
|
||||||
// compute graph without a plan
|
|
||||||
bool (*graph_compute)(ggml_backend_t backend, struct ggml_cgraph * cgraph);
|
|
||||||
|
|
||||||
// check if the backend supports an operation
|
|
||||||
bool (*supports_op)(ggml_backend_t backend, const struct ggml_tensor * op);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ggml_backend {
|
|
||||||
struct ggml_backend_i iface;
|
|
||||||
|
|
||||||
ggml_backend_context_t context;
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -1,950 +0,0 @@
|
|||||||
#include "ggml-backend-impl.h"
|
|
||||||
#include "ggml-alloc.h"
|
|
||||||
#include "ggml-impl.h"
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#define UNUSED GGML_UNUSED
|
|
||||||
|
|
||||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
|
||||||
|
|
||||||
// backend buffer
|
|
||||||
|
|
||||||
ggml_backend_buffer_t ggml_backend_buffer_init(
|
|
||||||
struct ggml_backend * backend,
|
|
||||||
struct ggml_backend_buffer_i iface,
|
|
||||||
ggml_backend_buffer_context_t context,
|
|
||||||
size_t size) {
|
|
||||||
ggml_backend_buffer_t buffer = malloc(sizeof(struct ggml_backend_buffer));
|
|
||||||
|
|
||||||
GGML_ASSERT(iface.get_base != NULL);
|
|
||||||
|
|
||||||
(*buffer) = (struct ggml_backend_buffer) {
|
|
||||||
/* .interface = */ iface,
|
|
||||||
/* .backend = */ backend,
|
|
||||||
/* .context = */ context,
|
|
||||||
/* .size = */ size,
|
|
||||||
};
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ggml_backend_buffer_free(ggml_backend_buffer_t buffer) {
|
|
||||||
if (buffer == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffer->iface.free_buffer != NULL) {
|
|
||||||
buffer->iface.free_buffer(buffer);
|
|
||||||
}
|
|
||||||
free(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t ggml_backend_buffer_get_alignment(ggml_backend_buffer_t buffer) {
|
|
||||||
return ggml_backend_get_alignment(buffer->backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t ggml_backend_buffer_get_size(ggml_backend_buffer_t buffer) {
|
|
||||||
return buffer->size;
|
|
||||||
}
|
|
||||||
|
|
||||||
void * ggml_backend_buffer_get_base(ggml_backend_buffer_t buffer) {
|
|
||||||
void * base = buffer->iface.get_base(buffer);
|
|
||||||
|
|
||||||
GGML_ASSERT(base != NULL && "backend buffer base cannot be NULL");
|
|
||||||
|
|
||||||
return base;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t ggml_backend_buffer_get_alloc_size(ggml_backend_buffer_t buffer, struct ggml_tensor * tensor) {
|
|
||||||
// get_alloc_size is optional, defaults to ggml_nbytes
|
|
||||||
if (buffer->iface.get_alloc_size) {
|
|
||||||
return buffer->iface.get_alloc_size(buffer, tensor);
|
|
||||||
}
|
|
||||||
return ggml_nbytes(tensor);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ggml_backend_buffer_init_tensor(ggml_backend_buffer_t buffer, struct ggml_tensor * tensor) {
|
|
||||||
// init_tensor is optional
|
|
||||||
if (buffer->iface.init_tensor) {
|
|
||||||
buffer->iface.init_tensor(buffer, tensor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ggml_backend_buffer_free_tensor(ggml_backend_buffer_t buffer, struct ggml_tensor * tensor) {
|
|
||||||
// free_tensor is optional
|
|
||||||
if (buffer->iface.free_tensor) {
|
|
||||||
buffer->iface.free_tensor(buffer, tensor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// backend
|
|
||||||
|
|
||||||
ggml_backend_t ggml_get_backend(const struct ggml_tensor * tensor) {
|
|
||||||
return tensor->buffer ? tensor->buffer->backend : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char * ggml_backend_name(ggml_backend_t backend) {
|
|
||||||
if (backend == NULL) {
|
|
||||||
return "NULL";
|
|
||||||
}
|
|
||||||
return backend->iface.get_name(backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ggml_backend_free(ggml_backend_t backend) {
|
|
||||||
if (backend == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
backend->iface.free(backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_backend_buffer_t ggml_backend_alloc_buffer(ggml_backend_t backend, size_t size) {
|
|
||||||
return backend->iface.alloc_buffer(backend, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t ggml_backend_get_alignment(ggml_backend_t backend) {
|
|
||||||
return backend->iface.get_alignment(backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ggml_backend_tensor_set_async(struct ggml_tensor * tensor, const void * data, size_t offset, size_t size) {
|
|
||||||
ggml_get_backend(tensor)->iface.set_tensor_async(ggml_get_backend(tensor), tensor, data, offset, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ggml_backend_tensor_get_async(const struct ggml_tensor * tensor, void * data, size_t offset, size_t size) {
|
|
||||||
ggml_get_backend(tensor)->iface.get_tensor_async(ggml_get_backend(tensor), tensor, data, offset, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ggml_backend_tensor_set(struct ggml_tensor * tensor, const void * data, size_t offset, size_t size) {
|
|
||||||
ggml_backend_t backend = ggml_get_backend(tensor);
|
|
||||||
|
|
||||||
GGML_ASSERT(tensor->data != NULL && "tensor not allocated");
|
|
||||||
GGML_ASSERT(backend != NULL && "tensor backend not set");
|
|
||||||
|
|
||||||
backend->iface.set_tensor_async(backend, tensor, data, offset, size);
|
|
||||||
backend->iface.synchronize(backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ggml_backend_tensor_get(const struct ggml_tensor * tensor, void * data, size_t offset, size_t size) {
|
|
||||||
ggml_backend_t backend = ggml_get_backend(tensor);
|
|
||||||
|
|
||||||
GGML_ASSERT(tensor->data != NULL && "tensor not allocated");
|
|
||||||
GGML_ASSERT(backend != NULL && "tensor backend not set");
|
|
||||||
|
|
||||||
backend->iface.get_tensor_async(backend, tensor, data, offset, size);
|
|
||||||
backend->iface.synchronize(backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ggml_backend_synchronize(ggml_backend_t backend) {
|
|
||||||
backend->iface.synchronize(backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_backend_graph_plan_t ggml_backend_graph_plan_create(ggml_backend_t backend, struct ggml_cgraph * cgraph) {
|
|
||||||
return backend->iface.graph_plan_create(backend, cgraph);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ggml_backend_graph_plan_free(ggml_backend_t backend, ggml_backend_graph_plan_t plan) {
|
|
||||||
backend->iface.graph_plan_free(backend, plan);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ggml_backend_graph_plan_compute(ggml_backend_t backend, ggml_backend_graph_plan_t plan) {
|
|
||||||
backend->iface.graph_plan_compute(backend, plan);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ggml_backend_graph_compute(ggml_backend_t backend, struct ggml_cgraph * cgraph) {
|
|
||||||
return backend->iface.graph_compute(backend, cgraph);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ggml_backend_supports_op(ggml_backend_t backend, const struct ggml_tensor * op) {
|
|
||||||
return backend->iface.supports_op(backend, op);
|
|
||||||
}
|
|
||||||
|
|
||||||
// backend copy
|
|
||||||
|
|
||||||
static bool ggml_are_same_layout(const struct ggml_tensor * a, const struct ggml_tensor * b) {
|
|
||||||
if (a->type != b->type) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < GGML_MAX_DIMS; i++) {
|
|
||||||
if (a->ne[i] != b->ne[i]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (a->nb[i] != b->nb[i]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ggml_backend_tensor_copy(struct ggml_tensor * src, struct ggml_tensor * dst) {
|
|
||||||
//printf("src: %s ne: [%d %d %d %d] nb: [%d %d %d %d]\n", src->name, (int)src->ne[0], (int)src->ne[1], (int)src->ne[2], (int)src->ne[3], (int)src->nb[0], (int)src->nb[1], (int)src->nb[2], (int)src->nb[3]);
|
|
||||||
//printf("dst: %s ne: [%d %d %d %d] nb: [%d %d %d %d]\n", dst->name, (int)dst->ne[0], (int)dst->ne[1], (int)dst->ne[2], (int)dst->ne[3], (int)dst->nb[0], (int)dst->nb[1], (int)dst->nb[2], (int)dst->nb[3]);
|
|
||||||
GGML_ASSERT(ggml_are_same_layout(src, dst) && "cannot copy tensors with different layouts");
|
|
||||||
|
|
||||||
// fprintf(stderr, "cpy tensor %s from %s to %s (%lu bytes)\n", src->name, ggml_backend_name(src->backend), ggml_backend_name(dst->backend), ggml_nbytes(src));
|
|
||||||
|
|
||||||
if (src == dst) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: allow backends to support copy to/from same backend
|
|
||||||
|
|
||||||
if (ggml_get_backend(dst)->iface.cpy_tensor_from != NULL) {
|
|
||||||
ggml_get_backend(dst)->iface.cpy_tensor_from(ggml_get_backend(dst)->context, src, dst);
|
|
||||||
} else if (ggml_get_backend(src)->iface.cpy_tensor_to != NULL) {
|
|
||||||
ggml_get_backend(src)->iface.cpy_tensor_to(ggml_get_backend(src)->context, src, dst);
|
|
||||||
} else {
|
|
||||||
// shouldn't be hit when copying from/to CPU
|
|
||||||
#ifndef NDEBUG
|
|
||||||
fprintf(stderr, "ggml_backend_tensor_copy: neither cpy_tensor_from nor cpy_tensor_to are implemented for backends %s and %s, falling back to get/set\n", ggml_backend_name(src->buffer->backend), ggml_backend_name(dst->buffer->backend));
|
|
||||||
#endif
|
|
||||||
size_t nbytes = ggml_nbytes(src);
|
|
||||||
void * data = malloc(nbytes);
|
|
||||||
ggml_backend_tensor_get(src, data, 0, nbytes);
|
|
||||||
ggml_backend_tensor_set(dst, data, 0, nbytes);
|
|
||||||
free(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// backend CPU
|
|
||||||
|
|
||||||
struct ggml_backend_cpu_context {
|
|
||||||
int n_threads;
|
|
||||||
void * work_data;
|
|
||||||
size_t work_size;
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char * ggml_backend_cpu_name(ggml_backend_t backend) {
|
|
||||||
return "CPU";
|
|
||||||
|
|
||||||
UNUSED(backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ggml_backend_cpu_free(ggml_backend_t backend) {
|
|
||||||
struct ggml_backend_cpu_context * cpu_ctx = (struct ggml_backend_cpu_context *)backend->context;
|
|
||||||
free(cpu_ctx->work_data);
|
|
||||||
free(cpu_ctx);
|
|
||||||
free(backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void * ggml_backend_cpu_buffer_get_base(ggml_backend_buffer_t buffer) {
|
|
||||||
return (void *)buffer->context;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ggml_backend_cpu_buffer_free_buffer(ggml_backend_buffer_t buffer) {
|
|
||||||
free(buffer->context);
|
|
||||||
UNUSED(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct ggml_backend_buffer_i cpu_backend_buffer_i = {
|
|
||||||
/* .free_buffer = */ ggml_backend_cpu_buffer_free_buffer,
|
|
||||||
/* .get_base = */ ggml_backend_cpu_buffer_get_base,
|
|
||||||
/* .get_alloc_size = */ NULL, // defaults to ggml_nbytes
|
|
||||||
/* .init_tensor = */ NULL, // no initialization required
|
|
||||||
/* .free_tensor = */ NULL, // no cleanup required
|
|
||||||
};
|
|
||||||
|
|
||||||
// for buffers from ptr, free is not called
|
|
||||||
static struct ggml_backend_buffer_i cpu_backend_buffer_i_from_ptr = {
|
|
||||||
/* .free_buffer = */ NULL, // ptr is not owned by the buffer, so it does not need to be freed
|
|
||||||
/* .get_base = */ ggml_backend_cpu_buffer_get_base,
|
|
||||||
/* .get_alloc_size = */ NULL, // defaults to ggml_nbytes
|
|
||||||
/* .init_tensor = */ NULL,
|
|
||||||
/* .free_tensor = */ NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const size_t TENSOR_ALIGNMENT = 64; // should be enough for AVX 512
|
|
||||||
|
|
||||||
static ggml_backend_buffer_t ggml_backend_cpu_alloc_buffer(ggml_backend_t backend, size_t size) {
|
|
||||||
size += TENSOR_ALIGNMENT; // malloc may return an address that is not aligned
|
|
||||||
void * data = malloc(size); // TODO: maybe use GGML_ALIGNED_MALLOC?
|
|
||||||
|
|
||||||
GGML_ASSERT(data != NULL && "failed to allocate buffer");
|
|
||||||
|
|
||||||
return ggml_backend_buffer_init(backend, cpu_backend_buffer_i, data, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t ggml_backend_cpu_get_alignment(ggml_backend_t backend) {
|
|
||||||
return TENSOR_ALIGNMENT;
|
|
||||||
UNUSED(backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ggml_backend_cpu_set_tensor_async(ggml_backend_t backend, struct ggml_tensor * tensor, const void * data, size_t offset, size_t size) {
|
|
||||||
GGML_ASSERT(offset + size <= ggml_nbytes(tensor) && "tensor write out of bounds");
|
|
||||||
GGML_ASSERT(tensor->data != NULL && "tensor not allocated");
|
|
||||||
|
|
||||||
memcpy((char *)tensor->data + offset, data, size);
|
|
||||||
|
|
||||||
UNUSED(backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ggml_backend_cpu_get_tensor_async(ggml_backend_t backend, const struct ggml_tensor * tensor, void * data, size_t offset, size_t size) {
|
|
||||||
GGML_ASSERT(offset + size <= ggml_nbytes(tensor) && "tensor read out of bounds");
|
|
||||||
GGML_ASSERT(tensor->data != NULL && "tensor not allocated");
|
|
||||||
|
|
||||||
memcpy(data, (const char *)tensor->data + offset, size);
|
|
||||||
|
|
||||||
UNUSED(backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ggml_backend_cpu_synchronize(ggml_backend_t backend) {
|
|
||||||
UNUSED(backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ggml_backend_cpu_cpy_tensor_from(ggml_backend_t backend, struct ggml_tensor * src, struct ggml_tensor * dst) {
|
|
||||||
ggml_backend_tensor_get(src, dst->data, 0, ggml_nbytes(src));
|
|
||||||
|
|
||||||
UNUSED(backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ggml_backend_cpu_cpy_tensor_to(ggml_backend_t backend, struct ggml_tensor * src, struct ggml_tensor * dst) {
|
|
||||||
ggml_backend_tensor_set(dst, src->data, 0, ggml_nbytes(src));
|
|
||||||
|
|
||||||
UNUSED(backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ggml_backend_plan_cpu {
|
|
||||||
struct ggml_cplan cplan;
|
|
||||||
struct ggml_cgraph cgraph;
|
|
||||||
};
|
|
||||||
|
|
||||||
static ggml_backend_graph_plan_t ggml_backend_cpu_graph_plan_create(ggml_backend_t backend, struct ggml_cgraph * cgraph) {
|
|
||||||
struct ggml_backend_cpu_context * cpu_ctx = (struct ggml_backend_cpu_context *)backend->context;
|
|
||||||
|
|
||||||
struct ggml_backend_plan_cpu * cpu_plan = malloc(sizeof(struct ggml_backend_plan_cpu));
|
|
||||||
|
|
||||||
cpu_plan->cplan = ggml_graph_plan(cgraph, cpu_ctx->n_threads);
|
|
||||||
cpu_plan->cgraph = *cgraph;
|
|
||||||
|
|
||||||
if (cpu_plan->cplan.work_size > 0) {
|
|
||||||
cpu_plan->cplan.work_data = malloc(cpu_plan->cplan.work_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cpu_plan;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ggml_backend_cpu_graph_plan_free(ggml_backend_t backend, ggml_backend_graph_plan_t plan) {
|
|
||||||
struct ggml_backend_plan_cpu * cpu_plan = (struct ggml_backend_plan_cpu *)plan;
|
|
||||||
|
|
||||||
free(cpu_plan->cplan.work_data);
|
|
||||||
free(cpu_plan);
|
|
||||||
|
|
||||||
UNUSED(backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ggml_backend_cpu_graph_plan_compute(ggml_backend_t backend, ggml_backend_graph_plan_t plan) {
|
|
||||||
struct ggml_backend_plan_cpu * cpu_plan = (struct ggml_backend_plan_cpu *)plan;
|
|
||||||
|
|
||||||
ggml_graph_compute(&cpu_plan->cgraph, &cpu_plan->cplan);
|
|
||||||
|
|
||||||
UNUSED(backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ggml_backend_cpu_graph_compute(ggml_backend_t backend, struct ggml_cgraph * cgraph) {
|
|
||||||
struct ggml_backend_cpu_context * cpu_ctx = (struct ggml_backend_cpu_context *)backend->context;
|
|
||||||
|
|
||||||
struct ggml_cplan cplan = ggml_graph_plan(cgraph, cpu_ctx->n_threads);
|
|
||||||
|
|
||||||
if (cpu_ctx->work_size < cplan.work_size) {
|
|
||||||
// TODO: may be faster to free and use malloc to avoid the copy
|
|
||||||
cpu_ctx->work_data = realloc(cpu_ctx->work_data, cplan.work_size);
|
|
||||||
cpu_ctx->work_size = cplan.work_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
cplan.work_data = cpu_ctx->work_data;
|
|
||||||
|
|
||||||
ggml_graph_compute(cgraph, &cplan);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool ggml_backend_cpu_supports_op(ggml_backend_t backend, const struct ggml_tensor * op) {
|
|
||||||
return true;
|
|
||||||
UNUSED(backend);
|
|
||||||
UNUSED(op);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct ggml_backend_i cpu_backend_i = {
|
|
||||||
/* .get_name = */ ggml_backend_cpu_name,
|
|
||||||
/* .free = */ ggml_backend_cpu_free,
|
|
||||||
/* .alloc_buffer = */ ggml_backend_cpu_alloc_buffer,
|
|
||||||
/* .get_alignment = */ ggml_backend_cpu_get_alignment,
|
|
||||||
/* .set_tensor_async = */ ggml_backend_cpu_set_tensor_async,
|
|
||||||
/* .get_tensor_async = */ ggml_backend_cpu_get_tensor_async,
|
|
||||||
/* .synchronize = */ ggml_backend_cpu_synchronize,
|
|
||||||
/* .cpy_tensor_from = */ ggml_backend_cpu_cpy_tensor_from,
|
|
||||||
/* .cpy_tensor_to = */ ggml_backend_cpu_cpy_tensor_to,
|
|
||||||
/* .graph_plan_create = */ ggml_backend_cpu_graph_plan_create,
|
|
||||||
/* .graph_plan_free = */ ggml_backend_cpu_graph_plan_free,
|
|
||||||
/* .graph_plan_compute = */ ggml_backend_cpu_graph_plan_compute,
|
|
||||||
/* .graph_compute = */ ggml_backend_cpu_graph_compute,
|
|
||||||
/* .supports_op = */ ggml_backend_cpu_supports_op,
|
|
||||||
};
|
|
||||||
|
|
||||||
ggml_backend_t ggml_backend_cpu_init(void) {
|
|
||||||
struct ggml_backend_cpu_context * ctx = malloc(sizeof(struct ggml_backend_cpu_context));
|
|
||||||
|
|
||||||
ctx->n_threads = GGML_DEFAULT_N_THREADS;
|
|
||||||
ctx->work_data = NULL;
|
|
||||||
ctx->work_size = 0;
|
|
||||||
|
|
||||||
ggml_backend_t cpu_backend = malloc(sizeof(struct ggml_backend));
|
|
||||||
|
|
||||||
*cpu_backend = (struct ggml_backend) {
|
|
||||||
/* .interface = */ cpu_backend_i,
|
|
||||||
/* .context = */ ctx
|
|
||||||
};
|
|
||||||
return cpu_backend;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ggml_backend_is_cpu(ggml_backend_t backend) {
|
|
||||||
return backend->iface.get_name == ggml_backend_cpu_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ggml_backend_cpu_set_n_threads(ggml_backend_t backend_cpu, int n_threads) {
|
|
||||||
GGML_ASSERT(ggml_backend_is_cpu(backend_cpu));
|
|
||||||
|
|
||||||
struct ggml_backend_cpu_context * ctx = (struct ggml_backend_cpu_context *)backend_cpu->context;
|
|
||||||
ctx->n_threads = n_threads;
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_backend_buffer_t ggml_backend_cpu_buffer_from_ptr(ggml_backend_t backend_cpu, void * ptr, size_t size) {
|
|
||||||
return ggml_backend_buffer_init(backend_cpu, cpu_backend_buffer_i_from_ptr, ptr, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// scheduler
|
|
||||||
|
|
||||||
#define GGML_MAX_BACKENDS 4
|
|
||||||
#define GGML_MAX_SPLITS 256
|
|
||||||
#define GGML_MAX_SPLIT_INPUTS 16
|
|
||||||
|
|
||||||
struct ggml_backend_sched_split {
|
|
||||||
ggml_tallocr_t tallocr;
|
|
||||||
int i_start;
|
|
||||||
int i_end;
|
|
||||||
struct ggml_tensor * inputs[GGML_MAX_SPLIT_INPUTS];
|
|
||||||
int n_inputs;
|
|
||||||
struct ggml_cgraph * graph;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ggml_backend_sched {
|
|
||||||
int n_backends;
|
|
||||||
ggml_backend_t backends[GGML_MAX_BACKENDS];
|
|
||||||
ggml_tallocr_t tallocs[GGML_MAX_BACKENDS];
|
|
||||||
|
|
||||||
ggml_gallocr_t galloc;
|
|
||||||
|
|
||||||
struct ggml_hash_set hash_set;
|
|
||||||
ggml_tallocr_t * node_talloc; // [hash_set.size]
|
|
||||||
struct ggml_tensor * (* node_copies)[GGML_MAX_BACKENDS]; // [hash_set.size][GGML_MAX_BACKENDS]
|
|
||||||
|
|
||||||
struct ggml_cgraph * graph;
|
|
||||||
struct ggml_backend_sched_split splits[GGML_MAX_SPLITS];
|
|
||||||
int n_splits;
|
|
||||||
|
|
||||||
struct ggml_context * ctx;
|
|
||||||
|
|
||||||
// align context_buffer to GGML_MEM_ALIGN
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
__declspec(align(GGML_MEM_ALIGN))
|
|
||||||
#else
|
|
||||||
__attribute__((aligned(GGML_MEM_ALIGN)))
|
|
||||||
#endif
|
|
||||||
char context_buffer[GGML_MAX_SPLITS*GGML_MAX_SPLIT_INPUTS*sizeof(struct ggml_tensor) + GGML_MAX_SPLITS*sizeof(struct ggml_cgraph)];
|
|
||||||
};
|
|
||||||
|
|
||||||
#define hash_id(node) ggml_hash_find_or_insert(sched->hash_set, node)
|
|
||||||
#define node_allocr(node) sched->node_talloc[hash_id(node)]
|
|
||||||
|
|
||||||
static bool ggml_is_view_op(enum ggml_op op) {
|
|
||||||
return op == GGML_OP_VIEW || op == GGML_OP_RESHAPE || op == GGML_OP_PERMUTE || op == GGML_OP_TRANSPOSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns the priority of the backend, lower is better
|
|
||||||
static int sched_backend_prio(ggml_backend_sched_t sched, ggml_backend_t backend) {
|
|
||||||
for (int i = 0; i < sched->n_backends; i++) {
|
|
||||||
if (sched->backends[i] == backend) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return INT_MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sched_allocr_prio(ggml_backend_sched_t sched, ggml_tallocr_t allocr) {
|
|
||||||
for (int i = 0; i < sched->n_backends; i++) {
|
|
||||||
if (sched->tallocs[i] == allocr) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return INT_MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns the backend that should be used for the node based on the current locations
|
|
||||||
char causes[GGML_DEFAULT_GRAPH_SIZE*4 + GGML_MAX_SPLITS*GGML_MAX_SPLIT_INPUTS][128]; // debug, remove
|
|
||||||
static ggml_backend_t sched_backend_from_cur(ggml_backend_sched_t sched, struct ggml_tensor * node) {
|
|
||||||
// if the dst tensor is already allocated in a buffer, we must assume that it is critical to keep it there
|
|
||||||
// ie. kv cache updates
|
|
||||||
// note that this doesn't allow fallback to CPU. need to add output tensors to the splits to copy the data back to the original backend.
|
|
||||||
// dst
|
|
||||||
ggml_backend_t cur_backend = ggml_get_backend(node);
|
|
||||||
if (cur_backend != NULL) {
|
|
||||||
sprintf(causes[hash_id(node)], "1.dst");
|
|
||||||
return cur_backend;
|
|
||||||
}
|
|
||||||
|
|
||||||
// view_src
|
|
||||||
if (node->view_src != NULL && ggml_get_backend(node->view_src) != NULL) {
|
|
||||||
sprintf(causes[hash_id(node)], "1.vsrc");
|
|
||||||
return ggml_get_backend(node->view_src);
|
|
||||||
}
|
|
||||||
|
|
||||||
// src
|
|
||||||
int cur_prio = INT_MAX;
|
|
||||||
size_t cur_size = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < GGML_MAX_SRC; i++) {
|
|
||||||
const struct ggml_tensor * src = node->src[i];
|
|
||||||
if (src == NULL) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ggml_backend_t src_backend = ggml_get_backend(src);
|
|
||||||
if (src_backend != NULL) {
|
|
||||||
int src_prio = sched_backend_prio(sched, src_backend);
|
|
||||||
size_t src_size = ggml_nbytes(src);
|
|
||||||
if (src_prio < cur_prio && src_size >= cur_size) {
|
|
||||||
cur_prio = src_prio;
|
|
||||||
cur_size = src_size;
|
|
||||||
cur_backend = src_backend;
|
|
||||||
sprintf(causes[hash_id(node)], "1.src%d", i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cur_backend;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char * fmt_size(size_t size) {
|
|
||||||
static char buffer[128];
|
|
||||||
if (size >= 1024*1024) {
|
|
||||||
sprintf(buffer, "%zuM", size/1024/1024);
|
|
||||||
} else {
|
|
||||||
sprintf(buffer, "%zuK", size/1024);
|
|
||||||
}
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sched_print_assignments(ggml_backend_sched_t sched, struct ggml_cgraph * graph) {
|
|
||||||
int cur_split = 0;
|
|
||||||
for (int i = 0; i < graph->n_nodes; i++) {
|
|
||||||
if (cur_split < sched->n_splits && i == sched->splits[cur_split].i_start) {
|
|
||||||
ggml_backend_t split_backend = ggml_tallocr_get_buffer(sched->splits[cur_split].tallocr)->backend;
|
|
||||||
fprintf(stderr, "\n## SPLIT #%d: %s # %d inputs: ", cur_split, ggml_backend_name(split_backend), sched->splits[cur_split].n_inputs);
|
|
||||||
for (int j = 0; j < sched->splits[cur_split].n_inputs; j++) {
|
|
||||||
fprintf(stderr, "[%s (%5.5s)] ", sched->splits[cur_split].inputs[j]->name, fmt_size(ggml_nbytes(sched->splits[cur_split].inputs[j])));
|
|
||||||
}
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
cur_split++;
|
|
||||||
}
|
|
||||||
struct ggml_tensor * node = graph->nodes[i];
|
|
||||||
if (ggml_is_view_op(node->op)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ggml_tallocr_t node_allocr = node_allocr(node);
|
|
||||||
ggml_backend_t node_backend = node_allocr ? ggml_tallocr_get_buffer(node_allocr)->backend : NULL;
|
|
||||||
fprintf(stderr, "node #%3d (%10.10s): %20.20s (%4.4s) [%4.4s %8.8s]:", i, ggml_op_name(node->op), node->name, fmt_size(ggml_nbytes(node)), node_allocr ? ggml_backend_name(node_backend) : "NULL", causes[hash_id(node)]);
|
|
||||||
for (int j = 0; j < GGML_MAX_SRC; j++) {
|
|
||||||
struct ggml_tensor * src = node->src[j];
|
|
||||||
if (src == NULL) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ggml_tallocr_t src_allocr = node_allocr(src);
|
|
||||||
ggml_backend_t src_backend = src_allocr ? ggml_tallocr_get_buffer(src_allocr)->backend : NULL;
|
|
||||||
fprintf(stderr, " %20.20s (%4.4s) [%4.4s %8.8s]", src->name, fmt_size(ggml_nbytes(src)), src_backend ? ggml_backend_name(src_backend) : "NULL", causes[hash_id(src)]);
|
|
||||||
}
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// creates a copy of the tensor with the same memory layout
|
|
||||||
static struct ggml_tensor * ggml_dup_tensor_layout(struct ggml_context * ctx, const struct ggml_tensor * tensor) {
|
|
||||||
struct ggml_tensor * dup = ggml_dup_tensor(ctx, tensor);
|
|
||||||
for (int i = 0; i < GGML_MAX_DIMS; i++) {
|
|
||||||
dup->nb[i] = tensor->nb[i];
|
|
||||||
}
|
|
||||||
return dup;
|
|
||||||
}
|
|
||||||
|
|
||||||
// assigns backends to ops and splits the graph into subgraphs that can be computed on the same backend
|
|
||||||
// TODO: merge passes
|
|
||||||
static void sched_split_graph(ggml_backend_sched_t sched, struct ggml_cgraph * graph) {
|
|
||||||
// reset state
|
|
||||||
size_t hash_size = sched->hash_set.size;
|
|
||||||
memset(sched->hash_set.keys, 0, sizeof(sched->hash_set.keys[0]) * hash_size);
|
|
||||||
memset(sched->node_talloc, 0, sizeof(sched->node_talloc[0]) * hash_size);
|
|
||||||
memset(sched->node_copies, 0, sizeof(sched->node_copies[0]) * hash_size);
|
|
||||||
sched->n_splits = 0;
|
|
||||||
|
|
||||||
struct ggml_init_params params = {
|
|
||||||
/*.mem_size = */ sizeof(sched->context_buffer),
|
|
||||||
/*.mem_buffer = */ sched->context_buffer,
|
|
||||||
/*.no_alloc = */ true
|
|
||||||
};
|
|
||||||
|
|
||||||
if (sched->ctx != NULL) {
|
|
||||||
ggml_free(sched->ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
sched->ctx = ggml_init(params);
|
|
||||||
|
|
||||||
// pass 1: assign backends to ops with allocated inputs
|
|
||||||
for (int i = 0; i < graph->n_leafs; i++) {
|
|
||||||
struct ggml_tensor * leaf = graph->leafs[i];
|
|
||||||
if (node_allocr(leaf) != NULL) {
|
|
||||||
// do not overwrite user assignments
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ggml_backend_t leaf_backend = ggml_get_backend(leaf);
|
|
||||||
if (leaf_backend == NULL && leaf->view_src != NULL) {
|
|
||||||
leaf_backend = ggml_get_backend(leaf->view_src);
|
|
||||||
}
|
|
||||||
if (leaf_backend != NULL) {
|
|
||||||
node_allocr(leaf) = ggml_backend_sched_get_tallocr(sched, leaf_backend);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < graph->n_nodes; i++) {
|
|
||||||
struct ggml_tensor * node = graph->nodes[i];
|
|
||||||
if (node_allocr(node) != NULL) {
|
|
||||||
// do not overwrite user assignments
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ggml_backend_t node_backend = sched_backend_from_cur(sched, node);
|
|
||||||
if (node_backend != NULL) {
|
|
||||||
node_allocr(node) = ggml_backend_sched_get_tallocr(sched, node_backend);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//printf("PASS 1 ASSIGNMENTS\n"); sched_print_assignments(sched, graph);
|
|
||||||
|
|
||||||
// pass 2: assign backends to ops from current assignments
|
|
||||||
// TODO:
|
|
||||||
// - reuse sched_backend_from_cur
|
|
||||||
for (int i = 0; i < graph->n_nodes; i++) {
|
|
||||||
struct ggml_tensor * node = graph->nodes[i];
|
|
||||||
ggml_tallocr_t node_allocr = node_allocr(node);
|
|
||||||
if (node_allocr == NULL) {
|
|
||||||
int cur_prio = INT_MAX;
|
|
||||||
size_t cur_size = 0;
|
|
||||||
for (int j = 0; j < GGML_MAX_SRC; j++) {
|
|
||||||
struct ggml_tensor * src = node->src[j];
|
|
||||||
if (src == NULL) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ggml_tallocr_t src_allocr = node_allocr(src);
|
|
||||||
if (src_allocr != NULL) {
|
|
||||||
int src_prio = sched_allocr_prio(sched, src_allocr);
|
|
||||||
size_t src_size = ggml_nbytes(src);
|
|
||||||
if (src_prio < cur_prio && src_size >= cur_size) {
|
|
||||||
cur_prio = src_prio;
|
|
||||||
cur_size = src_size;
|
|
||||||
node_allocr = src_allocr;
|
|
||||||
sprintf(causes[hash_id(node)], "2.src%d", j);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (node_allocr != NULL) {
|
|
||||||
node_allocr(node) = node_allocr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//printf("PASS 2 ASSIGNMENTS\n"); sched_print_assignments(sched, graph);
|
|
||||||
|
|
||||||
// pass 3: assign backends to remaining src from dst (should only be leafs)
|
|
||||||
for (int i = 0; i < graph->n_nodes; i++) {
|
|
||||||
struct ggml_tensor * node = graph->nodes[i];
|
|
||||||
ggml_tallocr_t node_allocr = node_allocr(node);
|
|
||||||
for (int j = 0; j < GGML_MAX_SRC; j++) {
|
|
||||||
struct ggml_tensor * src = node->src[j];
|
|
||||||
if (src == NULL) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ggml_tallocr_t src_allocr = node_allocr(src);
|
|
||||||
if (src_allocr == NULL) {
|
|
||||||
node_allocr(src) = node_allocr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//printf("PASS 3 ASSIGNMENTS\n"); sched_print_assignments(sched, graph);
|
|
||||||
|
|
||||||
// pass 4: split graph, find tensors that need to be copied
|
|
||||||
// TODO:
|
|
||||||
// - when switching from a less preferred backend to a more preferred backend, check if it is possible to move the switch to an earlier point for the same cost
|
|
||||||
// find first backend
|
|
||||||
int cur_split = 0;
|
|
||||||
for (int i = 0; i < graph->n_nodes; i++) {
|
|
||||||
struct ggml_tensor * node = graph->nodes[i];
|
|
||||||
if (node->view_src == NULL) {
|
|
||||||
sched->splits[0].tallocr = node_allocr(node);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sched->splits[0].i_start = 0;
|
|
||||||
sched->splits[0].n_inputs = 0;
|
|
||||||
memset(sched->splits[0].inputs, 0, sizeof(sched->splits[0].inputs)); //HACK
|
|
||||||
ggml_tallocr_t cur_allocr = sched->splits[0].tallocr;
|
|
||||||
size_t cur_backend_id = sched_allocr_prio(sched, cur_allocr);
|
|
||||||
for (int i = 0; i < graph->n_nodes; i++) {
|
|
||||||
struct ggml_tensor * node = graph->nodes[i];
|
|
||||||
|
|
||||||
if (ggml_is_view_op(node->op)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tallocr_t node_allocr = node_allocr(node);
|
|
||||||
|
|
||||||
if (node_allocr != cur_allocr) {
|
|
||||||
sched->splits[cur_split].i_end = i;
|
|
||||||
cur_split++;
|
|
||||||
GGML_ASSERT(cur_split < GGML_MAX_SPLITS);
|
|
||||||
sched->splits[cur_split].tallocr = node_allocr;
|
|
||||||
sched->splits[cur_split].i_start = i;
|
|
||||||
sched->splits[cur_split].n_inputs = 0;
|
|
||||||
memset(sched->splits[cur_split].inputs, 0, sizeof(sched->splits[cur_split].inputs)); //HACK
|
|
||||||
cur_allocr = node_allocr;
|
|
||||||
cur_backend_id = sched_allocr_prio(sched, cur_allocr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// find inputs that are not on the same backend
|
|
||||||
for (int j = 0; j < GGML_MAX_SRC; j++) {
|
|
||||||
struct ggml_tensor * src = node->src[j];
|
|
||||||
if (src == NULL) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ggml_tallocr_t src_allocr = node_allocr(src);
|
|
||||||
if (src_allocr != node_allocr) {
|
|
||||||
int n_inputs = sched->splits[cur_split].n_inputs++;
|
|
||||||
GGML_ASSERT(n_inputs < GGML_MAX_SPLIT_INPUTS);
|
|
||||||
sched->splits[cur_split].inputs[n_inputs] = (struct ggml_tensor *)src;
|
|
||||||
|
|
||||||
// create copies
|
|
||||||
size_t id = hash_id(src);
|
|
||||||
if (sched->node_copies[id][cur_backend_id] == NULL) {
|
|
||||||
struct ggml_tensor * tensor_copy = ggml_dup_tensor_layout(sched->ctx, src);
|
|
||||||
sched->node_copies[id][cur_backend_id] = tensor_copy;
|
|
||||||
node_allocr(tensor_copy) = cur_allocr;
|
|
||||||
ggml_backend_t backend = ggml_tallocr_get_buffer(cur_allocr)->backend;
|
|
||||||
ggml_format_name(tensor_copy, "%s#%s", ggml_backend_name(backend), src->name);
|
|
||||||
}
|
|
||||||
node->src[j] = sched->node_copies[id][cur_backend_id];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sched->splits[cur_split].i_end = graph->n_nodes;
|
|
||||||
sched->n_splits = cur_split + 1;
|
|
||||||
|
|
||||||
//fprintf(stderr, "PASS 4 ASSIGNMENTS\n"); sched_print_assignments(sched, graph); fflush(stdout);
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
// sanity check: all sources should have the same backend as the node
|
|
||||||
for (int i = 0; i < graph->n_nodes; i++) {
|
|
||||||
struct ggml_tensor * node = graph->nodes[i];
|
|
||||||
ggml_tallocr_t node_allocr = node_allocr(node);
|
|
||||||
if (node_allocr == NULL) {
|
|
||||||
fprintf(stderr, "!!!!!!! %s has no backend\n", node->name);
|
|
||||||
}
|
|
||||||
for (int j = 0; j < GGML_MAX_SRC; j++) {
|
|
||||||
struct ggml_tensor * src = node->src[j];
|
|
||||||
if (src == NULL) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ggml_tallocr_t src_allocr = node_allocr(src);
|
|
||||||
if (src_allocr != node_allocr /* && src_backend != NULL */) { // ignore nulls for now
|
|
||||||
fprintf(stderr, "!!!! %s has backend %s, src %d (%s) has backend %s\n",
|
|
||||||
node->name, node_allocr ? ggml_backend_name(ggml_tallocr_get_buffer(node_allocr)->backend) : "NULL",
|
|
||||||
j, src->name, src_allocr ? ggml_backend_name(ggml_tallocr_get_buffer(src_allocr)->backend) : "NULL");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// create copies of the graph for each split
|
|
||||||
// FIXME: avoid this copy, pass split inputs to ggml_gallocr_alloc_graph_n in some other way
|
|
||||||
struct ggml_cgraph * graph_copy = ggml_new_graph_custom(sched->ctx, graph->n_nodes + sched->n_splits*GGML_MAX_SPLIT_INPUTS, false);
|
|
||||||
for (int i = 0; i < sched->n_splits; i++) {
|
|
||||||
struct ggml_backend_sched_split * split = &sched->splits[i];
|
|
||||||
split->graph = ggml_graph_view(sched->ctx, graph, split->i_start, split->i_end);
|
|
||||||
|
|
||||||
// add inputs to the graph copy so that they are allocated by ggml-alloc at the start of the split
|
|
||||||
for (int j = 0; j < split->n_inputs; j++) {
|
|
||||||
struct ggml_tensor * input = split->inputs[j];
|
|
||||||
struct ggml_tensor * input_cpy = sched->node_copies[hash_id(input)][sched_allocr_prio(sched, split->tallocr)];
|
|
||||||
input_cpy->src[0] = input;
|
|
||||||
graph_copy->nodes[graph_copy->n_nodes++] = input_cpy;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int j = split->i_start; j < split->i_end; j++) {
|
|
||||||
graph_copy->nodes[graph_copy->n_nodes++] = graph->nodes[j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sched->graph = graph_copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sched_alloc_splits(ggml_backend_sched_t sched) {
|
|
||||||
ggml_gallocr_alloc_graph_n(
|
|
||||||
sched->galloc,
|
|
||||||
sched->graph,
|
|
||||||
sched->hash_set,
|
|
||||||
sched->node_talloc);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sched_compute_splits(ggml_backend_sched_t sched) {
|
|
||||||
uint64_t copy_us[GGML_MAX_BACKENDS] = {0};
|
|
||||||
uint64_t compute_us[GGML_MAX_BACKENDS] = {0};
|
|
||||||
|
|
||||||
struct ggml_backend_sched_split * splits = sched->splits;
|
|
||||||
|
|
||||||
for (int i = 0; i < sched->n_splits; i++) {
|
|
||||||
struct ggml_backend_sched_split * split = &splits[i];
|
|
||||||
ggml_backend_t split_backend = ggml_tallocr_get_buffer(split->tallocr)->backend;
|
|
||||||
int split_backend_id = sched_backend_prio(sched, split_backend);
|
|
||||||
|
|
||||||
// copy the input tensors to the split backend
|
|
||||||
uint64_t copy_start_us = ggml_time_us();
|
|
||||||
for (int j = 0; j < split->n_inputs; j++) {
|
|
||||||
struct ggml_tensor * input_cpy = sched->node_copies[hash_id(split->inputs[j])][sched_backend_prio(sched, split_backend)];
|
|
||||||
if (split->inputs[j]->buffer == NULL) {
|
|
||||||
if (split->inputs[j]->view_src == NULL) {
|
|
||||||
fprintf(stderr, "input %s has no buffer and no view_src\n", split->inputs[j]->name);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
struct ggml_tensor * view = split->inputs[j];
|
|
||||||
view->backend = view->view_src->backend;
|
|
||||||
view->buffer = view->view_src->buffer;
|
|
||||||
view->data = (char *)view->view_src->data + view->view_offs;
|
|
||||||
ggml_backend_buffer_init_tensor(ggml_backend_sched_get_buffer(sched, view->buffer->backend), view);
|
|
||||||
}
|
|
||||||
if (input_cpy->buffer == NULL) {
|
|
||||||
fprintf(stderr, "input_cpy %s has no buffer\n", input_cpy->name);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
GGML_ASSERT(split->inputs[j]->buffer->backend != input_cpy->buffer->backend);
|
|
||||||
GGML_ASSERT(input_cpy->buffer->backend == split_backend);
|
|
||||||
ggml_backend_tensor_copy(split->inputs[j], input_cpy);
|
|
||||||
}
|
|
||||||
// ggml_backend_synchronize(split_backend);
|
|
||||||
int64_t copy_end_us = ggml_time_us();
|
|
||||||
copy_us[split_backend_id] += copy_end_us - copy_start_us;
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
char split_filename[GGML_MAX_NAME];
|
|
||||||
snprintf(split_filename, GGML_MAX_NAME, "split_%i_%s.dot", i, ggml_backend_name(split_backend));
|
|
||||||
ggml_graph_dump_dot(split->graph, NULL, split_filename);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
uint64_t compute_start_us = ggml_time_us();
|
|
||||||
ggml_backend_graph_compute(split_backend, split->graph);
|
|
||||||
// ggml_backend_synchronize(split_backend);
|
|
||||||
uint64_t compute_end_us = ggml_time_us();
|
|
||||||
compute_us[split_backend_id] += compute_end_us - compute_start_us;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
// per-backend timings
|
|
||||||
fprintf(stderr, "sched_compute_splits times (%d splits):\n", sched->n_splits);
|
|
||||||
for (int i = 0; i < sched->n_backends; i++) {
|
|
||||||
if (copy_us[i] > 0 || compute_us[i] > 0) {
|
|
||||||
fprintf(stderr, "\t%5.5s: %lu us copy, %lu us compute\n", ggml_backend_name(sched->backends[i]), copy_us[i], compute_us[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sched_reset(ggml_backend_sched_t sched) {
|
|
||||||
for (int i = 0; i < sched->n_backends; i++) {
|
|
||||||
ggml_tallocr_reset(sched->tallocs[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_backend_sched_t ggml_backend_sched_new(ggml_backend_t * backends, int n_backends) {
|
|
||||||
GGML_ASSERT(n_backends <= GGML_MAX_BACKENDS);
|
|
||||||
|
|
||||||
struct ggml_backend_sched * sched = malloc(sizeof(struct ggml_backend_sched));
|
|
||||||
memset(sched, 0, sizeof(struct ggml_backend_sched));
|
|
||||||
|
|
||||||
fprintf(stderr, "ggml_backend_sched size: %lu KB\n", sizeof(struct ggml_backend_sched)/1024);
|
|
||||||
|
|
||||||
sched->n_backends = n_backends;
|
|
||||||
for (int i = 0; i < n_backends; i++) {
|
|
||||||
sched->backends[i] = backends[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
sched->galloc = ggml_gallocr_new();
|
|
||||||
|
|
||||||
// init measure allocs for each backend
|
|
||||||
for (int i = 0; i < n_backends; i++) {
|
|
||||||
sched->tallocs[i] = ggml_tallocr_new_measure_from_backend(backends[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return sched;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ggml_backend_sched_free(ggml_backend_sched_t sched) {
|
|
||||||
if (sched == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < sched->n_backends; i++) {
|
|
||||||
ggml_tallocr_free(sched->tallocs[i]);
|
|
||||||
}
|
|
||||||
ggml_gallocr_free(sched->galloc);
|
|
||||||
free(sched->hash_set.keys);
|
|
||||||
free(sched->node_talloc);
|
|
||||||
free(sched->node_copies);
|
|
||||||
free(sched);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ggml_backend_sched_init_measure(ggml_backend_sched_t sched, struct ggml_cgraph * measure_graph) {
|
|
||||||
// initialize hash tables
|
|
||||||
size_t hash_size = measure_graph->visited_hash_table.size + GGML_MAX_SPLITS*GGML_MAX_SPLIT_INPUTS;
|
|
||||||
sched->hash_set.size = hash_size;
|
|
||||||
sched->hash_set.keys = malloc(sizeof(sched->hash_set.keys[0]) * hash_size);
|
|
||||||
sched->node_talloc = malloc(sizeof(sched->node_talloc[0]) * hash_size);
|
|
||||||
sched->node_copies = malloc(sizeof(sched->node_copies[0]) * hash_size);
|
|
||||||
|
|
||||||
sched_split_graph(sched, measure_graph);
|
|
||||||
sched_alloc_splits(sched);
|
|
||||||
|
|
||||||
// allocate buffers and reset allocators
|
|
||||||
for (int i = 0; i < sched->n_backends; i++) {
|
|
||||||
size_t size = ggml_tallocr_max_size(sched->tallocs[i]);
|
|
||||||
ggml_tallocr_free(sched->tallocs[i]);
|
|
||||||
sched->tallocs[i] = ggml_tallocr_new_from_backend(sched->backends[i], size);
|
|
||||||
}
|
|
||||||
|
|
||||||
sched_reset(sched);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ggml_backend_sched_graph_compute(ggml_backend_sched_t sched, struct ggml_cgraph * graph) {
|
|
||||||
GGML_ASSERT(sched->hash_set.size >= graph->visited_hash_table.size + GGML_MAX_SPLITS*GGML_MAX_SPLIT_INPUTS);
|
|
||||||
|
|
||||||
sched_split_graph(sched, graph);
|
|
||||||
sched_alloc_splits(sched);
|
|
||||||
sched_compute_splits(sched);
|
|
||||||
sched_reset(sched);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tallocr_t ggml_backend_sched_get_tallocr(ggml_backend_sched_t sched, ggml_backend_t backend) {
|
|
||||||
int backend_index = sched_backend_prio(sched, backend);
|
|
||||||
return sched->tallocs[backend_index];
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_backend_buffer_t ggml_backend_sched_get_buffer(ggml_backend_sched_t sched, ggml_backend_t backend) {
|
|
||||||
int backend_index = sched_backend_prio(sched, backend);
|
|
||||||
return ggml_tallocr_get_buffer(sched->tallocs[backend_index]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ggml_backend_sched_set_node_backend(ggml_backend_sched_t sched, struct ggml_tensor * node, ggml_backend_t backend) {
|
|
||||||
int backend_index = sched_backend_prio(sched, backend);
|
|
||||||
GGML_ASSERT(backend_index >= 0 && backend_index < sched->n_backends);
|
|
||||||
node_allocr(node) = sched->tallocs[backend_index];
|
|
||||||
}
|
|
@ -1,136 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "ggml.h"
|
|
||||||
#include "ggml-alloc.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//
|
|
||||||
// Backend buffer
|
|
||||||
//
|
|
||||||
|
|
||||||
struct ggml_backend_buffer;
|
|
||||||
typedef struct ggml_backend_buffer * ggml_backend_buffer_t;
|
|
||||||
|
|
||||||
// backend buffer functions
|
|
||||||
GGML_API void ggml_backend_buffer_free (ggml_backend_buffer_t buffer);
|
|
||||||
GGML_API size_t ggml_backend_buffer_get_alignment (ggml_backend_buffer_t buffer);
|
|
||||||
GGML_API void * ggml_backend_buffer_get_base (ggml_backend_buffer_t buffer);
|
|
||||||
GGML_API size_t ggml_backend_buffer_get_size (ggml_backend_buffer_t buffer);
|
|
||||||
GGML_API size_t ggml_backend_buffer_get_alloc_size(ggml_backend_buffer_t buffer, struct ggml_tensor * tensor);
|
|
||||||
GGML_API void ggml_backend_buffer_init_tensor (ggml_backend_buffer_t buffer, struct ggml_tensor * tensor);
|
|
||||||
GGML_API void ggml_backend_buffer_free_tensor (ggml_backend_buffer_t buffer, struct ggml_tensor * tensor);
|
|
||||||
|
|
||||||
//
|
|
||||||
// Backend
|
|
||||||
//
|
|
||||||
|
|
||||||
struct ggml_backend;
|
|
||||||
typedef struct ggml_backend * ggml_backend_t;
|
|
||||||
typedef void * ggml_backend_graph_plan_t;
|
|
||||||
|
|
||||||
GGML_API ggml_backend_t ggml_get_backend(const struct ggml_tensor * tensor);
|
|
||||||
|
|
||||||
GGML_API const char * ggml_backend_name(ggml_backend_t backend);
|
|
||||||
GGML_API void ggml_backend_free(ggml_backend_t backend);
|
|
||||||
|
|
||||||
GGML_API ggml_backend_buffer_t ggml_backend_alloc_buffer(ggml_backend_t backend, size_t size);
|
|
||||||
|
|
||||||
GGML_API size_t ggml_backend_get_alignment(ggml_backend_t backend);
|
|
||||||
|
|
||||||
GGML_API void ggml_backend_tensor_set_async( struct ggml_tensor * tensor, const void * data, size_t offset, size_t size);
|
|
||||||
GGML_API void ggml_backend_tensor_get_async(const struct ggml_tensor * tensor, void * data, size_t offset, size_t size);
|
|
||||||
|
|
||||||
GGML_API void ggml_backend_tensor_set( struct ggml_tensor * tensor, const void * data, size_t offset, size_t size);
|
|
||||||
GGML_API void ggml_backend_tensor_get(const struct ggml_tensor * tensor, void * data, size_t offset, size_t size);
|
|
||||||
|
|
||||||
GGML_API void ggml_backend_synchronize(ggml_backend_t backend);
|
|
||||||
|
|
||||||
GGML_API ggml_backend_graph_plan_t ggml_backend_graph_plan_create (ggml_backend_t backend, struct ggml_cgraph * cgraph);
|
|
||||||
|
|
||||||
GGML_API void ggml_backend_graph_plan_free (ggml_backend_t backend, ggml_backend_graph_plan_t plan);
|
|
||||||
GGML_API void ggml_backend_graph_plan_compute(ggml_backend_t backend, ggml_backend_graph_plan_t plan);
|
|
||||||
GGML_API bool ggml_backend_graph_compute (ggml_backend_t backend, struct ggml_cgraph * cgraph);
|
|
||||||
GGML_API bool ggml_backend_supports_op (ggml_backend_t backend, const struct ggml_tensor * op);
|
|
||||||
|
|
||||||
// tensor copy between different backends
|
|
||||||
GGML_API void ggml_backend_tensor_copy(struct ggml_tensor * src, struct ggml_tensor * dst);
|
|
||||||
|
|
||||||
//
|
|
||||||
// CPU backend
|
|
||||||
//
|
|
||||||
|
|
||||||
GGML_API ggml_backend_t ggml_backend_cpu_init(void);
|
|
||||||
|
|
||||||
GGML_API bool ggml_backend_is_cpu(ggml_backend_t backend);
|
|
||||||
GGML_API void ggml_backend_cpu_set_n_threads(ggml_backend_t backend_cpu, int n_threads);
|
|
||||||
|
|
||||||
// Create a backend buffer from an existing pointer
|
|
||||||
GGML_API ggml_backend_buffer_t ggml_backend_cpu_buffer_from_ptr(ggml_backend_t backend_cpu, void * ptr, size_t size);
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Backend scheduler
|
|
||||||
//
|
|
||||||
|
|
||||||
// The backend scheduler allows for multiple backends to be used together
|
|
||||||
// Handles compute buffer allocation, assignment of tensors to backends, and copying of tensors between backends
|
|
||||||
// The backends are selected based on:
|
|
||||||
// - the backend that supports the operation
|
|
||||||
// - the location of the pre-allocated tensors (e.g. the weights)
|
|
||||||
/*
|
|
||||||
Example usage:
|
|
||||||
|
|
||||||
sched = ggml_backend_sched_new({backend_gpu, backend_gpu2, backend_cpu}, num_backends);
|
|
||||||
// sched is initialized with measure allocators and cannot be used until allocated with a measure graph
|
|
||||||
|
|
||||||
// initialize buffers from a measure graph
|
|
||||||
measure_graph = build_graph(sched); // use the allocr to allocate inputs as needed
|
|
||||||
|
|
||||||
// in build_graph:
|
|
||||||
build_graph(...) {
|
|
||||||
// allocating tensors in a specific backend (optional, recommended: pre-allocate inputs in a different buffer)
|
|
||||||
alloc_cpu = ggml_backend_sched_get_allocr(sched, backend_cpu);
|
|
||||||
ggml_allocr_alloc(alloc_cpu, tensor);
|
|
||||||
|
|
||||||
// manually assigning nodes to a backend (optional, shouldn't be needed in most cases)
|
|
||||||
struct ggml_tensor * node = ggml_mul_mat(ctx, ...);
|
|
||||||
ggml_backend_sched_set_node_backend(sched, node, backend_gpu);
|
|
||||||
}
|
|
||||||
|
|
||||||
// allocate backend buffers from measure graph
|
|
||||||
ggml_backend_sched_init_measure(sched, measure_graph);
|
|
||||||
|
|
||||||
// the scheduler is now ready to compute graphs
|
|
||||||
|
|
||||||
// compute
|
|
||||||
graph = build_graph(sched);
|
|
||||||
ggml_backend_sched_graph_compute(sched, graph);
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct ggml_backend_sched;
|
|
||||||
typedef struct ggml_backend_sched * ggml_backend_sched_t;
|
|
||||||
|
|
||||||
// Initialize a backend scheduler
|
|
||||||
GGML_API ggml_backend_sched_t ggml_backend_sched_new(ggml_backend_t * backends, int n_backends);
|
|
||||||
|
|
||||||
GGML_API void ggml_backend_sched_free(ggml_backend_sched_t sched);
|
|
||||||
|
|
||||||
// Initialize backend buffers from a measure graph
|
|
||||||
GGML_API void ggml_backend_sched_init_measure(ggml_backend_sched_t sched, struct ggml_cgraph * measure_graph);
|
|
||||||
|
|
||||||
GGML_API ggml_tallocr_t ggml_backend_sched_get_tallocr(ggml_backend_sched_t sched, ggml_backend_t backend);
|
|
||||||
GGML_API ggml_backend_buffer_t ggml_backend_sched_get_buffer (ggml_backend_sched_t sched, ggml_backend_t backend);
|
|
||||||
|
|
||||||
GGML_API void ggml_backend_sched_set_node_backend(ggml_backend_sched_t sched, struct ggml_tensor * node, ggml_backend_t backend);
|
|
||||||
|
|
||||||
// Allocate a graph on the backend scheduler
|
|
||||||
GGML_API void ggml_backend_sched_graph_compute(
|
|
||||||
ggml_backend_sched_t sched,
|
|
||||||
struct ggml_cgraph * graph);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -1,249 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "ggml.h"
|
|
||||||
|
|
||||||
// GGML internal header
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <string.h> // memcpy
|
|
||||||
#include <math.h> // fabsf
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// static_assert should be a #define, but if it's not,
|
|
||||||
// fall back to the _Static_assert C11 keyword.
|
|
||||||
// if C99 - static_assert is noop
|
|
||||||
// ref: https://stackoverflow.com/a/53923785/4039976
|
|
||||||
#ifndef static_assert
|
|
||||||
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201100L)
|
|
||||||
#define static_assert(cond, msg) _Static_assert(cond, msg)
|
|
||||||
#else
|
|
||||||
#define static_assert(cond, msg) struct global_scope_noop_trick
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// __FMA__ and __F16C__ are not defined in MSVC, however they are implied with AVX2/AVX512
|
|
||||||
#if defined(_MSC_VER) && (defined(__AVX2__) || defined(__AVX512F__))
|
|
||||||
#ifndef __FMA__
|
|
||||||
#define __FMA__
|
|
||||||
#endif
|
|
||||||
#ifndef __F16C__
|
|
||||||
#define __F16C__
|
|
||||||
#endif
|
|
||||||
#ifndef __SSE3__
|
|
||||||
#define __SSE3__
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#undef MIN
|
|
||||||
#undef MAX
|
|
||||||
|
|
||||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
|
||||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
|
||||||
|
|
||||||
// 16-bit float
|
|
||||||
// on Arm, we use __fp16
|
|
||||||
// on x86, we use uint16_t
|
|
||||||
#if defined(__ARM_NEON) && !defined(_MSC_VER)
|
|
||||||
|
|
||||||
// if YCM cannot find <arm_neon.h>, make a symbolic link to it, for example:
|
|
||||||
//
|
|
||||||
// $ ln -sfn /Library/Developer/CommandLineTools/usr/lib/clang/13.1.6/include/arm_neon.h ./src/
|
|
||||||
//
|
|
||||||
#include <arm_neon.h>
|
|
||||||
|
|
||||||
#define GGML_COMPUTE_FP16_TO_FP32(x) ((float) (x))
|
|
||||||
#define GGML_COMPUTE_FP32_TO_FP16(x) (x)
|
|
||||||
|
|
||||||
#define GGML_FP16_TO_FP32(x) ((float) (x))
|
|
||||||
#define GGML_FP32_TO_FP16(x) (x)
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#ifdef __wasm_simd128__
|
|
||||||
#include <wasm_simd128.h>
|
|
||||||
#else
|
|
||||||
#ifdef __POWER9_VECTOR__
|
|
||||||
#include <altivec.h>
|
|
||||||
#undef bool
|
|
||||||
#define bool _Bool
|
|
||||||
#else
|
|
||||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
|
||||||
#include <intrin.h>
|
|
||||||
#else
|
|
||||||
#if defined(__AVX__) || defined(__AVX2__) || defined(__AVX512F__) || defined(__SSSE3__) || defined(__SSE3__)
|
|
||||||
#if !defined(__riscv)
|
|
||||||
#include <immintrin.h>
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __riscv_v_intrinsic
|
|
||||||
#include <riscv_vector.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __F16C__
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#define GGML_COMPUTE_FP16_TO_FP32(x) _mm_cvtss_f32(_mm_cvtph_ps(_mm_cvtsi32_si128(x)))
|
|
||||||
#define GGML_COMPUTE_FP32_TO_FP16(x) _mm_extract_epi16(_mm_cvtps_ph(_mm_set_ss(x), 0), 0)
|
|
||||||
#else
|
|
||||||
#define GGML_COMPUTE_FP16_TO_FP32(x) _cvtsh_ss(x)
|
|
||||||
#define GGML_COMPUTE_FP32_TO_FP16(x) _cvtss_sh(x, 0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#elif defined(__POWER9_VECTOR__)
|
|
||||||
|
|
||||||
#define GGML_COMPUTE_FP16_TO_FP32(x) ggml_compute_fp16_to_fp32(x)
|
|
||||||
#define GGML_COMPUTE_FP32_TO_FP16(x) ggml_compute_fp32_to_fp16(x)
|
|
||||||
/* the inline asm below is about 12% faster than the lookup method */
|
|
||||||
#define GGML_FP16_TO_FP32(x) GGML_COMPUTE_FP16_TO_FP32(x)
|
|
||||||
#define GGML_FP32_TO_FP16(x) GGML_COMPUTE_FP32_TO_FP16(x)
|
|
||||||
|
|
||||||
static inline float ggml_compute_fp16_to_fp32(ggml_fp16_t h) {
|
|
||||||
register float f;
|
|
||||||
register double d;
|
|
||||||
__asm__(
|
|
||||||
"mtfprd %0,%2\n"
|
|
||||||
"xscvhpdp %0,%0\n"
|
|
||||||
"frsp %1,%0\n" :
|
|
||||||
/* temp */ "=d"(d),
|
|
||||||
/* out */ "=f"(f):
|
|
||||||
/* in */ "r"(h));
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline ggml_fp16_t ggml_compute_fp32_to_fp16(float f) {
|
|
||||||
register double d;
|
|
||||||
register ggml_fp16_t r;
|
|
||||||
__asm__( /* xscvdphp can work on double or single precision */
|
|
||||||
"xscvdphp %0,%2\n"
|
|
||||||
"mffprd %1,%0\n" :
|
|
||||||
/* temp */ "=d"(d),
|
|
||||||
/* out */ "=r"(r):
|
|
||||||
/* in */ "f"(f));
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
// FP16 <-> FP32
|
|
||||||
// ref: https://github.com/Maratyszcza/FP16
|
|
||||||
|
|
||||||
static inline float fp32_from_bits(uint32_t w) {
|
|
||||||
union {
|
|
||||||
uint32_t as_bits;
|
|
||||||
float as_value;
|
|
||||||
} fp32;
|
|
||||||
fp32.as_bits = w;
|
|
||||||
return fp32.as_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint32_t fp32_to_bits(float f) {
|
|
||||||
union {
|
|
||||||
float as_value;
|
|
||||||
uint32_t as_bits;
|
|
||||||
} fp32;
|
|
||||||
fp32.as_value = f;
|
|
||||||
return fp32.as_bits;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline float ggml_compute_fp16_to_fp32(ggml_fp16_t h) {
|
|
||||||
const uint32_t w = (uint32_t) h << 16;
|
|
||||||
const uint32_t sign = w & UINT32_C(0x80000000);
|
|
||||||
const uint32_t two_w = w + w;
|
|
||||||
|
|
||||||
const uint32_t exp_offset = UINT32_C(0xE0) << 23;
|
|
||||||
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) || defined(__GNUC__) && !defined(__STRICT_ANSI__)
|
|
||||||
const float exp_scale = 0x1.0p-112f;
|
|
||||||
#else
|
|
||||||
const float exp_scale = fp32_from_bits(UINT32_C(0x7800000));
|
|
||||||
#endif
|
|
||||||
const float normalized_value = fp32_from_bits((two_w >> 4) + exp_offset) * exp_scale;
|
|
||||||
|
|
||||||
const uint32_t magic_mask = UINT32_C(126) << 23;
|
|
||||||
const float magic_bias = 0.5f;
|
|
||||||
const float denormalized_value = fp32_from_bits((two_w >> 17) | magic_mask) - magic_bias;
|
|
||||||
|
|
||||||
const uint32_t denormalized_cutoff = UINT32_C(1) << 27;
|
|
||||||
const uint32_t result = sign |
|
|
||||||
(two_w < denormalized_cutoff ? fp32_to_bits(denormalized_value) : fp32_to_bits(normalized_value));
|
|
||||||
return fp32_from_bits(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline ggml_fp16_t ggml_compute_fp32_to_fp16(float f) {
|
|
||||||
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) || defined(__GNUC__) && !defined(__STRICT_ANSI__)
|
|
||||||
const float scale_to_inf = 0x1.0p+112f;
|
|
||||||
const float scale_to_zero = 0x1.0p-110f;
|
|
||||||
#else
|
|
||||||
const float scale_to_inf = fp32_from_bits(UINT32_C(0x77800000));
|
|
||||||
const float scale_to_zero = fp32_from_bits(UINT32_C(0x08800000));
|
|
||||||
#endif
|
|
||||||
float base = (fabsf(f) * scale_to_inf) * scale_to_zero;
|
|
||||||
|
|
||||||
const uint32_t w = fp32_to_bits(f);
|
|
||||||
const uint32_t shl1_w = w + w;
|
|
||||||
const uint32_t sign = w & UINT32_C(0x80000000);
|
|
||||||
uint32_t bias = shl1_w & UINT32_C(0xFF000000);
|
|
||||||
if (bias < UINT32_C(0x71000000)) {
|
|
||||||
bias = UINT32_C(0x71000000);
|
|
||||||
}
|
|
||||||
|
|
||||||
base = fp32_from_bits((bias >> 1) + UINT32_C(0x07800000)) + base;
|
|
||||||
const uint32_t bits = fp32_to_bits(base);
|
|
||||||
const uint32_t exp_bits = (bits >> 13) & UINT32_C(0x00007C00);
|
|
||||||
const uint32_t mantissa_bits = bits & UINT32_C(0x00000FFF);
|
|
||||||
const uint32_t nonsign = exp_bits + mantissa_bits;
|
|
||||||
return (sign >> 16) | (shl1_w > UINT32_C(0xFF000000) ? UINT16_C(0x7E00) : nonsign);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define GGML_COMPUTE_FP16_TO_FP32(x) ggml_compute_fp16_to_fp32(x)
|
|
||||||
#define GGML_COMPUTE_FP32_TO_FP16(x) ggml_compute_fp32_to_fp16(x)
|
|
||||||
|
|
||||||
#endif // __F16C__
|
|
||||||
|
|
||||||
#endif // __ARM_NEON
|
|
||||||
|
|
||||||
// precomputed f32 table for f16 (256 KB)
|
|
||||||
// defined in ggml.c, initialized in ggml_init()
|
|
||||||
extern float ggml_table_f32_f16[1 << 16];
|
|
||||||
|
|
||||||
// On ARM NEON, it's quicker to directly convert x -> x instead of calling into ggml_lookup_fp16_to_fp32,
|
|
||||||
// so we define GGML_FP16_TO_FP32 and GGML_FP32_TO_FP16 elsewhere for NEON.
|
|
||||||
// This is also true for POWER9.
|
|
||||||
#if !defined(GGML_FP16_TO_FP32) || !defined(GGML_FP32_TO_FP16)
|
|
||||||
|
|
||||||
inline static float ggml_lookup_fp16_to_fp32(ggml_fp16_t f) {
|
|
||||||
uint16_t s;
|
|
||||||
memcpy(&s, &f, sizeof(uint16_t));
|
|
||||||
return ggml_table_f32_f16[s];
|
|
||||||
}
|
|
||||||
|
|
||||||
#define GGML_FP16_TO_FP32(x) ggml_lookup_fp16_to_fp32(x)
|
|
||||||
#define GGML_FP32_TO_FP16(x) GGML_COMPUTE_FP32_TO_FP16(x)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define GGML_HASHTABLE_FULL ((size_t)-1)
|
|
||||||
#define GGML_HASHTABLE_ALREADY_EXISTS ((size_t)-2)
|
|
||||||
|
|
||||||
bool ggml_hash_contains (const struct ggml_hash_set hash_set, struct ggml_tensor * key);
|
|
||||||
|
|
||||||
// returns GGML_HASHTABLE_FULL if table is full, otherwise the current index of the key or where it should be inserted
|
|
||||||
size_t ggml_hash_find (const struct ggml_hash_set hash_set, struct ggml_tensor * key);
|
|
||||||
|
|
||||||
// returns GGML_HAHSHTABLE_ALREADY_EXISTS if key already exists, index otherwise, asserts if table is full
|
|
||||||
size_t ggml_hash_insert ( struct ggml_hash_set hash_set, struct ggml_tensor * key);
|
|
||||||
|
|
||||||
// return index, asserts if table is full
|
|
||||||
size_t ggml_hash_find_or_insert( struct ggml_hash_set hash_set, struct ggml_tensor * key);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
File diff suppressed because it is too large
Load Diff
@ -1,224 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "ggml-impl.h"
|
|
||||||
|
|
||||||
// GGML internal header
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
#define QK4_0 32
|
|
||||||
typedef struct {
|
|
||||||
ggml_fp16_t d; // delta
|
|
||||||
uint8_t qs[QK4_0 / 2]; // nibbles / quants
|
|
||||||
} block_q4_0;
|
|
||||||
static_assert(sizeof(block_q4_0) == sizeof(ggml_fp16_t) + QK4_0 / 2, "wrong q4_0 block size/padding");
|
|
||||||
|
|
||||||
#define QK4_1 32
|
|
||||||
typedef struct {
|
|
||||||
ggml_fp16_t d; // delta
|
|
||||||
ggml_fp16_t m; // min
|
|
||||||
uint8_t qs[QK4_1 / 2]; // nibbles / quants
|
|
||||||
} block_q4_1;
|
|
||||||
static_assert(sizeof(block_q4_1) == 2 * sizeof(ggml_fp16_t) + QK4_1 / 2, "wrong q4_1 block size/padding");
|
|
||||||
|
|
||||||
#define QK5_0 32
|
|
||||||
typedef struct {
|
|
||||||
ggml_fp16_t d; // delta
|
|
||||||
uint8_t qh[4]; // 5-th bit of quants
|
|
||||||
uint8_t qs[QK5_0 / 2]; // nibbles / quants
|
|
||||||
} block_q5_0;
|
|
||||||
static_assert(sizeof(block_q5_0) == sizeof(ggml_fp16_t) + sizeof(uint32_t) + QK5_0 / 2, "wrong q5_0 block size/padding");
|
|
||||||
|
|
||||||
#define QK5_1 32
|
|
||||||
typedef struct {
|
|
||||||
ggml_fp16_t d; // delta
|
|
||||||
ggml_fp16_t m; // min
|
|
||||||
uint8_t qh[4]; // 5-th bit of quants
|
|
||||||
uint8_t qs[QK5_1 / 2]; // nibbles / quants
|
|
||||||
} block_q5_1;
|
|
||||||
static_assert(sizeof(block_q5_1) == 2 * sizeof(ggml_fp16_t) + sizeof(uint32_t) + QK5_1 / 2, "wrong q5_1 block size/padding");
|
|
||||||
|
|
||||||
#define QK8_0 32
|
|
||||||
typedef struct {
|
|
||||||
ggml_fp16_t d; // delta
|
|
||||||
int8_t qs[QK8_0]; // quants
|
|
||||||
} block_q8_0;
|
|
||||||
static_assert(sizeof(block_q8_0) == sizeof(ggml_fp16_t) + QK8_0, "wrong q8_0 block size/padding");
|
|
||||||
|
|
||||||
#define QK8_1 32
|
|
||||||
typedef struct {
|
|
||||||
float d; // delta
|
|
||||||
float s; // d * sum(qs[i])
|
|
||||||
int8_t qs[QK8_1]; // quants
|
|
||||||
} block_q8_1;
|
|
||||||
static_assert(sizeof(block_q8_1) == 2*sizeof(float) + QK8_1, "wrong q8_1 block size/padding");
|
|
||||||
|
|
||||||
//
|
|
||||||
// Super-block quantization structures
|
|
||||||
//
|
|
||||||
|
|
||||||
// Super-block size
|
|
||||||
#ifdef GGML_QKK_64
|
|
||||||
#define QK_K 64
|
|
||||||
#define K_SCALE_SIZE 4
|
|
||||||
#else
|
|
||||||
#define QK_K 256
|
|
||||||
#define K_SCALE_SIZE 12
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// 2-bit quantization
|
|
||||||
// weight is represented as x = a * q + b
|
|
||||||
// 16 blocks of 16 elements each
|
|
||||||
// Effectively 2.5625 bits per weight
|
|
||||||
typedef struct {
|
|
||||||
uint8_t scales[QK_K/16]; // scales and mins, quantized with 4 bits
|
|
||||||
uint8_t qs[QK_K/4]; // quants
|
|
||||||
ggml_fp16_t d; // super-block scale for quantized scales
|
|
||||||
ggml_fp16_t dmin; // super-block scale for quantized mins
|
|
||||||
} block_q2_K;
|
|
||||||
static_assert(sizeof(block_q2_K) == 2*sizeof(ggml_fp16_t) + QK_K/16 + QK_K/4, "wrong q2_K block size/padding");
|
|
||||||
|
|
||||||
// 3-bit quantization
|
|
||||||
// weight is represented as x = a * q
|
|
||||||
// 16 blocks of 16 elements each
|
|
||||||
// Effectively 3.4375 bits per weight
|
|
||||||
#ifdef GGML_QKK_64
|
|
||||||
typedef struct {
|
|
||||||
uint8_t hmask[QK_K/8]; // quants - high bit
|
|
||||||
uint8_t qs[QK_K/4]; // quants - low 2 bits
|
|
||||||
uint8_t scales[2];
|
|
||||||
ggml_fp16_t d; // super-block scale
|
|
||||||
} block_q3_K;
|
|
||||||
static_assert(sizeof(block_q3_K) == sizeof(ggml_fp16_t) + QK_K / 4 + QK_K / 8 + 2, "wrong q3_K block size/padding");
|
|
||||||
#else
|
|
||||||
typedef struct {
|
|
||||||
uint8_t hmask[QK_K/8]; // quants - high bit
|
|
||||||
uint8_t qs[QK_K/4]; // quants - low 2 bits
|
|
||||||
uint8_t scales[12]; // scales, quantized with 6 bits
|
|
||||||
ggml_fp16_t d; // super-block scale
|
|
||||||
} block_q3_K;
|
|
||||||
static_assert(sizeof(block_q3_K) == sizeof(ggml_fp16_t) + QK_K / 4 + QK_K / 8 + 12, "wrong q3_K block size/padding");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// 4-bit quantization
|
|
||||||
// 8 blocks of 32 elements each
|
|
||||||
// weight is represented as x = a * q + b
|
|
||||||
// Effectively 4.5 bits per weight
|
|
||||||
#ifdef GGML_QKK_64
|
|
||||||
typedef struct {
|
|
||||||
ggml_fp16_t d[2]; // super-block scales/mins
|
|
||||||
uint8_t scales[2]; // 4-bit block scales/mins
|
|
||||||
uint8_t qs[QK_K/2]; // 4--bit quants
|
|
||||||
} block_q4_K;
|
|
||||||
static_assert(sizeof(block_q4_K) == 2*sizeof(ggml_fp16_t) + QK_K/2 + 2, "wrong q4_K block size/padding");
|
|
||||||
#else
|
|
||||||
typedef struct {
|
|
||||||
ggml_fp16_t d; // super-block scale for quantized scales
|
|
||||||
ggml_fp16_t dmin; // super-block scale for quantized mins
|
|
||||||
uint8_t scales[K_SCALE_SIZE]; // scales and mins, quantized with 6 bits
|
|
||||||
uint8_t qs[QK_K/2]; // 4--bit quants
|
|
||||||
} block_q4_K;
|
|
||||||
static_assert(sizeof(block_q4_K) == 2*sizeof(ggml_fp16_t) + K_SCALE_SIZE + QK_K/2, "wrong q4_K block size/padding");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// 5-bit quantization
|
|
||||||
// 8 blocks of 32 elements each
|
|
||||||
// weight is represented as x = a * q + b
|
|
||||||
// Effectively 5.5 bits per weight
|
|
||||||
#ifdef GGML_QKK_64
|
|
||||||
typedef struct {
|
|
||||||
ggml_fp16_t d; // super-block scale
|
|
||||||
int8_t scales[QK_K/16]; // 8-bit block scales
|
|
||||||
uint8_t qh[QK_K/8]; // quants, high bit
|
|
||||||
uint8_t qs[QK_K/2]; // quants, low 4 bits
|
|
||||||
} block_q5_K;
|
|
||||||
static_assert(sizeof(block_q5_K) == sizeof(ggml_fp16_t) + QK_K/2 + QK_K/8 + QK_K/16, "wrong q5_K block size/padding");
|
|
||||||
#else
|
|
||||||
typedef struct {
|
|
||||||
ggml_fp16_t d; // super-block scale for quantized scales
|
|
||||||
ggml_fp16_t dmin; // super-block scale for quantized mins
|
|
||||||
uint8_t scales[K_SCALE_SIZE]; // scales and mins, quantized with 6 bits
|
|
||||||
uint8_t qh[QK_K/8]; // quants, high bit
|
|
||||||
uint8_t qs[QK_K/2]; // quants, low 4 bits
|
|
||||||
} block_q5_K;
|
|
||||||
static_assert(sizeof(block_q5_K) == 2*sizeof(ggml_fp16_t) + K_SCALE_SIZE + QK_K/2 + QK_K/8, "wrong q5_K block size/padding");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// 6-bit quantization
|
|
||||||
// weight is represented as x = a * q
|
|
||||||
// 16 blocks of 16 elements each
|
|
||||||
// Effectively 6.5625 bits per weight
|
|
||||||
typedef struct {
|
|
||||||
uint8_t ql[QK_K/2]; // quants, lower 4 bits
|
|
||||||
uint8_t qh[QK_K/4]; // quants, upper 2 bits
|
|
||||||
int8_t scales[QK_K/16]; // scales, quantized with 8 bits
|
|
||||||
ggml_fp16_t d; // super-block scale
|
|
||||||
} block_q6_K;
|
|
||||||
static_assert(sizeof(block_q6_K) == sizeof(ggml_fp16_t) + QK_K / 16 + 3*QK_K/4, "wrong q6_K block size/padding");
|
|
||||||
|
|
||||||
// This is only used for intermediate quantization and dot products
|
|
||||||
typedef struct {
|
|
||||||
float d; // delta
|
|
||||||
int8_t qs[QK_K]; // quants
|
|
||||||
int16_t bsums[QK_K/16]; // sum of quants in groups of 16
|
|
||||||
} block_q8_K;
|
|
||||||
static_assert(sizeof(block_q8_K) == sizeof(float) + QK_K + QK_K/16*sizeof(int16_t), "wrong q8_K block size/padding");
|
|
||||||
|
|
||||||
|
|
||||||
// Quantization
|
|
||||||
void quantize_row_q4_0_reference(const float * restrict x, block_q4_0 * restrict y, int k);
|
|
||||||
void quantize_row_q4_1_reference(const float * restrict x, block_q4_1 * restrict y, int k);
|
|
||||||
void quantize_row_q5_0_reference(const float * restrict x, block_q5_0 * restrict y, int k);
|
|
||||||
void quantize_row_q5_1_reference(const float * restrict x, block_q5_1 * restrict y, int k);
|
|
||||||
void quantize_row_q8_0_reference(const float * restrict x, block_q8_0 * restrict y, int k);
|
|
||||||
void quantize_row_q8_1_reference(const float * restrict x, block_q8_1 * restrict y, int k);
|
|
||||||
|
|
||||||
void quantize_row_q2_K_reference(const float * restrict x, block_q2_K * restrict y, int k);
|
|
||||||
void quantize_row_q3_K_reference(const float * restrict x, block_q3_K * restrict y, int k);
|
|
||||||
void quantize_row_q4_K_reference(const float * restrict x, block_q4_K * restrict y, int k);
|
|
||||||
void quantize_row_q5_K_reference(const float * restrict x, block_q5_K * restrict y, int k);
|
|
||||||
void quantize_row_q6_K_reference(const float * restrict x, block_q6_K * restrict y, int k);
|
|
||||||
void quantize_row_q8_K_reference(const float * restrict x, block_q8_K * restrict y, int k);
|
|
||||||
|
|
||||||
void quantize_row_q4_0(const float * restrict x, void * restrict y, int k);
|
|
||||||
void quantize_row_q4_1(const float * restrict x, void * restrict y, int k);
|
|
||||||
void quantize_row_q5_0(const float * restrict x, void * restrict y, int k);
|
|
||||||
void quantize_row_q5_1(const float * restrict x, void * restrict y, int k);
|
|
||||||
void quantize_row_q8_0(const float * restrict x, void * restrict y, int k);
|
|
||||||
void quantize_row_q8_1(const float * restrict x, void * restrict y, int k);
|
|
||||||
|
|
||||||
void quantize_row_q2_K(const float * restrict x, void * restrict y, int k);
|
|
||||||
void quantize_row_q3_K(const float * restrict x, void * restrict y, int k);
|
|
||||||
void quantize_row_q4_K(const float * restrict x, void * restrict y, int k);
|
|
||||||
void quantize_row_q5_K(const float * restrict x, void * restrict y, int k);
|
|
||||||
void quantize_row_q6_K(const float * restrict x, void * restrict y, int k);
|
|
||||||
void quantize_row_q8_K(const float * restrict x, void * restrict y, int k);
|
|
||||||
|
|
||||||
// Dequantization
|
|
||||||
void dequantize_row_q4_0(const block_q4_0 * restrict x, float * restrict y, int k);
|
|
||||||
void dequantize_row_q4_1(const block_q4_1 * restrict x, float * restrict y, int k);
|
|
||||||
void dequantize_row_q5_0(const block_q5_0 * restrict x, float * restrict y, int k);
|
|
||||||
void dequantize_row_q5_1(const block_q5_1 * restrict x, float * restrict y, int k);
|
|
||||||
void dequantize_row_q8_0(const block_q8_0 * restrict x, float * restrict y, int k);
|
|
||||||
//void dequantize_row_q8_1(const block_q8_1 * restrict x, float * restrict y, int k);
|
|
||||||
|
|
||||||
void dequantize_row_q2_K(const block_q2_K * restrict x, float * restrict y, int k);
|
|
||||||
void dequantize_row_q3_K(const block_q3_K * restrict x, float * restrict y, int k);
|
|
||||||
void dequantize_row_q4_K(const block_q4_K * restrict x, float * restrict y, int k);
|
|
||||||
void dequantize_row_q5_K(const block_q5_K * restrict x, float * restrict y, int k);
|
|
||||||
void dequantize_row_q6_K(const block_q6_K * restrict x, float * restrict y, int k);
|
|
||||||
void dequantize_row_q8_K(const block_q8_K * restrict x, float * restrict y, int k);
|
|
||||||
|
|
||||||
// Dot product
|
|
||||||
void ggml_vec_dot_q4_0_q8_0(int n, float * restrict s, const void * restrict vx, const void * restrict vy);
|
|
||||||
void ggml_vec_dot_q4_1_q8_1(int n, float * restrict s, const void * restrict vx, const void * restrict vy);
|
|
||||||
void ggml_vec_dot_q5_0_q8_0(int n, float * restrict s, const void * restrict vx, const void * restrict vy);
|
|
||||||
void ggml_vec_dot_q5_1_q8_1(int n, float * restrict s, const void * restrict vx, const void * restrict vy);
|
|
||||||
void ggml_vec_dot_q8_0_q8_0(int n, float * restrict s, const void * restrict vx, const void * restrict vy);
|
|
||||||
|
|
||||||
void ggml_vec_dot_q2_K_q8_K(int n, float * restrict s, const void * restrict vx, const void * restrict vy);
|
|
||||||
void ggml_vec_dot_q3_K_q8_K(int n, float * restrict s, const void * restrict vx, const void * restrict vy);
|
|
||||||
void ggml_vec_dot_q4_K_q8_K(int n, float * restrict s, const void * restrict vx, const void * restrict vy);
|
|
||||||
void ggml_vec_dot_q5_K_q8_K(int n, float * restrict s, const void * restrict vx, const void * restrict vy);
|
|
||||||
void ggml_vec_dot_q6_K_q8_K(int n, float * restrict s, const void * restrict vx, const void * restrict vy);
|
|
@ -87,7 +87,7 @@ static VALUE ruby_whisper_initialize(int argc, VALUE *argv, VALUE self) {
|
|||||||
if (!rb_respond_to(whisper_model_file_path, rb_intern("to_s"))) {
|
if (!rb_respond_to(whisper_model_file_path, rb_intern("to_s"))) {
|
||||||
rb_raise(rb_eRuntimeError, "Expected file path to model to initialize Whisper::Context");
|
rb_raise(rb_eRuntimeError, "Expected file path to model to initialize Whisper::Context");
|
||||||
}
|
}
|
||||||
rw->context = whisper_init_from_file_with_params(StringValueCStr(whisper_model_file_path), whisper_context_default_params());
|
rw->context = whisper_init_from_file(StringValueCStr(whisper_model_file_path));
|
||||||
if (rw->context == nullptr) {
|
if (rw->context == nullptr) {
|
||||||
rb_raise(rb_eRuntimeError, "error: failed to initialize whisper context");
|
rb_raise(rb_eRuntimeError, "error: failed to initialize whisper context");
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ API_AVAILABLE(macos(12.0), ios(15.0), watchos(8.0), tvos(15.0)) __attribute__((v
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
Make a prediction using the convenience interface
|
Make a prediction using the convenience interface
|
||||||
@param logmel_data as 1 × n_mel × 3000 3-dimensional array of floats:
|
@param logmel_data as 1 × 80 × 3000 3-dimensional array of floats:
|
||||||
@param error If an error occurs, upon return contains an NSError object that describes the problem. If you are not interested in possible errors, pass in NULL.
|
@param error If an error occurs, upon return contains an NSError object that describes the problem. If you are not interested in possible errors, pass in NULL.
|
||||||
@return the prediction as whisper_encoder_implOutput
|
@return the prediction as whisper_encoder_implOutput
|
||||||
*/
|
*/
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
// Code is derived from the work of Github user @wangchou
|
// Code is derived from the work of Github user @wangchou
|
||||||
// ref: https://github.com/wangchou/callCoreMLFromCpp
|
// ref: https://github.com/wangchou/callCoreMLFromCpp
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#if __cplusplus
|
#if __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
@ -16,8 +14,6 @@ void whisper_coreml_free(struct whisper_coreml_context * ctx);
|
|||||||
|
|
||||||
void whisper_coreml_encode(
|
void whisper_coreml_encode(
|
||||||
const whisper_coreml_context * ctx,
|
const whisper_coreml_context * ctx,
|
||||||
int64_t n_ctx,
|
|
||||||
int64_t n_mel,
|
|
||||||
float * mel,
|
float * mel,
|
||||||
float * out);
|
float * out);
|
||||||
|
|
||||||
|
@ -22,13 +22,7 @@ struct whisper_coreml_context * whisper_coreml_init(const char * path_model) {
|
|||||||
|
|
||||||
NSURL * url_model = [NSURL fileURLWithPath: path_model_str];
|
NSURL * url_model = [NSURL fileURLWithPath: path_model_str];
|
||||||
|
|
||||||
// select which device to run the Core ML model on
|
const void * data = CFBridgingRetain([[whisper_encoder_impl alloc] initWithContentsOfURL:url_model error:nil]);
|
||||||
MLModelConfiguration *config = [[MLModelConfiguration alloc] init];
|
|
||||||
// config.computeUnits = MLComputeUnitsCPUAndGPU;
|
|
||||||
//config.computeUnits = MLComputeUnitsCPUAndNeuralEngine;
|
|
||||||
config.computeUnits = MLComputeUnitsAll;
|
|
||||||
|
|
||||||
const void * data = CFBridgingRetain([[whisper_encoder_impl alloc] initWithContentsOfURL:url_model configuration:config error:nil]);
|
|
||||||
|
|
||||||
if (data == NULL) {
|
if (data == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -48,15 +42,13 @@ void whisper_coreml_free(struct whisper_coreml_context * ctx) {
|
|||||||
|
|
||||||
void whisper_coreml_encode(
|
void whisper_coreml_encode(
|
||||||
const whisper_coreml_context * ctx,
|
const whisper_coreml_context * ctx,
|
||||||
int64_t n_ctx,
|
|
||||||
int64_t n_mel,
|
|
||||||
float * mel,
|
float * mel,
|
||||||
float * out) {
|
float * out) {
|
||||||
MLMultiArray * inMultiArray = [
|
MLMultiArray * inMultiArray = [
|
||||||
[MLMultiArray alloc] initWithDataPointer: mel
|
[MLMultiArray alloc] initWithDataPointer: mel
|
||||||
shape: @[@1, @(n_mel), @(n_ctx)]
|
shape: @[@1, @80, @3000]
|
||||||
dataType: MLMultiArrayDataTypeFloat32
|
dataType: MLMultiArrayDataTypeFloat32
|
||||||
strides: @[@(n_ctx*n_mel), @(n_ctx), @1]
|
strides: @[@(240000), @(3000), @1]
|
||||||
deallocator: nil
|
deallocator: nil
|
||||||
error: nil
|
error: nil
|
||||||
];
|
];
|
||||||
|
@ -14,10 +14,6 @@ if (WHISPER_SDL2)
|
|||||||
message(STATUS "SDL2_LIBRARIES = ${SDL2_LIBRARIES}")
|
message(STATUS "SDL2_LIBRARIES = ${SDL2_LIBRARIES}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (WHISPER_CLBLAST)
|
|
||||||
find_package(CLBlast REQUIRED)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# common
|
# common
|
||||||
|
|
||||||
set(TARGET common)
|
set(TARGET common)
|
||||||
@ -27,7 +23,6 @@ add_library(${TARGET} STATIC
|
|||||||
common.cpp
|
common.cpp
|
||||||
common-ggml.h
|
common-ggml.h
|
||||||
common-ggml.cpp
|
common-ggml.cpp
|
||||||
grammar-parser.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
include(DefaultTargetOptions)
|
include(DefaultTargetOptions)
|
||||||
@ -69,7 +64,6 @@ elseif(CMAKE_JS_VERSION)
|
|||||||
else()
|
else()
|
||||||
add_subdirectory(main)
|
add_subdirectory(main)
|
||||||
add_subdirectory(stream)
|
add_subdirectory(stream)
|
||||||
add_subdirectory(server)
|
|
||||||
add_subdirectory(command)
|
add_subdirectory(command)
|
||||||
add_subdirectory(bench)
|
add_subdirectory(bench)
|
||||||
add_subdirectory(quantize)
|
add_subdirectory(quantize)
|
||||||
@ -77,5 +71,3 @@ else()
|
|||||||
add_subdirectory(talk-llama)
|
add_subdirectory(talk-llama)
|
||||||
add_subdirectory(lsp)
|
add_subdirectory(lsp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory(wchess)
|
|
||||||
|
@ -11,7 +11,6 @@ const whisperParamsMock = {
|
|||||||
language: "en",
|
language: "en",
|
||||||
model: path.join(__dirname, "../../../models/ggml-base.en.bin"),
|
model: path.join(__dirname, "../../../models/ggml-base.en.bin"),
|
||||||
fname_inp: path.join(__dirname, "../../../samples/jfk.wav"),
|
fname_inp: path.join(__dirname, "../../../samples/jfk.wav"),
|
||||||
use_gpu: true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
describe("Run whisper.node", () => {
|
describe("Run whisper.node", () => {
|
||||||
|
@ -36,7 +36,6 @@ struct whisper_params {
|
|||||||
bool print_colors = false;
|
bool print_colors = false;
|
||||||
bool print_progress = false;
|
bool print_progress = false;
|
||||||
bool no_timestamps = false;
|
bool no_timestamps = false;
|
||||||
bool use_gpu = true;
|
|
||||||
|
|
||||||
std::string language = "en";
|
std::string language = "en";
|
||||||
std::string prompt;
|
std::string prompt;
|
||||||
@ -154,9 +153,7 @@ int run(whisper_params ¶ms, std::vector<std::vector<std::string>> &result) {
|
|||||||
|
|
||||||
// whisper init
|
// whisper init
|
||||||
|
|
||||||
struct whisper_context_params cparams = whisper_context_default_params();
|
struct whisper_context * ctx = whisper_init_from_file(params.model.c_str());
|
||||||
cparams.use_gpu = params.use_gpu;
|
|
||||||
struct whisper_context * ctx = whisper_init_from_file_with_params(params.model.c_str(), cparams);
|
|
||||||
|
|
||||||
if (ctx == nullptr) {
|
if (ctx == nullptr) {
|
||||||
fprintf(stderr, "error: failed to initialize whisper context\n");
|
fprintf(stderr, "error: failed to initialize whisper context\n");
|
||||||
@ -318,12 +315,10 @@ Napi::Value whisper(const Napi::CallbackInfo& info) {
|
|||||||
std::string language = whisper_params.Get("language").As<Napi::String>();
|
std::string language = whisper_params.Get("language").As<Napi::String>();
|
||||||
std::string model = whisper_params.Get("model").As<Napi::String>();
|
std::string model = whisper_params.Get("model").As<Napi::String>();
|
||||||
std::string input = whisper_params.Get("fname_inp").As<Napi::String>();
|
std::string input = whisper_params.Get("fname_inp").As<Napi::String>();
|
||||||
bool use_gpu = whisper_params.Get("use_gpu").As<Napi::Boolean>();
|
|
||||||
|
|
||||||
params.language = language;
|
params.language = language;
|
||||||
params.model = model;
|
params.model = model;
|
||||||
params.fname_inp.emplace_back(input);
|
params.fname_inp.emplace_back(input);
|
||||||
params.use_gpu = use_gpu;
|
|
||||||
|
|
||||||
Napi::Function callback = info[1].As<Napi::Function>();
|
Napi::Function callback = info[1].As<Napi::Function>();
|
||||||
Worker* worker = new Worker(callback, params);
|
Worker* worker = new Worker(callback, params);
|
||||||
|
@ -11,7 +11,6 @@ const whisperParams = {
|
|||||||
language: "en",
|
language: "en",
|
||||||
model: path.join(__dirname, "../../models/ggml-base.en.bin"),
|
model: path.join(__dirname, "../../models/ggml-base.en.bin"),
|
||||||
fname_inp: "../../samples/jfk.wav",
|
fname_inp: "../../samples/jfk.wav",
|
||||||
use_gpu: true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const arguments = process.argv.slice(2);
|
const arguments = process.argv.slice(2);
|
||||||
|
@ -23,9 +23,7 @@ void bench_main(size_t index) {
|
|||||||
|
|
||||||
fprintf(stderr, "%s: running benchmark with %d threads - please wait...\n", __func__, n_threads);
|
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, WHISPER_N_MEL)) {
|
||||||
|
|
||||||
if (int ret = whisper_set_mel(ctx, nullptr, 0, n_mels)) {
|
|
||||||
fprintf(stderr, "error: failed to set mel: %d\n", ret);
|
fprintf(stderr, "error: failed to set mel: %d\n", ret);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -59,7 +57,7 @@ EMSCRIPTEN_BINDINGS(bench) {
|
|||||||
emscripten::function("init", emscripten::optional_override([](const std::string & path_model) {
|
emscripten::function("init", emscripten::optional_override([](const std::string & path_model) {
|
||||||
for (size_t i = 0; i < g_contexts.size(); ++i) {
|
for (size_t i = 0; i < g_contexts.size(); ++i) {
|
||||||
if (g_contexts[i] == nullptr) {
|
if (g_contexts[i] == nullptr) {
|
||||||
g_contexts[i] = whisper_init_from_file_with_params(path_model.c_str(), whisper_context_default_params());
|
g_contexts[i] = whisper_init_from_file(path_model.c_str());
|
||||||
if (g_contexts[i] != nullptr) {
|
if (g_contexts[i] != nullptr) {
|
||||||
if (g_worker.joinable()) {
|
if (g_worker.joinable()) {
|
||||||
g_worker.join();
|
g_worker.join();
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#include "whisper.h"
|
#include "whisper.h"
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
@ -11,8 +10,6 @@ struct whisper_params {
|
|||||||
int32_t what = 0; // what to benchmark: 0 - whisper ecoder, 1 - memcpy, 2 - ggml_mul_mat
|
int32_t what = 0; // what to benchmark: 0 - whisper ecoder, 1 - memcpy, 2 - ggml_mul_mat
|
||||||
|
|
||||||
std::string model = "models/ggml-base.en.bin";
|
std::string model = "models/ggml-base.en.bin";
|
||||||
|
|
||||||
bool use_gpu = true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void whisper_print_usage(int argc, char ** argv, const whisper_params & params);
|
void whisper_print_usage(int argc, char ** argv, const whisper_params & params);
|
||||||
@ -28,7 +25,6 @@ bool whisper_params_parse(int argc, char ** argv, whisper_params & params) {
|
|||||||
else if (arg == "-t" || arg == "--threads") { params.n_threads = std::stoi(argv[++i]); }
|
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 == "-m" || arg == "--model") { params.model = argv[++i]; }
|
||||||
else if (arg == "-w" || arg == "--what") { params.what = atoi(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 {
|
else {
|
||||||
fprintf(stderr, "error: unknown argument: %s\n", arg.c_str());
|
fprintf(stderr, "error: unknown argument: %s\n", arg.c_str());
|
||||||
whisper_print_usage(argc, argv, params);
|
whisper_print_usage(argc, argv, params);
|
||||||
@ -48,20 +44,16 @@ void whisper_print_usage(int /*argc*/, char ** argv, const whisper_params & para
|
|||||||
fprintf(stderr, " -t N, --threads N [%-7d] number of threads to use during computation\n", params.n_threads);
|
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, " -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, " -w N, --what N [%-7d] what to benchmark:\n", params.what);
|
||||||
fprintf(stderr, " -ng, --no-gpu [%-7s] disable GPU\n", params.use_gpu ? "false" : "true");
|
fprintf(stderr, " %-7s 0 - whisper encoder\n", "");
|
||||||
fprintf(stderr, " %-7s 0 - whisper\n", "");
|
|
||||||
fprintf(stderr, " %-7s 1 - memcpy\n", "");
|
fprintf(stderr, " %-7s 1 - memcpy\n", "");
|
||||||
fprintf(stderr, " %-7s 2 - ggml_mul_mat\n", "");
|
fprintf(stderr, " %-7s 2 - ggml_mul_mat\n", "");
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
int whisper_bench_full(const whisper_params & params) {
|
int whisper_bench_encoder(const whisper_params & params) {
|
||||||
// whisper init
|
// whisper init
|
||||||
|
|
||||||
struct whisper_context_params cparams = whisper_context_default_params();
|
struct whisper_context * ctx = whisper_init_from_file(params.model.c_str());
|
||||||
cparams.use_gpu = params.use_gpu;
|
|
||||||
|
|
||||||
struct whisper_context * ctx = whisper_init_from_file_with_params(params.model.c_str(), cparams);
|
|
||||||
|
|
||||||
{
|
{
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
@ -73,65 +65,16 @@ int whisper_bench_full(const whisper_params & params) {
|
|||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int n_mels = whisper_model_n_mels(ctx);
|
if (int ret = whisper_set_mel(ctx, nullptr, 0, WHISPER_N_MEL)) {
|
||||||
|
|
||||||
if (int ret = whisper_set_mel(ctx, nullptr, 0, n_mels)) {
|
|
||||||
fprintf(stderr, "error: failed to set mel: %d\n", ret);
|
fprintf(stderr, "error: failed to set mel: %d\n", ret);
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
// heat encoder
|
|
||||||
if (int ret = whisper_encode(ctx, 0, params.n_threads) != 0) {
|
if (int ret = whisper_encode(ctx, 0, params.n_threads) != 0) {
|
||||||
fprintf(stderr, "error: failed to encode: %d\n", ret);
|
fprintf(stderr, "error: failed to encode model: %d\n", ret);
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
whisper_token tokens[512];
|
|
||||||
memset(tokens, 0, sizeof(tokens));
|
|
||||||
|
|
||||||
// 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
|
|
||||||
if (int ret = whisper_decode(ctx, tokens, 1, 256, 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_print_timings(ctx);
|
||||||
whisper_free(ctx);
|
whisper_free(ctx);
|
||||||
|
|
||||||
@ -160,7 +103,7 @@ int main(int argc, char ** argv) {
|
|||||||
int ret = -1;
|
int ret = -1;
|
||||||
|
|
||||||
switch (params.what) {
|
switch (params.what) {
|
||||||
case 0: ret = whisper_bench_full(params); break;
|
case 0: ret = whisper_bench_encoder(params); break;
|
||||||
case 1: ret = whisper_bench_memcpy(params.n_threads); break;
|
case 1: ret = whisper_bench_memcpy(params.n_threads); break;
|
||||||
case 2: ret = whisper_bench_ggml_mul_mat(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;
|
default: fprintf(stderr, "error: unknown benchmark: %d\n", params.what); break;
|
||||||
|
@ -243,7 +243,7 @@ EMSCRIPTEN_BINDINGS(command) {
|
|||||||
emscripten::function("init", emscripten::optional_override([](const std::string & path_model) {
|
emscripten::function("init", emscripten::optional_override([](const std::string & path_model) {
|
||||||
for (size_t i = 0; i < g_contexts.size(); ++i) {
|
for (size_t i = 0; i < g_contexts.size(); ++i) {
|
||||||
if (g_contexts[i] == nullptr) {
|
if (g_contexts[i] == nullptr) {
|
||||||
g_contexts[i] = whisper_init_from_file_with_params(path_model.c_str(), whisper_context_default_params());
|
g_contexts[i] = whisper_init_from_file(path_model.c_str());
|
||||||
if (g_contexts[i] != nullptr) {
|
if (g_contexts[i] != nullptr) {
|
||||||
g_running = true;
|
g_running = true;
|
||||||
if (g_worker.joinable()) {
|
if (g_worker.joinable()) {
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
#include "common-sdl.h"
|
#include "common-sdl.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "whisper.h"
|
#include "whisper.h"
|
||||||
#include "grammar-parser.h"
|
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
@ -22,11 +21,6 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
bool file_exists(const std::string & fname) {
|
|
||||||
std::ifstream f(fname.c_str());
|
|
||||||
return f.good();
|
|
||||||
}
|
|
||||||
|
|
||||||
// command-line parameters
|
// command-line parameters
|
||||||
struct whisper_params {
|
struct whisper_params {
|
||||||
int32_t n_threads = std::min(4, (int32_t) std::thread::hardware_concurrency());
|
int32_t n_threads = std::min(4, (int32_t) std::thread::hardware_concurrency());
|
||||||
@ -39,24 +33,17 @@ struct whisper_params {
|
|||||||
float vad_thold = 0.6f;
|
float vad_thold = 0.6f;
|
||||||
float freq_thold = 100.0f;
|
float freq_thold = 100.0f;
|
||||||
|
|
||||||
float grammar_penalty = 100.0f;
|
|
||||||
|
|
||||||
grammar_parser::parse_state grammar_parsed;
|
|
||||||
|
|
||||||
bool speed_up = false;
|
bool speed_up = false;
|
||||||
bool translate = false;
|
bool translate = false;
|
||||||
bool print_special = false;
|
bool print_special = false;
|
||||||
bool print_energy = false;
|
bool print_energy = false;
|
||||||
bool no_timestamps = true;
|
bool no_timestamps = true;
|
||||||
bool use_gpu = true;
|
|
||||||
|
|
||||||
std::string language = "en";
|
std::string language = "en";
|
||||||
std::string model = "models/ggml-base.en.bin";
|
std::string model = "models/ggml-base.en.bin";
|
||||||
std::string fname_out;
|
std::string fname_out;
|
||||||
std::string commands;
|
std::string commands;
|
||||||
std::string prompt;
|
std::string prompt;
|
||||||
std::string context;
|
|
||||||
std::string grammar;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void whisper_print_usage(int argc, char ** argv, const whisper_params & params);
|
void whisper_print_usage(int argc, char ** argv, const whisper_params & params);
|
||||||
@ -81,15 +68,11 @@ bool whisper_params_parse(int argc, char ** argv, whisper_params & params) {
|
|||||||
else if (arg == "-tr" || arg == "--translate") { params.translate = true; }
|
else if (arg == "-tr" || arg == "--translate") { params.translate = true; }
|
||||||
else if (arg == "-ps" || arg == "--print-special") { params.print_special = 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 == "-pe" || arg == "--print-energy") { params.print_energy = true; }
|
||||||
else if (arg == "-ng" || arg == "--no-gpu") { params.use_gpu = false; }
|
|
||||||
else if (arg == "-l" || arg == "--language") { params.language = argv[++i]; }
|
else if (arg == "-l" || arg == "--language") { params.language = argv[++i]; }
|
||||||
else if (arg == "-m" || arg == "--model") { params.model = 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 == "-f" || arg == "--file") { params.fname_out = argv[++i]; }
|
||||||
else if (arg == "-cmd" || arg == "--commands") { params.commands = 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 == "-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 {
|
else {
|
||||||
fprintf(stderr, "error: unknown argument: %s\n", arg.c_str());
|
fprintf(stderr, "error: unknown argument: %s\n", arg.c_str());
|
||||||
whisper_print_usage(argc, argv, params);
|
whisper_print_usage(argc, argv, params);
|
||||||
@ -118,36 +101,21 @@ void whisper_print_usage(int /*argc*/, char ** argv, const whisper_params & para
|
|||||||
fprintf(stderr, " -tr, --translate [%-7s] translate from source language to english\n", params.translate ? "true" : "false");
|
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, " -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, " -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, " -l LANG, --language LANG [%-7s] spoken language\n", params.language.c_str());
|
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, " -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, " -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, " -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, " -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, "\n");
|
fprintf(stderr, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string transcribe(
|
std::string transcribe(whisper_context * ctx, const whisper_params & params, const std::vector<float> & pcmf32, float & prob, int64_t & t_ms) {
|
||||||
whisper_context * ctx,
|
|
||||||
const whisper_params & params,
|
|
||||||
const std::vector<float> & 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();
|
const auto t_start = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
logprob_min = 0.0f;
|
prob = 0.0f;
|
||||||
logprob_sum = 0.0f;
|
|
||||||
n_tokens = 0;
|
|
||||||
t_ms = 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_GREEDY);
|
||||||
whisper_full_params wparams = whisper_full_default_params(WHISPER_SAMPLING_BEAM_SEARCH);
|
|
||||||
|
|
||||||
wparams.print_progress = false;
|
wparams.print_progress = false;
|
||||||
wparams.print_special = params.print_special;
|
wparams.print_special = params.print_special;
|
||||||
@ -155,7 +123,6 @@ std::string transcribe(
|
|||||||
wparams.print_timestamps = !params.no_timestamps;
|
wparams.print_timestamps = !params.no_timestamps;
|
||||||
wparams.translate = params.translate;
|
wparams.translate = params.translate;
|
||||||
wparams.no_context = true;
|
wparams.no_context = true;
|
||||||
wparams.no_timestamps = params.no_timestamps;
|
|
||||||
wparams.single_segment = true;
|
wparams.single_segment = true;
|
||||||
wparams.max_tokens = params.max_tokens;
|
wparams.max_tokens = params.max_tokens;
|
||||||
wparams.language = params.language.c_str();
|
wparams.language = params.language.c_str();
|
||||||
@ -164,32 +131,11 @@ std::string transcribe(
|
|||||||
wparams.audio_ctx = params.audio_ctx;
|
wparams.audio_ctx = params.audio_ctx;
|
||||||
wparams.speed_up = params.speed_up;
|
wparams.speed_up = params.speed_up;
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
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) {
|
if (whisper_full(ctx, wparams, pcmf32.data(), pcmf32.size()) != 0) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int prob_n = 0;
|
||||||
std::string result;
|
std::string result;
|
||||||
|
|
||||||
const int n_segments = whisper_full_n_segments(ctx);
|
const int n_segments = whisper_full_n_segments(ctx);
|
||||||
@ -198,17 +144,19 @@ std::string transcribe(
|
|||||||
|
|
||||||
result += text;
|
result += text;
|
||||||
|
|
||||||
const int n = whisper_full_n_tokens(ctx, i);
|
const int n_tokens = whisper_full_n_tokens(ctx, i);
|
||||||
for (int j = 0; j < n; ++j) {
|
for (int j = 0; j < n_tokens; ++j) {
|
||||||
const auto token = whisper_full_get_token_data(ctx, i, j);
|
const auto token = whisper_full_get_token_data(ctx, i, j);
|
||||||
|
|
||||||
if(token.plog > 0.0f) exit(0);
|
prob += token.p;
|
||||||
logprob_min = std::min(logprob_min, token.plog);
|
++prob_n;
|
||||||
logprob_sum += token.plog;
|
|
||||||
++n_tokens;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (prob_n > 0) {
|
||||||
|
prob /= prob_n;
|
||||||
|
}
|
||||||
|
|
||||||
const auto t_end = std::chrono::high_resolution_clock::now();
|
const auto t_end = std::chrono::high_resolution_clock::now();
|
||||||
t_ms = std::chrono::duration_cast<std::chrono::milliseconds>(t_end - t_start).count();
|
t_ms = std::chrono::duration_cast<std::chrono::milliseconds>(t_end - t_start).count();
|
||||||
|
|
||||||
@ -467,9 +415,7 @@ int always_prompt_transcription(struct whisper_context * ctx, audio_async & audi
|
|||||||
bool is_running = true;
|
bool is_running = true;
|
||||||
bool ask_prompt = true;
|
bool ask_prompt = true;
|
||||||
|
|
||||||
float logprob_min = 0.0f;
|
float prob = 0.0f;
|
||||||
float logprob_sum = 0.0f;
|
|
||||||
int n_tokens = 0;
|
|
||||||
|
|
||||||
std::vector<float> pcmf32_cur;
|
std::vector<float> pcmf32_cur;
|
||||||
|
|
||||||
@ -507,7 +453,7 @@ int always_prompt_transcription(struct whisper_context * ctx, audio_async & audi
|
|||||||
// detect the commands
|
// detect the commands
|
||||||
audio.get(params.command_ms, pcmf32_cur);
|
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 txt = ::trim(::transcribe(ctx, params, pcmf32_cur, prob, t_ms));
|
||||||
|
|
||||||
const auto words = get_words(txt);
|
const auto words = get_words(txt);
|
||||||
|
|
||||||
@ -543,27 +489,18 @@ int always_prompt_transcription(struct whisper_context * ctx, audio_async & audi
|
|||||||
|
|
||||||
// general-purpose mode
|
// general-purpose mode
|
||||||
// freely transcribe the voice into text
|
// freely transcribe the voice into text
|
||||||
int process_general_transcription(struct whisper_context * ctx, audio_async & audio, const whisper_params & params) {
|
int process_general_transcription(struct whisper_context * ctx, audio_async &audio, const whisper_params ¶ms) {
|
||||||
bool is_running = true;
|
bool is_running = true;
|
||||||
bool have_prompt = false;
|
bool have_prompt = false;
|
||||||
bool ask_prompt = true;
|
bool ask_prompt = true;
|
||||||
|
|
||||||
float logprob_min0 = 0.0f;
|
float prob0 = 0.0f;
|
||||||
float logprob_min = 0.0f;
|
float prob = 0.0f;
|
||||||
|
|
||||||
float logprob_sum0 = 0.0f;
|
|
||||||
float logprob_sum = 0.0f;
|
|
||||||
|
|
||||||
int n_tokens0 = 0;
|
|
||||||
int n_tokens = 0;
|
|
||||||
|
|
||||||
std::vector<float> pcmf32_cur;
|
std::vector<float> pcmf32_cur;
|
||||||
std::vector<float> pcmf32_prompt;
|
std::vector<float> pcmf32_prompt;
|
||||||
|
|
||||||
std::string k_prompt = "Ok Whisper, start listening for commands.";
|
const std::string k_prompt = "Ok Whisper, start listening for commands.";
|
||||||
if (!params.prompt.empty()) {
|
|
||||||
k_prompt = params.prompt;
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
fprintf(stderr, "%s: general-purpose mode\n", __func__);
|
fprintf(stderr, "%s: general-purpose mode\n", __func__);
|
||||||
@ -596,11 +533,9 @@ int process_general_transcription(struct whisper_context * ctx, audio_async & au
|
|||||||
// wait for activation phrase
|
// wait for activation phrase
|
||||||
audio.get(params.prompt_ms, pcmf32_cur);
|
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 auto txt = ::trim(::transcribe(ctx, params, pcmf32_cur, prob0, t_ms));
|
||||||
|
|
||||||
const float p = 100.0f * std::exp(logprob_min0);
|
fprintf(stdout, "%s: Heard '%s%s%s', (t = %d ms)\n", __func__, "\033[1m", txt.c_str(), "\033[0m", (int) t_ms);
|
||||||
|
|
||||||
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);
|
const float sim = similarity(txt, k_prompt);
|
||||||
|
|
||||||
@ -621,30 +556,19 @@ int process_general_transcription(struct whisper_context * ctx, audio_async & au
|
|||||||
// we have heard the activation phrase, now detect the commands
|
// we have heard the activation phrase, now detect the commands
|
||||||
audio.get(params.command_ms, pcmf32_cur);
|
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
|
// prepend the prompt audio
|
||||||
pcmf32_cur.insert(pcmf32_cur.begin(), pcmf32_prompt.begin(), pcmf32_prompt.end());
|
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 auto txt = ::trim(::transcribe(ctx, params, pcmf32_cur, prob, t_ms));
|
||||||
|
|
||||||
//const float p = 100.0f * std::exp((logprob - logprob0) / (n_tokens - n_tokens0));
|
prob = 100.0f*(prob - prob0);
|
||||||
const float p = 100.0f * std::exp(logprob_min);
|
|
||||||
|
|
||||||
//fprintf(stdout, "%s: heard '%s'\n", __func__, txt.c_str());
|
//fprintf(stdout, "%s: heard '%s'\n", __func__, txt.c_str());
|
||||||
|
|
||||||
// find the prompt in the text
|
// find the prompt in the text
|
||||||
float best_sim = 0.0f;
|
float best_sim = 0.0f;
|
||||||
size_t best_len = 0;
|
size_t best_len = 0;
|
||||||
for (size_t n = 0.8*k_prompt.size(); n <= 1.2*k_prompt.size(); ++n) {
|
for (int 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 auto prompt = txt.substr(0, n);
|
||||||
|
|
||||||
const float sim = similarity(prompt, k_prompt);
|
const float sim = similarity(prompt, k_prompt);
|
||||||
@ -657,16 +581,9 @@ int process_general_transcription(struct whisper_context * ctx, audio_async & au
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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));
|
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, "%s: Command '%s%s%s', (t = %d ms)\n", __func__, "\033[1m", command.c_str(), "\033[0m", (int) t_ms);
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stdout, "\n");
|
fprintf(stdout, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -693,10 +610,7 @@ int main(int argc, char ** argv) {
|
|||||||
|
|
||||||
// whisper init
|
// whisper init
|
||||||
|
|
||||||
struct whisper_context_params cparams = whisper_context_default_params();
|
struct whisper_context * ctx = whisper_init_from_file(params.model.c_str());
|
||||||
cparams.use_gpu = params.use_gpu;
|
|
||||||
|
|
||||||
struct whisper_context * ctx = whisper_init_from_file_with_params(params.model.c_str(), cparams);
|
|
||||||
|
|
||||||
// print some info about the processing
|
// print some info about the processing
|
||||||
{
|
{
|
||||||
@ -734,37 +648,13 @@ int main(int argc, char ** argv) {
|
|||||||
|
|
||||||
int ret_val = 0;
|
int ret_val = 0;
|
||||||
|
|
||||||
if (!params.grammar.empty()) {
|
|
||||||
auto & grammar = params.grammar_parsed;
|
|
||||||
if (file_exists(params.grammar.c_str())) {
|
|
||||||
// read grammar from file
|
|
||||||
std::ifstream ifs(params.grammar.c_str());
|
|
||||||
const std::string txt = std::string((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret_val == 0) {
|
|
||||||
if (!params.commands.empty()) {
|
if (!params.commands.empty()) {
|
||||||
ret_val = process_command_list(ctx, audio, params);
|
ret_val = process_command_list(ctx, audio, params);
|
||||||
} else if (!params.prompt.empty() && params.grammar_parsed.rules.empty()) {
|
} else if (!params.prompt.empty()) {
|
||||||
ret_val = always_prompt_transcription(ctx, audio, params);
|
ret_val = always_prompt_transcription(ctx, audio, params);
|
||||||
} else {
|
} else {
|
||||||
ret_val = process_general_transcription(ctx, audio, params);
|
ret_val = process_general_transcription(ctx, audio, params);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
audio.pause();
|
audio.pause();
|
||||||
|
|
||||||
|
@ -9,11 +9,6 @@ static const std::map<std::string, enum ggml_ftype> GGML_FTYPE_MAP = {
|
|||||||
{"q5_0", GGML_FTYPE_MOSTLY_Q5_0},
|
{"q5_0", GGML_FTYPE_MOSTLY_Q5_0},
|
||||||
{"q5_1", GGML_FTYPE_MOSTLY_Q5_1},
|
{"q5_1", GGML_FTYPE_MOSTLY_Q5_1},
|
||||||
{"q8_0", GGML_FTYPE_MOSTLY_Q8_0},
|
{"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) {
|
void ggml_print_ftypes(FILE * fp) {
|
||||||
@ -53,18 +48,15 @@ bool ggml_common_quantize_0(
|
|||||||
case GGML_FTYPE_MOSTLY_Q5_0: qtype = GGML_TYPE_Q5_0; 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_Q5_1: qtype = GGML_TYPE_Q5_1; break;
|
||||||
case GGML_FTYPE_MOSTLY_Q8_0: qtype = GGML_TYPE_Q8_0; 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_UNKNOWN:
|
||||||
case GGML_FTYPE_ALL_F32:
|
case GGML_FTYPE_ALL_F32:
|
||||||
case GGML_FTYPE_MOSTLY_F16:
|
case GGML_FTYPE_MOSTLY_F16:
|
||||||
case GGML_FTYPE_MOSTLY_Q4_1_SOME_F16:
|
case GGML_FTYPE_MOSTLY_Q4_1_SOME_F16:
|
||||||
case GGML_FTYPE_MOSTLY_IQ2_XXS:
|
case GGML_FTYPE_MOSTLY_Q2_K:
|
||||||
case GGML_FTYPE_MOSTLY_IQ2_XS:
|
case GGML_FTYPE_MOSTLY_Q3_K:
|
||||||
case GGML_FTYPE_MOSTLY_IQ3_XXS:
|
case GGML_FTYPE_MOSTLY_Q4_K:
|
||||||
|
case GGML_FTYPE_MOSTLY_Q5_K:
|
||||||
|
case GGML_FTYPE_MOSTLY_Q6_K:
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: invalid model type %d\n", __func__, ftype);
|
fprintf(stderr, "%s: invalid model type %d\n", __func__, ftype);
|
||||||
return false;
|
return false;
|
||||||
@ -175,17 +167,24 @@ bool ggml_common_quantize_0(
|
|||||||
|
|
||||||
switch ((ggml_type) ttype) {
|
switch ((ggml_type) ttype) {
|
||||||
case GGML_TYPE_Q4_0:
|
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], hist_cur.data(), nullptr);
|
cur_size = ggml_quantize_q4_0(data_f32.data(), work.data(), nelements, ne[0], hist_cur.data());
|
||||||
|
} break;
|
||||||
|
case GGML_TYPE_Q4_1:
|
||||||
|
{
|
||||||
|
cur_size = ggml_quantize_q4_1(data_f32.data(), work.data(), nelements, ne[0], hist_cur.data());
|
||||||
|
} break;
|
||||||
|
case GGML_TYPE_Q5_0:
|
||||||
|
{
|
||||||
|
cur_size = ggml_quantize_q5_0(data_f32.data(), work.data(), nelements, ne[0], hist_cur.data());
|
||||||
|
} break;
|
||||||
|
case GGML_TYPE_Q5_1:
|
||||||
|
{
|
||||||
|
cur_size = ggml_quantize_q5_1(data_f32.data(), work.data(), nelements, ne[0], hist_cur.data());
|
||||||
|
} break;
|
||||||
|
case GGML_TYPE_Q8_0:
|
||||||
|
{
|
||||||
|
cur_size = ggml_quantize_q8_0(data_f32.data(), work.data(), nelements, ne[0], hist_cur.data());
|
||||||
} break;
|
} break;
|
||||||
case GGML_TYPE_F32:
|
case GGML_TYPE_F32:
|
||||||
case GGML_TYPE_F16:
|
case GGML_TYPE_F16:
|
||||||
@ -193,10 +192,12 @@ bool ggml_common_quantize_0(
|
|||||||
case GGML_TYPE_I16:
|
case GGML_TYPE_I16:
|
||||||
case GGML_TYPE_I32:
|
case GGML_TYPE_I32:
|
||||||
case GGML_TYPE_Q8_1:
|
case GGML_TYPE_Q8_1:
|
||||||
|
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:
|
||||||
case GGML_TYPE_Q8_K:
|
case GGML_TYPE_Q8_K:
|
||||||
case GGML_TYPE_IQ2_XXS:
|
|
||||||
case GGML_TYPE_IQ2_XS:
|
|
||||||
case GGML_TYPE_IQ3_XXS:
|
|
||||||
case GGML_TYPE_COUNT:
|
case GGML_TYPE_COUNT:
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: unsupported quantization type %d (%s)\n", __func__, ttype, ggml_type_name((ggml_type) ttype));
|
fprintf(stderr, "%s: unsupported quantization type %d (%s)\n", __func__, ttype, ggml_type_name((ggml_type) ttype));
|
||||||
|
@ -139,13 +139,10 @@ void audio_async::callback(uint8_t * stream, int len) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t n_samples = len / sizeof(float);
|
const size_t n_samples = len / sizeof(float);
|
||||||
|
|
||||||
if (n_samples > m_audio.size()) {
|
m_audio_new.resize(n_samples);
|
||||||
n_samples = m_audio.size();
|
memcpy(m_audio_new.data(), stream, n_samples * sizeof(float));
|
||||||
|
|
||||||
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);
|
//fprintf(stderr, "%s: %zu samples, pos %zu, len %zu\n", __func__, n_samples, m_audio_pos, m_audio_len);
|
||||||
|
|
||||||
@ -156,7 +153,7 @@ void audio_async::callback(uint8_t * stream, int len) {
|
|||||||
const size_t n0 = m_audio.size() - m_audio_pos;
|
const size_t n0 = m_audio.size() - m_audio_pos;
|
||||||
|
|
||||||
memcpy(&m_audio[m_audio_pos], stream, n0 * sizeof(float));
|
memcpy(&m_audio[m_audio_pos], stream, n0 * sizeof(float));
|
||||||
memcpy(&m_audio[0], stream + n0 * sizeof(float), (n_samples - n0) * sizeof(float));
|
memcpy(&m_audio[0], &stream[n0], (n_samples - n0) * sizeof(float));
|
||||||
|
|
||||||
m_audio_pos = (m_audio_pos + n_samples) % m_audio.size();
|
m_audio_pos = (m_audio_pos + n_samples) % m_audio.size();
|
||||||
m_audio_len = m_audio.size();
|
m_audio_len = m_audio.size();
|
||||||
|
@ -41,6 +41,7 @@ private:
|
|||||||
std::mutex m_mutex;
|
std::mutex m_mutex;
|
||||||
|
|
||||||
std::vector<float> m_audio;
|
std::vector<float> m_audio;
|
||||||
|
std::vector<float> m_audio_new;
|
||||||
size_t m_audio_pos = 0;
|
size_t m_audio_pos = 0;
|
||||||
size_t m_audio_len = 0;
|
size_t m_audio_len = 0;
|
||||||
};
|
};
|
||||||
|
@ -38,12 +38,12 @@ bool gpt_params_parse(int argc, char ** argv, gpt_params & params) {
|
|||||||
params.seed = std::stoi(get_next_arg(i, argc, argv, arg, params));
|
params.seed = std::stoi(get_next_arg(i, argc, argv, arg, params));
|
||||||
} else if (arg == "-t" || arg == "--threads") {
|
} else if (arg == "-t" || arg == "--threads") {
|
||||||
params.n_threads = std::stoi(get_next_arg(i, argc, argv, arg, params));
|
params.n_threads = 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 == "-p" || arg == "--prompt") {
|
} else if (arg == "-p" || arg == "--prompt") {
|
||||||
params.prompt = get_next_arg(i, argc, argv, arg, params);
|
params.prompt = get_next_arg(i, argc, argv, arg, params);
|
||||||
} else if (arg == "-n" || arg == "--n_predict") {
|
} else if (arg == "-n" || arg == "--n_predict") {
|
||||||
params.n_predict = std::stoi(get_next_arg(i, argc, argv, arg, params));
|
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") {
|
} else if (arg == "--top_k") {
|
||||||
params.top_k = std::stoi(get_next_arg(i, argc, argv, arg, params));
|
params.top_k = std::stoi(get_next_arg(i, argc, argv, arg, params));
|
||||||
} else if (arg == "--top_p") {
|
} else if (arg == "--top_p") {
|
||||||
@ -56,12 +56,6 @@ bool gpt_params_parse(int argc, char ** argv, gpt_params & params) {
|
|||||||
params.repeat_penalty = std::stof(get_next_arg(i, argc, argv, arg, params));
|
params.repeat_penalty = std::stof(get_next_arg(i, argc, argv, arg, params));
|
||||||
} else if (arg == "-b" || arg == "--batch_size") {
|
} else if (arg == "-b" || arg == "--batch_size") {
|
||||||
params.n_batch= std::stoi(get_next_arg(i, argc, argv, arg, params));
|
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") {
|
} else if (arg == "-m" || arg == "--model") {
|
||||||
params.model = get_next_arg(i, argc, argv, arg, params);
|
params.model = get_next_arg(i, argc, argv, arg, params);
|
||||||
} else if (arg == "-i" || arg == "--interactive") {
|
} else if (arg == "-i" || arg == "--interactive") {
|
||||||
@ -103,6 +97,7 @@ void gpt_print_usage(int /*argc*/, char ** argv, const gpt_params & params) {
|
|||||||
fprintf(stderr, " -h, --help show this help message and exit\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, " -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, " -t N, --threads N number of threads to use during computation (default: %d)\n", params.n_threads);
|
||||||
|
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, " -p PROMPT, --prompt PROMPT\n");
|
fprintf(stderr, " -p PROMPT, --prompt PROMPT\n");
|
||||||
fprintf(stderr, " prompt to start generation with (default: random)\n");
|
fprintf(stderr, " prompt to start generation with (default: random)\n");
|
||||||
fprintf(stderr, " -f FNAME, --file FNAME\n");
|
fprintf(stderr, " -f FNAME, --file FNAME\n");
|
||||||
@ -116,9 +111,6 @@ void gpt_print_usage(int /*argc*/, char ** argv, const gpt_params & params) {
|
|||||||
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-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, " --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, " -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, " -m FNAME, --model FNAME\n");
|
||||||
fprintf(stderr, " model path (default: %s)\n", params.model.c_str());
|
fprintf(stderr, " model path (default: %s)\n", params.model.c_str());
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
@ -615,21 +607,6 @@ gpt_vocab::id gpt_sample_top_k_top_p_repeat(
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_wav_buffer(const std::string buf) {
|
|
||||||
// RIFF ref: https://en.wikipedia.org/wiki/Resource_Interchange_File_Format
|
|
||||||
// WAV ref: https://www.mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html
|
|
||||||
if (buf.size() < 12 || buf.substr(0, 4) != "RIFF" || buf.substr(8, 4) != "WAVE") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t chunk_size = *reinterpret_cast<const uint32_t*>(buf.data() + 4);
|
|
||||||
if (chunk_size + 8 != buf.size()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool read_wav(const std::string & fname, std::vector<float>& pcmf32, std::vector<std::vector<float>>& pcmf32s, bool stereo) {
|
bool read_wav(const std::string & fname, std::vector<float>& pcmf32, std::vector<std::vector<float>>& pcmf32s, bool stereo) {
|
||||||
drwav wav;
|
drwav wav;
|
||||||
std::vector<uint8_t> wav_data; // used for pipe input from stdin
|
std::vector<uint8_t> wav_data; // used for pipe input from stdin
|
||||||
@ -654,12 +631,6 @@ bool read_wav(const std::string & fname, std::vector<float>& pcmf32, std::vector
|
|||||||
|
|
||||||
fprintf(stderr, "%s: read %zu bytes from stdin\n", __func__, wav_data.size());
|
fprintf(stderr, "%s: read %zu bytes from stdin\n", __func__, wav_data.size());
|
||||||
}
|
}
|
||||||
else if (is_wav_buffer(fname)) {
|
|
||||||
if (drwav_init_memory(&wav, fname.c_str(), fname.size(), nullptr) == false) {
|
|
||||||
fprintf(stderr, "error: failed to open WAV file from fname buffer\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (drwav_init_file(&wav, fname.c_str(), nullptr) == false) {
|
else if (drwav_init_file(&wav, fname.c_str(), nullptr) == false) {
|
||||||
fprintf(stderr, "error: failed to open '%s' as WAV file\n", fname.c_str());
|
fprintf(stderr, "error: failed to open '%s' as WAV file\n", fname.c_str());
|
||||||
return false;
|
return false;
|
||||||
|
@ -7,8 +7,6 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <ctime>
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
#define COMMON_SAMPLE_RATE 16000
|
#define COMMON_SAMPLE_RATE 16000
|
||||||
|
|
||||||
@ -20,12 +18,7 @@ struct gpt_params {
|
|||||||
int32_t seed = -1; // RNG seed
|
int32_t seed = -1; // RNG seed
|
||||||
int32_t n_threads = std::min(4, (int32_t) std::thread::hardware_concurrency());
|
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_predict = 200; // new tokens to predict
|
||||||
int32_t n_parallel = 1; // number of parallel streams
|
|
||||||
int32_t n_batch = 8; // batch size for prompt processing
|
int32_t n_batch = 8; // 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
|
// sampling parameters
|
||||||
int32_t top_k = 40;
|
int32_t top_k = 40;
|
||||||
@ -40,6 +33,8 @@ struct gpt_params {
|
|||||||
|
|
||||||
bool interactive = false;
|
bool interactive = false;
|
||||||
int32_t interactive_port = -1;
|
int32_t interactive_port = -1;
|
||||||
|
|
||||||
|
int32_t n_gpu_layers = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool gpt_params_parse(int argc, char ** argv, gpt_params & params);
|
bool gpt_params_parse(int argc, char ** argv, gpt_params & params);
|
||||||
@ -135,11 +130,7 @@ gpt_vocab::id gpt_sample_top_k_top_p_repeat(
|
|||||||
// Audio utils
|
// Audio utils
|
||||||
//
|
//
|
||||||
|
|
||||||
// Check if a buffer is a WAV audio file
|
|
||||||
bool is_wav_buffer(const std::string buf);
|
|
||||||
|
|
||||||
// Read WAV audio file and store the PCM data into pcmf32
|
// 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
|
// 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
|
// If stereo flag is set and the audio has 2 channels, the pcmf32s will contain 2 channel PCM
|
||||||
bool read_wav(
|
bool read_wav(
|
||||||
@ -148,104 +139,6 @@ bool read_wav(
|
|||||||
std::vector<std::vector<float>> & pcmf32s,
|
std::vector<std::vector<float>> & pcmf32s,
|
||||||
bool stereo);
|
bool stereo);
|
||||||
|
|
||||||
// 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<const char *>(&sub_chunk_size), 4);
|
|
||||||
file.write(reinterpret_cast<const char *>(&audio_format), 2);
|
|
||||||
file.write(reinterpret_cast<const char *>(&channels), 2);
|
|
||||||
file.write(reinterpret_cast<const char *>(&sample_rate), 4);
|
|
||||||
file.write(reinterpret_cast<const char *>(&byte_rate), 4);
|
|
||||||
file.write(reinterpret_cast<const char *>(&block_align), 2);
|
|
||||||
file.write(reinterpret_cast<const char *>(&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 = data[i] * 32767;
|
|
||||||
file.write(reinterpret_cast<const char *>(&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<char *>(&fileSize), 4);
|
|
||||||
file.seekp(40, std::ios::beg);
|
|
||||||
file.write(reinterpret_cast<char *>(&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
|
// Apply a high-pass frequency filter to PCM audio
|
||||||
// Suppresses frequencies below cutoff Hz
|
// Suppresses frequencies below cutoff Hz
|
||||||
void high_pass_filter(
|
void high_pass_filter(
|
||||||
|
@ -1,423 +0,0 @@
|
|||||||
#include "grammar-parser.h"
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cwchar>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <exception>
|
|
||||||
|
|
||||||
namespace grammar_parser {
|
|
||||||
// NOTE: assumes valid utf8 (but checks for overrun)
|
|
||||||
// copied from whisper.cpp
|
|
||||||
std::pair<uint32_t, const char *> 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<uint8_t>(*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<uint8_t>(*pos) & 0x3F);
|
|
||||||
}
|
|
||||||
return std::make_pair(value, pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t get_symbol_id(parse_state & state, const char * src, size_t len) {
|
|
||||||
uint32_t next_id = static_cast<uint32_t>(state.symbol_ids.size());
|
|
||||||
auto result = state.symbol_ids.insert(std::make_pair(std::string(src, len), next_id));
|
|
||||||
return result.first->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t generate_symbol_id(parse_state & state, const std::string & base_name) {
|
|
||||||
uint32_t next_id = static_cast<uint32_t>(state.symbol_ids.size());
|
|
||||||
state.symbol_ids[base_name + '_' + std::to_string(next_id)] = next_id;
|
|
||||||
return next_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_rule(
|
|
||||||
parse_state & state,
|
|
||||||
uint32_t rule_id,
|
|
||||||
const std::vector<whisper_grammar_element> & rule) {
|
|
||||||
if (state.rules.size() <= rule_id) {
|
|
||||||
state.rules.resize(rule_id + 1);
|
|
||||||
}
|
|
||||||
state.rules[rule_id] = rule;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_word_char(char c) {
|
|
||||||
return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '-' || ('0' <= c && c <= '9');
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<uint32_t, const char *> 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<uint32_t, const char *> 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");
|
|
||||||
}
|
|
||||||
|
|
||||||
const char * parse_alternates(
|
|
||||||
parse_state & state,
|
|
||||||
const char * src,
|
|
||||||
const std::string & rule_name,
|
|
||||||
uint32_t rule_id,
|
|
||||||
bool is_nested);
|
|
||||||
|
|
||||||
const char * parse_sequence(
|
|
||||||
parse_state & state,
|
|
||||||
const char * src,
|
|
||||||
const std::string & rule_name,
|
|
||||||
std::vector<whisper_grammar_element> & 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 preceeding 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<whisper_grammar_element> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char * parse_alternates(
|
|
||||||
parse_state & state,
|
|
||||||
const char * src,
|
|
||||||
const std::string & rule_name,
|
|
||||||
uint32_t rule_id,
|
|
||||||
bool is_nested) {
|
|
||||||
std::vector<whisper_grammar_element> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_grammar_char(FILE * file, uint32_t c) {
|
|
||||||
if (0x20 <= c && c <= 0x7f) {
|
|
||||||
fprintf(file, "%c", static_cast<char>(c));
|
|
||||||
} else {
|
|
||||||
// cop out of encoding UTF-8
|
|
||||||
fprintf(file, "<U+%04X>", c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_rule_binary(FILE * file, const std::vector<whisper_grammar_element> & 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");
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_rule(
|
|
||||||
FILE * file,
|
|
||||||
uint32_t rule_id,
|
|
||||||
const std::vector<whisper_grammar_element> & rule,
|
|
||||||
const std::map<uint32_t, std::string> & 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<uint32_t, std::string> 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<const whisper_grammar_element *> parse_state::c_rules() const{
|
|
||||||
std::vector<const whisper_grammar_element *> ret;
|
|
||||||
for (const auto & rule : rules) {
|
|
||||||
ret.push_back(rule.data());
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
// 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 <vector>
|
|
||||||
#include <map>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace grammar_parser {
|
|
||||||
struct parse_state {
|
|
||||||
std::map<std::string, uint32_t> symbol_ids;
|
|
||||||
std::vector<std::vector<whisper_grammar_element>> rules;
|
|
||||||
|
|
||||||
std::vector<const whisper_grammar_element *> c_rules() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
parse_state parse(const char * src);
|
|
||||||
void print_grammar(FILE * file, const parse_state & state);
|
|
||||||
}
|
|
@ -22,7 +22,6 @@ var printTextarea = (function() {
|
|||||||
async function clearCache() {
|
async function clearCache() {
|
||||||
if (confirm('Are you sure you want to clear the cache?\nAll the models will be downloaded again.')) {
|
if (confirm('Are you sure you want to clear the cache?\nAll the models will be downloaded again.')) {
|
||||||
indexedDB.deleteDatabase(dbName);
|
indexedDB.deleteDatabase(dbName);
|
||||||
location.reload();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ if [ -n "$3" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Whisper models
|
# Whisper models
|
||||||
models=( "tiny.en" "tiny" "base.en" "base" "small.en" "small" "medium.en" "medium" "large-v1" "large-v2" "large-v3" )
|
models=( "tiny.en" "tiny" "base.en" "base" "small.en" "small" "medium.en" "medium" "large-v1" "large" )
|
||||||
|
|
||||||
# list available models
|
# list available models
|
||||||
function list_models {
|
function list_models {
|
||||||
|
@ -30,7 +30,6 @@ struct whisper_params {
|
|||||||
bool translate = false;
|
bool translate = false;
|
||||||
bool print_special = false;
|
bool print_special = false;
|
||||||
bool print_energy = false;
|
bool print_energy = false;
|
||||||
bool use_gpu = true;
|
|
||||||
|
|
||||||
std::string language = "en";
|
std::string language = "en";
|
||||||
std::string model = "models/ggml-base.en.bin";
|
std::string model = "models/ggml-base.en.bin";
|
||||||
@ -73,7 +72,6 @@ bool whisper_params_parse(int argc, char ** argv, whisper_params & params) {
|
|||||||
else if (arg == "-tr" || arg == "--translate") { params.translate = true; }
|
else if (arg == "-tr" || arg == "--translate") { params.translate = true; }
|
||||||
else if (arg == "-ps" || arg == "--print-special") { params.print_special = 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 == "-pe" || arg == "--print-energy") { params.print_energy = true; }
|
||||||
else if (arg == "-ng" || arg == "--no-gpu") { params.use_gpu = false; }
|
|
||||||
else if (arg == "-l" || arg == "--language") { params.language = argv[++i]; }
|
else if (arg == "-l" || arg == "--language") { params.language = argv[++i]; }
|
||||||
else if (arg == "-m" || arg == "--model") { params.model = argv[++i]; }
|
else if (arg == "-m" || arg == "--model") { params.model = argv[++i]; }
|
||||||
else {
|
else {
|
||||||
@ -104,7 +102,6 @@ void whisper_print_usage(int /*argc*/, char ** argv, const whisper_params & para
|
|||||||
fprintf(stderr, " -tr, --translate [%-7s] translate from source language to english\n", params.translate ? "true" : "false");
|
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, " -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, " -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, " -l LANG, --language LANG [%-7s] spoken language\n", params.language.c_str());
|
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, " -m FNAME, --model FNAME [%-7s] model path\n", params.model.c_str());
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
@ -435,9 +432,7 @@ int main(int argc, char ** argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// whisper init
|
// whisper init
|
||||||
struct whisper_context_params cparams = whisper_context_default_params();
|
struct whisper_context * ctx = whisper_init_from_file(params.model.c_str());
|
||||||
cparams.use_gpu = params.use_gpu;
|
|
||||||
struct whisper_context * ctx = whisper_init_from_file_with_params(params.model.c_str(), cparams);
|
|
||||||
// init audio
|
// init audio
|
||||||
|
|
||||||
audio_async audio(30*1000);
|
audio_async audio(30*1000);
|
||||||
|
@ -17,37 +17,28 @@ options:
|
|||||||
-d N, --duration N [0 ] duration of audio to process in milliseconds
|
-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
|
-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
|
-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
|
-bo N, --best-of N [5 ] number of best candidates to keep
|
||||||
-bs N, --beam-size N [5 ] beam size for beam search
|
-bs N, --beam-size N [-1 ] beam size for beam search
|
||||||
-wt N, --word-thold N [0.01 ] word timestamp probability threshold
|
-wt N, --word-thold N [0.01 ] word timestamp probability threshold
|
||||||
-et N, --entropy-thold N [2.40 ] entropy threshold for decoder fail
|
-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
|
-lpt N, --logprob-thold N [-1.00 ] log probability threshold for decoder fail
|
||||||
-debug, --debug-mode [false ] enable debug mode (eg. dump log_mel)
|
-su, --speed-up [false ] speed up audio by x2 (reduced accuracy)
|
||||||
-tr, --translate [false ] translate from source language to english
|
-tr, --translate [false ] translate from source language to english
|
||||||
-di, --diarize [false ] stereo audio diarization
|
-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
|
-nf, --no-fallback [false ] do not use temperature fallback while decoding
|
||||||
-otxt, --output-txt [false ] output result in a text file
|
-otxt, --output-txt [false ] output result in a text file
|
||||||
-ovtt, --output-vtt [false ] output result in a vtt file
|
-ovtt, --output-vtt [false ] output result in a vtt file
|
||||||
-osrt, --output-srt [false ] output result in a srt 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
|
-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
|
-ocsv, --output-csv [false ] output result in a CSV file
|
||||||
-oj, --output-json [false ] output result in a JSON 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)
|
-of FNAME, --output-file FNAME [ ] output file path (without file extension)
|
||||||
-ps, --print-special [false ] print special tokens
|
-ps, --print-special [false ] print special tokens
|
||||||
-pc, --print-colors [false ] print colors
|
-pc, --print-colors [false ] print colors
|
||||||
-pp, --print-progress [false ] print progress
|
-pp, --print-progress [false ] print progress
|
||||||
-nt, --no-timestamps [false ] do not print timestamps
|
-nt, --no-timestamps [true ] do not print timestamps
|
||||||
-l LANG, --language LANG [en ] spoken language ('auto' for auto-detect)
|
-l LANG, --language LANG [en ] spoken language ('auto' for auto-detect)
|
||||||
-dl, --detect-language [false ] exit after automatically detecting language
|
|
||||||
--prompt PROMPT [ ] initial prompt
|
--prompt PROMPT [ ] initial prompt
|
||||||
-m FNAME, --model FNAME [models/ggml-base.en.bin] model path
|
-m FNAME, --model FNAME [models/ggml-base.en.bin] model path
|
||||||
-f FNAME, --file FNAME [ ] input WAV file path
|
-f FNAME, --file FNAME [ ] input WAV file path
|
||||||
-oved D, --ov-e-device DNAME [CPU ] the OpenVINO device used for encode inference
|
|
||||||
-ls, --log-score [false ] log best decoder scores of tokens
|
|
||||||
-ng, --no-gpu [false ] disable GPU
|
|
||||||
```
|
```
|
||||||
|
@ -62,9 +62,8 @@ struct whisper_params {
|
|||||||
int32_t progress_step = 5;
|
int32_t progress_step = 5;
|
||||||
int32_t max_context = -1;
|
int32_t max_context = -1;
|
||||||
int32_t max_len = 0;
|
int32_t max_len = 0;
|
||||||
int32_t best_of = whisper_full_default_params(WHISPER_SAMPLING_GREEDY).greedy.best_of;
|
int32_t best_of = 2;
|
||||||
int32_t beam_size = whisper_full_default_params(WHISPER_SAMPLING_BEAM_SEARCH).beam_search.beam_size;
|
int32_t beam_size = -1;
|
||||||
int32_t audio_ctx = 0;
|
|
||||||
|
|
||||||
float word_thold = 0.01f;
|
float word_thold = 0.01f;
|
||||||
float entropy_thold = 2.40f;
|
float entropy_thold = 2.40f;
|
||||||
@ -84,15 +83,12 @@ struct whisper_params {
|
|||||||
bool output_wts = false;
|
bool output_wts = false;
|
||||||
bool output_csv = false;
|
bool output_csv = false;
|
||||||
bool output_jsn = false;
|
bool output_jsn = false;
|
||||||
bool output_jsn_full = false;
|
|
||||||
bool output_lrc = false;
|
bool output_lrc = false;
|
||||||
bool no_prints = false;
|
|
||||||
bool print_special = false;
|
bool print_special = false;
|
||||||
bool print_colors = false;
|
bool print_colors = false;
|
||||||
bool print_progress = false;
|
bool print_progress = false;
|
||||||
bool no_timestamps = false;
|
bool no_timestamps = false;
|
||||||
bool log_score = false;
|
bool log_score = false;
|
||||||
bool use_gpu = true;
|
|
||||||
|
|
||||||
std::string language = "en";
|
std::string language = "en";
|
||||||
std::string prompt;
|
std::string prompt;
|
||||||
@ -137,7 +133,6 @@ bool whisper_params_parse(int argc, char ** argv, whisper_params & params) {
|
|||||||
else if (arg == "-ml" || arg == "--max-len") { params.max_len = std::stoi(argv[++i]); }
|
else if (arg == "-ml" || arg == "--max-len") { params.max_len = std::stoi(argv[++i]); }
|
||||||
else if (arg == "-bo" || arg == "--best-of") { params.best_of = std::stoi(argv[++i]); }
|
else if (arg == "-bo" || arg == "--best-of") { params.best_of = std::stoi(argv[++i]); }
|
||||||
else if (arg == "-bs" || arg == "--beam-size") { params.beam_size = std::stoi(argv[++i]); }
|
else if (arg == "-bs" || arg == "--beam-size") { params.beam_size = std::stoi(argv[++i]); }
|
||||||
else if (arg == "-ac" || arg == "--audio-context") { params.audio_ctx = std::stoi(argv[++i]); }
|
|
||||||
else if (arg == "-wt" || arg == "--word-thold") { params.word_thold = std::stof(argv[++i]); }
|
else if (arg == "-wt" || arg == "--word-thold") { params.word_thold = std::stof(argv[++i]); }
|
||||||
else if (arg == "-et" || arg == "--entropy-thold") { params.entropy_thold = std::stof(argv[++i]); }
|
else if (arg == "-et" || arg == "--entropy-thold") { params.entropy_thold = std::stof(argv[++i]); }
|
||||||
else if (arg == "-lpt" || arg == "--logprob-thold") { params.logprob_thold = std::stof(argv[++i]); }
|
else if (arg == "-lpt" || arg == "--logprob-thold") { params.logprob_thold = std::stof(argv[++i]); }
|
||||||
@ -156,9 +151,7 @@ bool whisper_params_parse(int argc, char ** argv, whisper_params & params) {
|
|||||||
else if (arg == "-fp" || arg == "--font-path") { params.font_path = argv[++i]; }
|
else if (arg == "-fp" || arg == "--font-path") { params.font_path = argv[++i]; }
|
||||||
else if (arg == "-ocsv" || arg == "--output-csv") { params.output_csv = true; }
|
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 == "-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[++i]); }
|
else if (arg == "-of" || arg == "--output-file") { params.fname_out.emplace_back(argv[++i]); }
|
||||||
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 == "-ps" || arg == "--print-special") { params.print_special = true; }
|
||||||
else if (arg == "-pc" || arg == "--print-colors") { params.print_colors = true; }
|
else if (arg == "-pc" || arg == "--print-colors") { params.print_colors = true; }
|
||||||
else if (arg == "-pp" || arg == "--print-progress") { params.print_progress = true; }
|
else if (arg == "-pp" || arg == "--print-progress") { params.print_progress = true; }
|
||||||
@ -170,7 +163,6 @@ bool whisper_params_parse(int argc, char ** argv, whisper_params & params) {
|
|||||||
else if (arg == "-f" || arg == "--file") { params.fname_inp.emplace_back(argv[++i]); }
|
else if (arg == "-f" || arg == "--file") { params.fname_inp.emplace_back(argv[++i]); }
|
||||||
else if (arg == "-oved" || arg == "--ov-e-device") { params.openvino_encode_device = argv[++i]; }
|
else if (arg == "-oved" || arg == "--ov-e-device") { params.openvino_encode_device = argv[++i]; }
|
||||||
else if (arg == "-ls" || arg == "--log-score") { params.log_score = true; }
|
else if (arg == "-ls" || arg == "--log-score") { params.log_score = true; }
|
||||||
else if (arg == "-ng" || arg == "--no-gpu") { params.use_gpu = false; }
|
|
||||||
else {
|
else {
|
||||||
fprintf(stderr, "error: unknown argument: %s\n", arg.c_str());
|
fprintf(stderr, "error: unknown argument: %s\n", arg.c_str());
|
||||||
whisper_print_usage(argc, argv, params);
|
whisper_print_usage(argc, argv, params);
|
||||||
@ -197,7 +189,6 @@ void whisper_print_usage(int /*argc*/, char ** argv, const whisper_params & para
|
|||||||
fprintf(stderr, " -sow, --split-on-word [%-7s] split on word rather than on token\n", params.split_on_word ? "true" : "false");
|
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, " -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, " -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, " -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, " -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, " -lpt N, --logprob-thold N [%-7.2f] log probability threshold for decoder fail\n", params.logprob_thold);
|
||||||
@ -215,9 +206,7 @@ void whisper_print_usage(int /*argc*/, char ** argv, const whisper_params & para
|
|||||||
fprintf(stderr, " -fp, --font-path [%-7s] path to a monospace font for karaoke video\n", params.font_path.c_str());
|
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, " -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, " -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, " -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, " -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, " -pc, --print-colors [%-7s] print colors\n", params.print_colors ? "true" : "false");
|
||||||
fprintf(stderr, " -pp, --print-progress [%-7s] print progress\n", params.print_progress ? "true" : "false");
|
fprintf(stderr, " -pp, --print-progress [%-7s] print progress\n", params.print_progress ? "true" : "false");
|
||||||
@ -229,7 +218,6 @@ void whisper_print_usage(int /*argc*/, char ** argv, const whisper_params & para
|
|||||||
fprintf(stderr, " -f FNAME, --file FNAME [%-7s] input WAV file path\n", "");
|
fprintf(stderr, " -f FNAME, --file FNAME [%-7s] input WAV 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, " -oved D, --ov-e-device DNAME [%-7s] the OpenVINO device used for encode inference\n", params.openvino_encode_device.c_str());
|
||||||
fprintf(stderr, " -ls, --log-score [%-7s] log best decoder scores of tokens\n", params.log_score?"true":"false");
|
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, "\n");
|
fprintf(stderr, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -523,12 +511,7 @@ bool output_score(struct whisper_context * ctx, const char * fname, const whispe
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool output_json(
|
bool output_json(struct whisper_context * ctx, const char * fname, const whisper_params & params, std::vector<std::vector<float>> pcmf32s) {
|
||||||
struct whisper_context * ctx,
|
|
||||||
const char * fname,
|
|
||||||
const whisper_params & params,
|
|
||||||
std::vector<std::vector<float>> pcmf32s,
|
|
||||||
bool full) {
|
|
||||||
std::ofstream fout(fname);
|
std::ofstream fout(fname);
|
||||||
int indent = 0;
|
int indent = 0;
|
||||||
|
|
||||||
@ -545,7 +528,7 @@ bool output_json(
|
|||||||
auto end_arr = [&](bool end) {
|
auto end_arr = [&](bool end) {
|
||||||
indent--;
|
indent--;
|
||||||
doindent();
|
doindent();
|
||||||
fout << (end ? "]\n" : "],\n");
|
fout << (end ? "]\n" : "},\n");
|
||||||
};
|
};
|
||||||
|
|
||||||
auto start_obj = [&](const char *name) {
|
auto start_obj = [&](const char *name) {
|
||||||
@ -586,29 +569,12 @@ bool output_json(
|
|||||||
end_value(end);
|
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) {
|
auto value_b = [&](const char *name, const bool val, bool end) {
|
||||||
start_value(name);
|
start_value(name);
|
||||||
fout << (val ? "true" : "false");
|
fout << (val ? "true" : "false");
|
||||||
end_value(end);
|
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!fout.is_open()) {
|
if (!fout.is_open()) {
|
||||||
fprintf(stderr, "%s: failed to open '%s' for writing\n", __func__, fname);
|
fprintf(stderr, "%s: failed to open '%s' for writing\n", __func__, fname);
|
||||||
return false;
|
return false;
|
||||||
@ -654,26 +620,15 @@ bool output_json(
|
|||||||
const int64_t t1 = whisper_full_get_segment_t1(ctx, i);
|
const int64_t t1 = whisper_full_get_segment_t1(ctx, i);
|
||||||
|
|
||||||
start_obj(nullptr);
|
start_obj(nullptr);
|
||||||
times_o(t0, t1, false);
|
start_obj("timestamps");
|
||||||
value_s("text", text, !params.diarize && !params.tinydiarize && !full);
|
value_s("from", to_timestamp(t0, true).c_str(), false);
|
||||||
|
value_s("to", to_timestamp(t1, true).c_str(), true);
|
||||||
if (full) {
|
end_obj(false);
|
||||||
start_arr("tokens");
|
start_obj("offsets");
|
||||||
const int n = whisper_full_n_tokens(ctx, i);
|
value_i("from", t0 * 10, false);
|
||||||
for (int j = 0; j < n; ++j) {
|
value_i("to", t1 * 10, true);
|
||||||
auto token = whisper_full_get_token_data(ctx, i, j);
|
end_obj(false);
|
||||||
start_obj(nullptr);
|
value_s("text", text, !params.diarize && !params.tinydiarize);
|
||||||
value_s("text", whisper_token_to_str(ctx, token.id), false);
|
|
||||||
if(token.t0 > -1 && token.t1 > -1) {
|
|
||||||
// If we have per-token timestamps, write them out
|
|
||||||
times_o(token.t0, token.t1, false);
|
|
||||||
}
|
|
||||||
value_i("id", token.id, false);
|
|
||||||
value_f("p", token.p, true);
|
|
||||||
end_obj(j == (n - 1));
|
|
||||||
}
|
|
||||||
end_arr(!params.diarize && !params.tinydiarize);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params.diarize && pcmf32s.size() == 2) {
|
if (params.diarize && pcmf32s.size() == 2) {
|
||||||
value_s("speaker", estimate_diarization_speaker(pcmf32s, t0, t1, true).c_str(), true);
|
value_s("speaker", estimate_diarization_speaker(pcmf32s, t0, t1, true).c_str(), true);
|
||||||
@ -858,9 +813,6 @@ bool output_lrc(struct whisper_context * ctx, const char * fname, const whisper_
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void cb_log_disable(enum ggml_log_level , const char * , void * ) { }
|
|
||||||
|
|
||||||
int main(int argc, char ** argv) {
|
int main(int argc, char ** argv) {
|
||||||
whisper_params params;
|
whisper_params params;
|
||||||
|
|
||||||
@ -887,16 +839,9 @@ int main(int argc, char ** argv) {
|
|||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.no_prints) {
|
|
||||||
whisper_log_set(cb_log_disable, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// whisper init
|
// whisper init
|
||||||
|
|
||||||
struct whisper_context_params cparams = whisper_context_default_params();
|
struct whisper_context * ctx = whisper_init_from_file(params.model.c_str());
|
||||||
cparams.use_gpu = params.use_gpu;
|
|
||||||
|
|
||||||
struct whisper_context * ctx = whisper_init_from_file_with_params(params.model.c_str(), cparams);
|
|
||||||
|
|
||||||
if (ctx == nullptr) {
|
if (ctx == nullptr) {
|
||||||
fprintf(stderr, "error: failed to initialize whisper context\n");
|
fprintf(stderr, "error: failed to initialize whisper context\n");
|
||||||
@ -918,6 +863,16 @@ int main(int argc, char ** argv) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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");
|
||||||
if (!whisper_is_multilingual(ctx)) {
|
if (!whisper_is_multilingual(ctx)) {
|
||||||
if (params.language != "en" || params.translate) {
|
if (params.language != "en" || params.translate) {
|
||||||
params.language = "en";
|
params.language = "en";
|
||||||
@ -928,18 +883,9 @@ int main(int argc, char ** argv) {
|
|||||||
if (params.detect_language) {
|
if (params.detect_language) {
|
||||||
params.language = "auto";
|
params.language = "auto";
|
||||||
}
|
}
|
||||||
|
fprintf(stderr, "%s: processing '%s' (%d samples, %.1f sec), %d threads, %d processors, lang = %s, task = %s, %stimestamps = %d ...\n",
|
||||||
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,
|
__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.n_threads, params.n_processors,
|
||||||
params.language.c_str(),
|
params.language.c_str(),
|
||||||
params.translate ? "translate" : "transcribe",
|
params.translate ? "translate" : "transcribe",
|
||||||
params.tinydiarize ? "tdrz = 1, " : "",
|
params.tinydiarize ? "tdrz = 1, " : "",
|
||||||
@ -966,11 +912,10 @@ int main(int argc, char ** argv) {
|
|||||||
wparams.offset_ms = params.offset_t_ms;
|
wparams.offset_ms = params.offset_t_ms;
|
||||||
wparams.duration_ms = params.duration_ms;
|
wparams.duration_ms = params.duration_ms;
|
||||||
|
|
||||||
wparams.token_timestamps = params.output_wts || params.output_jsn_full || params.max_len > 0;
|
wparams.token_timestamps = params.output_wts || params.max_len > 0;
|
||||||
wparams.thold_pt = params.word_thold;
|
wparams.thold_pt = params.word_thold;
|
||||||
wparams.max_len = params.output_wts && params.max_len == 0 ? 60 : params.max_len;
|
wparams.max_len = params.output_wts && params.max_len == 0 ? 60 : params.max_len;
|
||||||
wparams.split_on_word = params.split_on_word;
|
wparams.split_on_word = params.split_on_word;
|
||||||
wparams.audio_ctx = params.audio_ctx;
|
|
||||||
|
|
||||||
wparams.speed_up = params.speed_up;
|
wparams.speed_up = params.speed_up;
|
||||||
wparams.debug_mode = params.debug_mode;
|
wparams.debug_mode = params.debug_mode;
|
||||||
@ -986,8 +931,6 @@ int main(int argc, char ** argv) {
|
|||||||
wparams.entropy_thold = params.entropy_thold;
|
wparams.entropy_thold = params.entropy_thold;
|
||||||
wparams.logprob_thold = params.logprob_thold;
|
wparams.logprob_thold = params.logprob_thold;
|
||||||
|
|
||||||
wparams.no_timestamps = params.no_timestamps;
|
|
||||||
|
|
||||||
whisper_print_user_data user_data = { ¶ms, &pcmf32s, 0 };
|
whisper_print_user_data user_data = { ¶ms, &pcmf32s, 0 };
|
||||||
|
|
||||||
// this callback is called on each new segment
|
// this callback is called on each new segment
|
||||||
@ -1001,9 +944,8 @@ int main(int argc, char ** argv) {
|
|||||||
wparams.progress_callback_user_data = &user_data;
|
wparams.progress_callback_user_data = &user_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// examples for abort mechanism
|
// example for abort mechanism
|
||||||
// in examples below, we do not abort the processing, but we could if the flag is set to true
|
// in this example, 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
|
// 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
|
static bool is_aborted = false; // NOTE: this should be atomic to avoid data race
|
||||||
@ -1015,17 +957,6 @@ int main(int argc, char ** argv) {
|
|||||||
wparams.encoder_begin_callback_user_data = &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) {
|
if (whisper_full_parallel(ctx, wparams, pcmf32.data(), pcmf32.size(), params.n_processors) != 0) {
|
||||||
fprintf(stderr, "%s: failed to process audio\n", argv[0]);
|
fprintf(stderr, "%s: failed to process audio\n", argv[0]);
|
||||||
return 10;
|
return 10;
|
||||||
@ -1069,7 +1000,7 @@ int main(int argc, char ** argv) {
|
|||||||
// output to JSON file
|
// output to JSON file
|
||||||
if (params.output_jsn) {
|
if (params.output_jsn) {
|
||||||
const auto fname_jsn = fname_out + ".json";
|
const auto fname_jsn = fname_out + ".json";
|
||||||
output_json(ctx, fname_jsn.c_str(), params, pcmf32s, params.output_jsn_full);
|
output_json(ctx, fname_jsn.c_str(), params, pcmf32s);
|
||||||
}
|
}
|
||||||
|
|
||||||
// output to LRC file
|
// output to LRC file
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
import whisper_processor
|
|
||||||
|
|
||||||
try:
|
|
||||||
result = whisper_processor.process_audio("./audio/wake_word_detected16k.wav", "base.en")
|
|
||||||
print(result)
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error: {e}")
|
|
@ -1,54 +0,0 @@
|
|||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
|
|
||||||
def process_audio(wav_file, model_name="base.en"):
|
|
||||||
"""
|
|
||||||
Processes an audio file using a specified model and returns the processed string.
|
|
||||||
|
|
||||||
:param wav_file: Path to the WAV file
|
|
||||||
:param model_name: Name of the model to use
|
|
||||||
:return: Processed string output from the audio processing
|
|
||||||
:raises: Exception if an error occurs during processing
|
|
||||||
"""
|
|
||||||
|
|
||||||
model = f"./models/ggml-{model_name}.bin"
|
|
||||||
|
|
||||||
# Check if the file exists
|
|
||||||
if not os.path.exists(model):
|
|
||||||
raise FileNotFoundError(f"Model file not found: {model} \n\nDownload a model with this command:\n\n> bash ./models/download-ggml-model.sh {model_name}\n\n")
|
|
||||||
|
|
||||||
if not os.path.exists(wav_file):
|
|
||||||
raise FileNotFoundError(f"WAV file not found: {wav_file}")
|
|
||||||
|
|
||||||
full_command = f"./main -m {model} -f {wav_file} -np -nt"
|
|
||||||
|
|
||||||
# Execute the command
|
|
||||||
process = subprocess.Popen(full_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
||||||
|
|
||||||
# Get the output and error (if any)
|
|
||||||
output, error = process.communicate()
|
|
||||||
|
|
||||||
if error:
|
|
||||||
raise Exception(f"Error processing audio: {error.decode('utf-8')}")
|
|
||||||
|
|
||||||
# Process and return the output string
|
|
||||||
decoded_str = output.decode('utf-8').strip()
|
|
||||||
processed_str = decoded_str.replace('[BLANK_AUDIO]', '').strip()
|
|
||||||
|
|
||||||
return processed_str
|
|
||||||
|
|
||||||
def main():
|
|
||||||
if len(sys.argv) >= 2:
|
|
||||||
wav_file = sys.argv[1]
|
|
||||||
model_name = sys.argv[2] if len(sys.argv) == 3 else "base.en"
|
|
||||||
try:
|
|
||||||
result = process_audio(wav_file, model_name)
|
|
||||||
print(result)
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error: {e}")
|
|
||||||
else:
|
|
||||||
print("Usage: python whisper_processor.py <wav_file> [<model_name>]")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
@ -1,10 +0,0 @@
|
|||||||
set(TARGET server)
|
|
||||||
add_executable(${TARGET} server.cpp httplib.h json.hpp)
|
|
||||||
|
|
||||||
include(DefaultTargetOptions)
|
|
||||||
|
|
||||||
target_link_libraries(${TARGET} PRIVATE common whisper ${CMAKE_THREAD_LIBS_INIT})
|
|
||||||
|
|
||||||
if (WIN32)
|
|
||||||
target_link_libraries(${TARGET} PRIVATE ws2_32)
|
|
||||||
endif()
|
|
@ -1,69 +0,0 @@
|
|||||||
# whisper.cpp http server
|
|
||||||
|
|
||||||
Simple http server. WAV Files are passed to the inference model via http requests.
|
|
||||||
|
|
||||||
https://github.com/ggerganov/whisper.cpp/assets/1991296/e983ee53-8741-4eb5-9048-afe5e4594b8f
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```
|
|
||||||
./server -h
|
|
||||||
|
|
||||||
usage: ./bin/server [options]
|
|
||||||
|
|
||||||
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 [2 ] number of best candidates to keep
|
|
||||||
-bs N, --beam-size N [-1 ] beam size for beam search
|
|
||||||
-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
|
|
||||||
-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
|
|
||||||
-ps, --print-special [false ] print special tokens
|
|
||||||
-pc, --print-colors [false ] print colors
|
|
||||||
-pr, --print-realtime [false ] print output in realtime
|
|
||||||
-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
|
|
||||||
-m FNAME, --model FNAME [models/ggml-base.en.bin] model path
|
|
||||||
-oved D, --ov-e-device DNAME [CPU ] the OpenVINO device used for encode inference
|
|
||||||
--host HOST, [127.0.0.1] Hostname/ip-adress for the server
|
|
||||||
--port PORT, [8080 ] Port number for the server
|
|
||||||
--convert, [false ] Convert audio to WAV, requires ffmpeg on the server
|
|
||||||
```
|
|
||||||
|
|
||||||
> [!WARNING]
|
|
||||||
> **Do not run the server example with administrative privileges and ensure it's operated in a sandbox environment, especially since it involves risky operations like accepting user file uploads and using ffmpeg for format conversions. Always validate and sanitize inputs to guard against potential security threats.**
|
|
||||||
|
|
||||||
## request examples
|
|
||||||
|
|
||||||
**/inference**
|
|
||||||
```
|
|
||||||
curl 127.0.0.1:8080/inference \
|
|
||||||
-H "Content-Type: multipart/form-data" \
|
|
||||||
-F file="@<file-path>" \
|
|
||||||
-F temperature="0.0" \
|
|
||||||
-F temperature_inc="0.2" \
|
|
||||||
-F response_format="json"
|
|
||||||
```
|
|
||||||
|
|
||||||
**/load**
|
|
||||||
```
|
|
||||||
curl 127.0.0.1:8080/load \
|
|
||||||
-H "Content-Type: multipart/form-data" \
|
|
||||||
-F model="<path-to-model-file>"
|
|
||||||
```
|
|
File diff suppressed because it is too large
Load Diff
24596
examples/server/json.hpp
24596
examples/server/json.hpp
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -132,7 +132,7 @@ EMSCRIPTEN_BINDINGS(stream) {
|
|||||||
emscripten::function("init", emscripten::optional_override([](const std::string & path_model) {
|
emscripten::function("init", emscripten::optional_override([](const std::string & path_model) {
|
||||||
for (size_t i = 0; i < g_contexts.size(); ++i) {
|
for (size_t i = 0; i < g_contexts.size(); ++i) {
|
||||||
if (g_contexts[i] == nullptr) {
|
if (g_contexts[i] == nullptr) {
|
||||||
g_contexts[i] = whisper_init_from_file_with_params(path_model.c_str(), whisper_context_default_params());
|
g_contexts[i] = whisper_init_from_file(path_model.c_str());
|
||||||
if (g_contexts[i] != nullptr) {
|
if (g_contexts[i] != nullptr) {
|
||||||
g_running = true;
|
g_running = true;
|
||||||
if (g_worker.joinable()) {
|
if (g_worker.joinable()) {
|
||||||
|
@ -4,7 +4,7 @@ This is a naive example of performing real-time inference on audio from your mic
|
|||||||
The `stream` tool samples the audio every half a second and runs the transcription continously.
|
The `stream` tool samples the audio every half a second and runs the transcription continously.
|
||||||
More info is available in [issue #10](https://github.com/ggerganov/whisper.cpp/issues/10).
|
More info is available in [issue #10](https://github.com/ggerganov/whisper.cpp/issues/10).
|
||||||
|
|
||||||
```bash
|
```java
|
||||||
./stream -m ./models/ggml-base.en.bin -t 8 --step 500 --length 5000
|
./stream -m ./models/ggml-base.en.bin -t 8 --step 500 --length 5000
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ https://user-images.githubusercontent.com/1991296/194935793-76afede7-cfa8-48d8-a
|
|||||||
|
|
||||||
Setting the `--step` argument to `0` enables the sliding window mode:
|
Setting the `--step` argument to `0` enables the sliding window mode:
|
||||||
|
|
||||||
```bash
|
```java
|
||||||
./stream -m ./models/ggml-small.en.bin -t 6 --step 0 --length 30000 -vth 0.6
|
./stream -m ./models/ggml-small.en.bin -t 6 --step 0 --length 30000 -vth 0.6
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -39,20 +39,6 @@ brew install sdl2
|
|||||||
make stream
|
make stream
|
||||||
```
|
```
|
||||||
|
|
||||||
Ensure you are at the root of the repo when running `make stream`. Not within the `examples/stream` dir
|
|
||||||
as the libraries needed like `common-sdl.h` are located within `examples`. Attempting to compile within
|
|
||||||
`examples/steam` means your compiler cannot find them and it gives an error it cannot find the file.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
whisper.cpp/examples/stream$ make stream
|
|
||||||
g++ stream.cpp -o stream
|
|
||||||
stream.cpp:6:10: fatal error: common/sdl.h: No such file or directory
|
|
||||||
6 | #include "common/sdl.h"
|
|
||||||
| ^~~~~~~~~~~~~~
|
|
||||||
compilation terminated.
|
|
||||||
make: *** [<builtin>: stream] Error 1
|
|
||||||
```
|
|
||||||
|
|
||||||
## Web version
|
## Web version
|
||||||
|
|
||||||
This tool can also run in the browser: [examples/stream.wasm](/examples/stream.wasm)
|
This tool can also run in the browser: [examples/stream.wasm](/examples/stream.wasm)
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
//
|
//
|
||||||
// A very quick-n-dirty implementation serving mainly as a proof of concept.
|
// A very quick-n-dirty implementation serving mainly as a proof of concept.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "common-sdl.h"
|
#include "common-sdl.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "whisper.h"
|
#include "whisper.h"
|
||||||
@ -13,7 +14,6 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
|
|
||||||
// 500 -> 00:05.000
|
// 500 -> 00:05.000
|
||||||
// 6000 -> 01:00.000
|
// 6000 -> 01:00.000
|
||||||
std::string to_timestamp(int64_t t) {
|
std::string to_timestamp(int64_t t) {
|
||||||
@ -48,8 +48,6 @@ struct whisper_params {
|
|||||||
bool no_context = true;
|
bool no_context = true;
|
||||||
bool no_timestamps = false;
|
bool no_timestamps = false;
|
||||||
bool tinydiarize = false;
|
bool tinydiarize = false;
|
||||||
bool save_audio = false; // save audio to wav file
|
|
||||||
bool use_gpu = true;
|
|
||||||
|
|
||||||
std::string language = "en";
|
std::string language = "en";
|
||||||
std::string model = "models/ggml-base.en.bin";
|
std::string model = "models/ggml-base.en.bin";
|
||||||
@ -84,8 +82,6 @@ bool whisper_params_parse(int argc, char ** argv, whisper_params & params) {
|
|||||||
else if (arg == "-m" || arg == "--model") { params.model = 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 == "-f" || arg == "--file") { params.fname_out = argv[++i]; }
|
||||||
else if (arg == "-tdrz" || arg == "--tinydiarize") { params.tinydiarize = true; }
|
else if (arg == "-tdrz" || arg == "--tinydiarize") { params.tinydiarize = true; }
|
||||||
else if (arg == "-sa" || arg == "--save-audio") { params.save_audio = true; }
|
|
||||||
else if (arg == "-ng" || arg == "--no-gpu") { params.use_gpu = false; }
|
|
||||||
|
|
||||||
else {
|
else {
|
||||||
fprintf(stderr, "error: unknown argument: %s\n", arg.c_str());
|
fprintf(stderr, "error: unknown argument: %s\n", arg.c_str());
|
||||||
@ -121,8 +117,6 @@ void whisper_print_usage(int /*argc*/, char ** argv, const whisper_params & para
|
|||||||
fprintf(stderr, " -m FNAME, --model FNAME [%-7s] model path\n", params.model.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, " -f FNAME, --file FNAME [%-7s] text output file name\n", params.fname_out.c_str());
|
||||||
fprintf(stderr, " -tdrz, --tinydiarize [%-7s] enable tinydiarize (requires a tdrz model)\n", params.tinydiarize ? "true" : "false");
|
fprintf(stderr, " -tdrz, --tinydiarize [%-7s] enable tinydiarize (requires a tdrz model)\n", params.tinydiarize ? "true" : "false");
|
||||||
fprintf(stderr, " -sa, --save-audio [%-7s] save the recorded audio to a file\n", params.save_audio ? "true" : "false");
|
|
||||||
fprintf(stderr, " -ng, --no-gpu [%-7s] disable GPU inference\n", params.use_gpu ? "false" : "true");
|
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,16 +154,14 @@ int main(int argc, char ** argv) {
|
|||||||
audio.resume();
|
audio.resume();
|
||||||
|
|
||||||
// whisper init
|
// whisper init
|
||||||
|
|
||||||
if (params.language != "auto" && whisper_lang_id(params.language.c_str()) == -1){
|
if (params.language != "auto" && whisper_lang_id(params.language.c_str()) == -1){
|
||||||
fprintf(stderr, "error: unknown language '%s'\n", params.language.c_str());
|
fprintf(stderr, "error: unknown language '%s'\n", params.language.c_str());
|
||||||
whisper_print_usage(argc, argv, params);
|
whisper_print_usage(argc, argv, params);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct whisper_context_params cparams = whisper_context_default_params();
|
struct whisper_context * ctx = whisper_init_from_file(params.model.c_str());
|
||||||
cparams.use_gpu = params.use_gpu;
|
|
||||||
|
|
||||||
struct whisper_context * ctx = whisper_init_from_file_with_params(params.model.c_str(), cparams);
|
|
||||||
|
|
||||||
std::vector<float> pcmf32 (n_samples_30s, 0.0f);
|
std::vector<float> pcmf32 (n_samples_30s, 0.0f);
|
||||||
std::vector<float> pcmf32_old;
|
std::vector<float> pcmf32_old;
|
||||||
@ -220,18 +212,7 @@ int main(int argc, char ** argv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wav_writer wavWriter;
|
printf("[Start speaking]");
|
||||||
// save wav file
|
|
||||||
if (params.save_audio) {
|
|
||||||
// Get current date/time for filename
|
|
||||||
time_t now = time(0);
|
|
||||||
char buffer[80];
|
|
||||||
strftime(buffer, sizeof(buffer), "%Y%m%d%H%M%S", localtime(&now));
|
|
||||||
std::string filename = std::string(buffer) + ".wav";
|
|
||||||
|
|
||||||
wavWriter.open(filename, WHISPER_SAMPLE_RATE, 16, 1);
|
|
||||||
}
|
|
||||||
printf("[Start speaking]\n");
|
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
|
||||||
auto t_last = std::chrono::high_resolution_clock::now();
|
auto t_last = std::chrono::high_resolution_clock::now();
|
||||||
@ -239,9 +220,6 @@ int main(int argc, char ** argv) {
|
|||||||
|
|
||||||
// main audio loop
|
// main audio loop
|
||||||
while (is_running) {
|
while (is_running) {
|
||||||
if (params.save_audio) {
|
|
||||||
wavWriter.write(pcmf32_new.data(), pcmf32_new.size());
|
|
||||||
}
|
|
||||||
// handle Ctrl + C
|
// handle Ctrl + C
|
||||||
is_running = sdl_poll_events();
|
is_running = sdl_poll_events();
|
||||||
|
|
||||||
@ -393,7 +371,7 @@ int main(int argc, char ** argv) {
|
|||||||
fout << std::endl;
|
fout << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (use_vad) {
|
if (use_vad){
|
||||||
printf("\n");
|
printf("\n");
|
||||||
printf("### Transcription %d END\n", n_iter);
|
printf("### Transcription %d END\n", n_iter);
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,16 @@
|
|||||||
if (WHISPER_SDL2)
|
if (WHISPER_SDL2)
|
||||||
# talk-llama
|
# talk-llama
|
||||||
set(TARGET talk-llama)
|
set(TARGET talk-llama)
|
||||||
add_executable(${TARGET} talk-llama.cpp llama.cpp)
|
#add_executable(${TARGET} talk-llama.cpp llama.cpp)
|
||||||
target_include_directories(${TARGET} PRIVATE ${SDL2_INCLUDE_DIRS})
|
#target_include_directories(${TARGET} PRIVATE ${SDL2_INCLUDE_DIRS})
|
||||||
|
#target_link_libraries(${TARGET} PRIVATE common common-sdl whisper ${SDL2_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
||||||
|
|
||||||
if (WHISPER_CLBLAST)
|
# TODO: this is temporary
|
||||||
set(CLBLAST_LIBNAME clblast)
|
# need to export ggml symbols for MSVC, but too lazy ..
|
||||||
endif ()
|
add_executable(${TARGET} talk-llama.cpp llama.cpp ../common.cpp ../common-sdl.cpp ../../ggml.c ../../whisper.cpp)
|
||||||
target_link_libraries(${TARGET} PRIVATE common common-sdl whisper ${SDL2_LIBRARIES} ${CLBLAST_LIBNAME} ${CMAKE_THREAD_LIBS_INIT})
|
|
||||||
|
|
||||||
if(WIN32)
|
target_include_directories(${TARGET} PRIVATE ${SDL2_INCLUDE_DIRS} ../../)
|
||||||
# It requires Windows 8.1 or later for PrefetchVirtualMemory
|
target_link_libraries(${TARGET} PRIVATE ${SDL2_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
||||||
target_compile_definitions(${TARGET} PRIVATE -D_WIN32_WINNT=0x0602)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
include(DefaultTargetOptions)
|
include(DefaultTargetOptions)
|
||||||
endif ()
|
endif ()
|
||||||
|
@ -2,12 +2,6 @@
|
|||||||
|
|
||||||
Talk with an LLaMA AI in your terminal
|
Talk with an LLaMA AI in your terminal
|
||||||
|
|
||||||
*Latest perf as of 2 Nov 2023 using Whisper Medium + LLaMA v2 13B Q8_0 on M2 Ultra:*
|
|
||||||
|
|
||||||
https://github.com/ggerganov/whisper.cpp/assets/1991296/d97a3788-bf2a-4756-9a43-60c6b391649e
|
|
||||||
|
|
||||||
*Previous demo running on CPUs*
|
|
||||||
|
|
||||||
[Demo Talk](https://user-images.githubusercontent.com/1991296/228024237-848f998c-c334-46a6-bef8-3271590da83b.mp4)
|
[Demo Talk](https://user-images.githubusercontent.com/1991296/228024237-848f998c-c334-46a6-bef8-3271590da83b.mp4)
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
@ -25,7 +19,7 @@ brew install sdl2
|
|||||||
make talk-llama
|
make talk-llama
|
||||||
|
|
||||||
# Run it
|
# Run it
|
||||||
./talk-llama -mw ./models/ggml-small.en.bin -ml ../llama.cpp/models/llama-13b/ggml-model-q4_0.gguf -p "Georgi" -t 8
|
./talk-llama -mw ./models/ggml-small.en.bin -ml ../llama.cpp/models/13B/ggml-model-q4_0.bin -p "Georgi" -t 8
|
||||||
```
|
```
|
||||||
|
|
||||||
- The `-mw` argument specifies the Whisper model that you would like to use. Recommended `base` or `small` for real-time experience
|
- The `-mw` argument specifies the Whisper model that you would like to use. Recommended `base` or `small` for real-time experience
|
||||||
@ -42,7 +36,7 @@ This feature is especially helpful for maintaining context in long conversations
|
|||||||
Example usage:
|
Example usage:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./talk-llama --session ./my-session-file -mw ./models/ggml-small.en.bin -ml ../llama.cpp/models/llama-13b/ggml-model-q4_0.gguf -p "Georgi" -t 8
|
./talk-llama --session ./my-session-file -mw ./models/ggml-small.en.bin -ml ../llama.cpp/models/13B/ggml-model-q4_0.bin -p "Georgi" -t 8
|
||||||
```
|
```
|
||||||
|
|
||||||
## TTS
|
## TTS
|
||||||
|
474
examples/talk-llama/llama-util.h
Normal file
474
examples/talk-llama/llama-util.h
Normal file
@ -0,0 +1,474 @@
|
|||||||
|
// Internal header to be included only by llama.cpp.
|
||||||
|
// Contains wrappers around OS interfaces.
|
||||||
|
|
||||||
|
#ifndef LLAMA_UTIL_H
|
||||||
|
#define LLAMA_UTIL_H
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cerrno>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdarg>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <climits>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#ifdef __has_include
|
||||||
|
#if __has_include(<unistd.h>)
|
||||||
|
#include <unistd.h>
|
||||||
|
#if defined(_POSIX_MAPPED_FILES)
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#endif
|
||||||
|
#if defined(_POSIX_MEMLOCK_RANGE)
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX
|
||||||
|
#endif
|
||||||
|
#include <windows.h>
|
||||||
|
#include <io.h>
|
||||||
|
#include <stdio.h> // for _fseeki64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define LLAMA_ASSERT(x) \
|
||||||
|
do { \
|
||||||
|
if (!(x)) { \
|
||||||
|
fprintf(stderr, "LLAMA_ASSERT: %s:%d: %s\n", __FILE__, __LINE__, #x); \
|
||||||
|
abort(); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
__attribute__((format(gnu_printf, 1, 2)))
|
||||||
|
#else
|
||||||
|
__attribute__((format(printf, 1, 2)))
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
static std::string format(const char * fmt, ...) {
|
||||||
|
va_list ap, ap2;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
va_copy(ap2, ap);
|
||||||
|
int size = vsnprintf(NULL, 0, fmt, ap);
|
||||||
|
LLAMA_ASSERT(size >= 0 && size < INT_MAX);
|
||||||
|
std::vector<char> buf(size + 1);
|
||||||
|
int size2 = vsnprintf(buf.data(), size + 1, fmt, ap2);
|
||||||
|
LLAMA_ASSERT(size2 == size);
|
||||||
|
va_end(ap2);
|
||||||
|
va_end(ap);
|
||||||
|
return std::string(buf.data(), size);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct llama_file {
|
||||||
|
// use FILE * so we don't have to re-open the file to mmap
|
||||||
|
FILE * fp;
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
llama_file(const char * fname, const char * mode) {
|
||||||
|
fp = std::fopen(fname, mode);
|
||||||
|
if (fp == NULL) {
|
||||||
|
throw std::runtime_error(format("failed to open %s: %s", fname, strerror(errno)));
|
||||||
|
}
|
||||||
|
seek(0, SEEK_END);
|
||||||
|
size = tell();
|
||||||
|
seek(0, SEEK_SET);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t tell() const {
|
||||||
|
#ifdef _WIN32
|
||||||
|
__int64 ret = _ftelli64(fp);
|
||||||
|
#else
|
||||||
|
long ret = std::ftell(fp);
|
||||||
|
#endif
|
||||||
|
LLAMA_ASSERT(ret != -1); // this really shouldn't fail
|
||||||
|
return (size_t) ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void seek(size_t offset, int whence) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
int ret = _fseeki64(fp, (__int64) offset, whence);
|
||||||
|
#else
|
||||||
|
int ret = std::fseek(fp, (long) offset, whence);
|
||||||
|
#endif
|
||||||
|
LLAMA_ASSERT(ret == 0); // same
|
||||||
|
}
|
||||||
|
|
||||||
|
void read_raw(void * ptr, size_t len) const {
|
||||||
|
if (len == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
errno = 0;
|
||||||
|
std::size_t ret = std::fread(ptr, len, 1, fp);
|
||||||
|
if (ferror(fp)) {
|
||||||
|
throw std::runtime_error(format("read error: %s", strerror(errno)));
|
||||||
|
}
|
||||||
|
if (ret != 1) {
|
||||||
|
throw std::runtime_error(std::string("unexpectedly reached end of file"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t read_u32() {
|
||||||
|
std::uint32_t ret;
|
||||||
|
read_raw(&ret, sizeof(ret));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string read_string(std::uint32_t len) {
|
||||||
|
std::vector<char> chars(len);
|
||||||
|
read_raw(chars.data(), len);
|
||||||
|
return std::string(chars.data(), len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_raw(const void * ptr, size_t len) const {
|
||||||
|
if (len == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
errno = 0;
|
||||||
|
size_t ret = std::fwrite(ptr, len, 1, fp);
|
||||||
|
if (ret != 1) {
|
||||||
|
throw std::runtime_error(format("write error: %s", strerror(errno)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_u32(std::uint32_t val) {
|
||||||
|
write_raw(&val, sizeof(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
~llama_file() {
|
||||||
|
if (fp) {
|
||||||
|
std::fclose(fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
static std::string llama_format_win_err(DWORD err) {
|
||||||
|
LPSTR buf;
|
||||||
|
size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
|
NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&buf, 0, NULL);
|
||||||
|
if (!size) {
|
||||||
|
return "FormatMessageA failed";
|
||||||
|
}
|
||||||
|
std::string ret(buf, size);
|
||||||
|
LocalFree(buf);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct llama_mmap {
|
||||||
|
void * addr;
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
llama_mmap(const llama_mmap &) = delete;
|
||||||
|
|
||||||
|
#ifdef _POSIX_MAPPED_FILES
|
||||||
|
static constexpr bool SUPPORTED = true;
|
||||||
|
|
||||||
|
llama_mmap(struct llama_file * file, size_t prefetch = (size_t) -1 /* -1 = max value */) {
|
||||||
|
size = file->size;
|
||||||
|
int fd = fileno(file->fp);
|
||||||
|
int flags = MAP_SHARED;
|
||||||
|
#ifdef __linux__
|
||||||
|
flags |= MAP_POPULATE;
|
||||||
|
#endif
|
||||||
|
addr = mmap(NULL, file->size, PROT_READ, flags, fd, 0);
|
||||||
|
if (addr == MAP_FAILED) {
|
||||||
|
throw std::runtime_error(format("mmap failed: %s", strerror(errno)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prefetch > 0) {
|
||||||
|
// Advise the kernel to preload the mapped memory
|
||||||
|
if (posix_madvise(addr, std::min(file->size, prefetch), POSIX_MADV_WILLNEED)) {
|
||||||
|
fprintf(stderr, "warning: posix_madvise(.., POSIX_MADV_WILLNEED) failed: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~llama_mmap() {
|
||||||
|
munmap(addr, size);
|
||||||
|
}
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
static constexpr bool SUPPORTED = true;
|
||||||
|
|
||||||
|
llama_mmap(struct llama_file * file, bool prefetch = true) {
|
||||||
|
size = file->size;
|
||||||
|
|
||||||
|
HANDLE hFile = (HANDLE) _get_osfhandle(_fileno(file->fp));
|
||||||
|
|
||||||
|
HANDLE hMapping = CreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
||||||
|
DWORD error = GetLastError();
|
||||||
|
|
||||||
|
if (hMapping == NULL) {
|
||||||
|
throw std::runtime_error(format("CreateFileMappingA failed: %s", llama_format_win_err(error).c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
|
||||||
|
error = GetLastError();
|
||||||
|
CloseHandle(hMapping);
|
||||||
|
|
||||||
|
if (addr == NULL) {
|
||||||
|
throw std::runtime_error(format("MapViewOfFile failed: %s", llama_format_win_err(error).c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if _WIN32_WINNT >= _WIN32_WINNT_WIN8
|
||||||
|
if (prefetch) {
|
||||||
|
// Advise the kernel to preload the mapped memory
|
||||||
|
WIN32_MEMORY_RANGE_ENTRY range;
|
||||||
|
range.VirtualAddress = addr;
|
||||||
|
range.NumberOfBytes = (SIZE_T)size;
|
||||||
|
if (!PrefetchVirtualMemory(GetCurrentProcess(), 1, &range, 0)) {
|
||||||
|
fprintf(stderr, "warning: PrefetchVirtualMemory failed: %s\n",
|
||||||
|
llama_format_win_err(GetLastError()).c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#pragma message("warning: You are building for pre-Windows 8; prefetch not supported")
|
||||||
|
#endif // _WIN32_WINNT >= _WIN32_WINNT_WIN8
|
||||||
|
}
|
||||||
|
|
||||||
|
~llama_mmap() {
|
||||||
|
if (!UnmapViewOfFile(addr)) {
|
||||||
|
fprintf(stderr, "warning: UnmapViewOfFile failed: %s\n",
|
||||||
|
llama_format_win_err(GetLastError()).c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static constexpr bool SUPPORTED = false;
|
||||||
|
|
||||||
|
llama_mmap(struct llama_file *, bool prefetch = true) {
|
||||||
|
(void)prefetch;
|
||||||
|
throw std::runtime_error(std::string("mmap not supported"));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
// Represents some region of memory being locked using mlock or VirtualLock;
|
||||||
|
// will automatically unlock on destruction.
|
||||||
|
struct llama_mlock {
|
||||||
|
void * addr = NULL;
|
||||||
|
size_t size = 0;
|
||||||
|
bool failed_already = false;
|
||||||
|
|
||||||
|
llama_mlock() {}
|
||||||
|
llama_mlock(const llama_mlock &) = delete;
|
||||||
|
|
||||||
|
~llama_mlock() {
|
||||||
|
if (size) {
|
||||||
|
raw_unlock(addr, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(void * ptr) {
|
||||||
|
LLAMA_ASSERT(addr == NULL && size == 0);
|
||||||
|
addr = ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void grow_to(size_t target_size) {
|
||||||
|
LLAMA_ASSERT(addr);
|
||||||
|
if (failed_already) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
size_t granularity = lock_granularity();
|
||||||
|
target_size = (target_size + granularity - 1) & ~(granularity - 1);
|
||||||
|
if (target_size > size) {
|
||||||
|
if (raw_lock((uint8_t *) addr + size, target_size - size)) {
|
||||||
|
size = target_size;
|
||||||
|
} else {
|
||||||
|
failed_already = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _POSIX_MEMLOCK_RANGE
|
||||||
|
static constexpr bool SUPPORTED = true;
|
||||||
|
|
||||||
|
size_t lock_granularity() {
|
||||||
|
return (size_t) sysconf(_SC_PAGESIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#define MLOCK_SUGGESTION \
|
||||||
|
"Try increasing the sysctl values 'vm.user_wire_limit' and 'vm.global_user_wire_limit' and/or " \
|
||||||
|
"decreasing 'vm.global_no_user_wire_amount'. Also try increasing RLIMIT_MLOCK (ulimit -l).\n"
|
||||||
|
#else
|
||||||
|
#define MLOCK_SUGGESTION \
|
||||||
|
"Try increasing RLIMIT_MLOCK ('ulimit -l' as root).\n"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool raw_lock(const void * addr, size_t size) {
|
||||||
|
if (!mlock(addr, size)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
char* errmsg = std::strerror(errno);
|
||||||
|
bool suggest = (errno == ENOMEM);
|
||||||
|
|
||||||
|
// Check if the resource limit is fine after all
|
||||||
|
struct rlimit lock_limit;
|
||||||
|
if (suggest && getrlimit(RLIMIT_MEMLOCK, &lock_limit))
|
||||||
|
suggest = false;
|
||||||
|
if (suggest && (lock_limit.rlim_max > lock_limit.rlim_cur + size))
|
||||||
|
suggest = false;
|
||||||
|
|
||||||
|
fprintf(stderr, "warning: failed to mlock %zu-byte buffer (after previously locking %zu bytes): %s\n%s",
|
||||||
|
size, this->size, errmsg, suggest ? MLOCK_SUGGESTION : "");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef MLOCK_SUGGESTION
|
||||||
|
|
||||||
|
void raw_unlock(void * addr, size_t size) {
|
||||||
|
if (munlock(addr, size)) {
|
||||||
|
fprintf(stderr, "warning: failed to munlock buffer: %s\n", std::strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
static constexpr bool SUPPORTED = true;
|
||||||
|
|
||||||
|
size_t lock_granularity() {
|
||||||
|
SYSTEM_INFO si;
|
||||||
|
GetSystemInfo(&si);
|
||||||
|
return (size_t) si.dwPageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool raw_lock(void * ptr, size_t len) {
|
||||||
|
for (int tries = 1; ; tries++) {
|
||||||
|
if (VirtualLock(ptr, len)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (tries == 2) {
|
||||||
|
fprintf(stderr, "warning: failed to VirtualLock %zu-byte buffer (after previously locking %zu bytes): %s\n",
|
||||||
|
len, size, llama_format_win_err(GetLastError()).c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// It failed but this was only the first try; increase the working
|
||||||
|
// set size and try again.
|
||||||
|
SIZE_T min_ws_size, max_ws_size;
|
||||||
|
if (!GetProcessWorkingSetSize(GetCurrentProcess(), &min_ws_size, &max_ws_size)) {
|
||||||
|
fprintf(stderr, "warning: GetProcessWorkingSetSize failed: %s\n",
|
||||||
|
llama_format_win_err(GetLastError()).c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Per MSDN: "The maximum number of pages that a process can lock
|
||||||
|
// is equal to the number of pages in its minimum working set minus
|
||||||
|
// a small overhead."
|
||||||
|
// Hopefully a megabyte is enough overhead:
|
||||||
|
size_t increment = len + 1048576;
|
||||||
|
// The minimum must be <= the maximum, so we need to increase both:
|
||||||
|
min_ws_size += increment;
|
||||||
|
max_ws_size += increment;
|
||||||
|
if (!SetProcessWorkingSetSize(GetCurrentProcess(), min_ws_size, max_ws_size)) {
|
||||||
|
fprintf(stderr, "warning: SetProcessWorkingSetSize failed: %s\n",
|
||||||
|
llama_format_win_err(GetLastError()).c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void raw_unlock(void * ptr, size_t len) {
|
||||||
|
if (!VirtualUnlock(ptr, len)) {
|
||||||
|
fprintf(stderr, "warning: failed to VirtualUnlock buffer: %s\n",
|
||||||
|
llama_format_win_err(GetLastError()).c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static constexpr bool SUPPORTED = false;
|
||||||
|
|
||||||
|
size_t lock_granularity() {
|
||||||
|
return (size_t) 65536;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool raw_lock(const void * addr, size_t len) {
|
||||||
|
fprintf(stderr, "warning: mlock not supported on this system\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void raw_unlock(const void * addr, size_t len) {}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
// Replacement for std::vector<uint8_t> that doesn't require zero-initialization.
|
||||||
|
struct llama_buffer {
|
||||||
|
uint8_t * addr = NULL;
|
||||||
|
size_t size = 0;
|
||||||
|
|
||||||
|
llama_buffer() = default;
|
||||||
|
|
||||||
|
void resize(size_t len) {
|
||||||
|
delete[] addr;
|
||||||
|
addr = new uint8_t[len];
|
||||||
|
size = len;
|
||||||
|
}
|
||||||
|
|
||||||
|
~llama_buffer() {
|
||||||
|
delete[] addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// disable copy and move
|
||||||
|
llama_buffer(const llama_buffer&) = delete;
|
||||||
|
llama_buffer(llama_buffer&&) = delete;
|
||||||
|
llama_buffer& operator=(const llama_buffer&) = delete;
|
||||||
|
llama_buffer& operator=(llama_buffer&&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef GGML_USE_CUBLAS
|
||||||
|
#include "ggml-cuda.h"
|
||||||
|
struct llama_ctx_buffer {
|
||||||
|
uint8_t * addr = NULL;
|
||||||
|
bool is_cuda;
|
||||||
|
size_t size = 0;
|
||||||
|
|
||||||
|
llama_ctx_buffer() = default;
|
||||||
|
|
||||||
|
void resize(size_t size) {
|
||||||
|
free();
|
||||||
|
|
||||||
|
addr = (uint8_t *) ggml_cuda_host_malloc(size);
|
||||||
|
if (addr) {
|
||||||
|
is_cuda = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// fall back to pageable memory
|
||||||
|
addr = new uint8_t[size];
|
||||||
|
is_cuda = false;
|
||||||
|
}
|
||||||
|
this->size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free() {
|
||||||
|
if (addr) {
|
||||||
|
if (is_cuda) {
|
||||||
|
ggml_cuda_host_free(addr);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
delete[] addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
~llama_ctx_buffer() {
|
||||||
|
free();
|
||||||
|
}
|
||||||
|
|
||||||
|
// disable copy and move
|
||||||
|
llama_ctx_buffer(const llama_ctx_buffer&) = delete;
|
||||||
|
llama_ctx_buffer(llama_ctx_buffer&&) = delete;
|
||||||
|
llama_ctx_buffer& operator=(const llama_ctx_buffer&) = delete;
|
||||||
|
llama_ctx_buffer& operator=(llama_ctx_buffer&&) = delete;
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
typedef llama_buffer llama_ctx_buffer;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
File diff suppressed because it is too large
Load Diff
@ -1,12 +1,8 @@
|
|||||||
#ifndef LLAMA_H
|
#ifndef LLAMA_H
|
||||||
#define LLAMA_H
|
#define LLAMA_H
|
||||||
|
|
||||||
#include "ggml.h"
|
|
||||||
#include "ggml-backend.h"
|
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#ifdef LLAMA_SHARED
|
#ifdef LLAMA_SHARED
|
||||||
@ -23,23 +19,17 @@
|
|||||||
# define LLAMA_API
|
# define LLAMA_API
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __GNUC__
|
#define LLAMA_FILE_MAGIC_GGJT 0x67676a74u // 'ggjt'
|
||||||
# define DEPRECATED(func, hint) func __attribute__((deprecated(hint)))
|
|
||||||
#elif defined(_MSC_VER)
|
|
||||||
# define DEPRECATED(func, hint) __declspec(deprecated(hint)) func
|
|
||||||
#else
|
|
||||||
# define DEPRECATED(func, hint) func
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define LLAMA_DEFAULT_SEED 0xFFFFFFFF
|
|
||||||
|
|
||||||
#define LLAMA_MAX_RNG_STATE (64*1024)
|
|
||||||
|
|
||||||
#define LLAMA_FILE_MAGIC_GGLA 0x67676c61u // 'ggla'
|
#define LLAMA_FILE_MAGIC_GGLA 0x67676c61u // 'ggla'
|
||||||
|
#define LLAMA_FILE_MAGIC_GGMF 0x67676d66u // 'ggmf'
|
||||||
|
#define LLAMA_FILE_MAGIC_GGML 0x67676d6cu // 'ggml'
|
||||||
#define LLAMA_FILE_MAGIC_GGSN 0x6767736eu // 'ggsn'
|
#define LLAMA_FILE_MAGIC_GGSN 0x6767736eu // 'ggsn'
|
||||||
|
|
||||||
|
#define LLAMA_FILE_VERSION 3
|
||||||
|
#define LLAMA_FILE_MAGIC LLAMA_FILE_MAGIC_GGJT
|
||||||
|
#define LLAMA_FILE_MAGIC_UNVERSIONED LLAMA_FILE_MAGIC_GGML
|
||||||
#define LLAMA_SESSION_MAGIC LLAMA_FILE_MAGIC_GGSN
|
#define LLAMA_SESSION_MAGIC LLAMA_FILE_MAGIC_GGSN
|
||||||
#define LLAMA_SESSION_VERSION 4
|
#define LLAMA_SESSION_VERSION 1
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -51,27 +41,40 @@ extern "C" {
|
|||||||
// TODO: show sample usage
|
// TODO: show sample usage
|
||||||
//
|
//
|
||||||
|
|
||||||
struct llama_model;
|
|
||||||
struct llama_context;
|
struct llama_context;
|
||||||
|
|
||||||
typedef int32_t llama_pos;
|
typedef int llama_token;
|
||||||
typedef int32_t llama_token;
|
|
||||||
typedef int32_t llama_seq_id;
|
|
||||||
|
|
||||||
enum llama_vocab_type {
|
typedef struct llama_token_data {
|
||||||
LLAMA_VOCAB_TYPE_SPM = 0, // SentencePiece
|
llama_token id; // token id
|
||||||
LLAMA_VOCAB_TYPE_BPE = 1, // Byte Pair Encoding
|
float logit; // log-odds of the token
|
||||||
LLAMA_VOCAB_TYPE_WPM = 2, // WordPiece
|
float p; // probability of the token
|
||||||
};
|
} llama_token_data;
|
||||||
|
|
||||||
enum llama_token_type {
|
typedef struct llama_token_data_array {
|
||||||
LLAMA_TOKEN_TYPE_UNDEFINED = 0,
|
llama_token_data * data;
|
||||||
LLAMA_TOKEN_TYPE_NORMAL = 1,
|
size_t size;
|
||||||
LLAMA_TOKEN_TYPE_UNKNOWN = 2,
|
bool sorted;
|
||||||
LLAMA_TOKEN_TYPE_CONTROL = 3,
|
} llama_token_data_array;
|
||||||
LLAMA_TOKEN_TYPE_USER_DEFINED = 4,
|
|
||||||
LLAMA_TOKEN_TYPE_UNUSED = 5,
|
typedef void (*llama_progress_callback)(float progress, void *ctx);
|
||||||
LLAMA_TOKEN_TYPE_BYTE = 6,
|
|
||||||
|
struct llama_context_params {
|
||||||
|
int n_ctx; // text context
|
||||||
|
int n_gpu_layers; // number of layers to store in VRAM
|
||||||
|
int seed; // RNG seed, -1 for random
|
||||||
|
|
||||||
|
bool f16_kv; // use fp16 for KV cache
|
||||||
|
bool logits_all; // the llama_eval() call computes all logits, not just the last one
|
||||||
|
bool vocab_only; // only load the vocabulary, no weights
|
||||||
|
bool use_mmap; // use mmap if possible
|
||||||
|
bool use_mlock; // force system to keep model in RAM
|
||||||
|
bool embedding; // embedding mode only
|
||||||
|
|
||||||
|
// called with a progress value between 0 and 1, pass NULL to disable
|
||||||
|
llama_progress_callback progress_callback;
|
||||||
|
// context pointer passed to the progress callback
|
||||||
|
void * progress_callback_user_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
// model file types
|
// model file types
|
||||||
@ -86,302 +89,38 @@ extern "C" {
|
|||||||
LLAMA_FTYPE_MOSTLY_Q8_0 = 7, // except 1d tensors
|
LLAMA_FTYPE_MOSTLY_Q8_0 = 7, // except 1d tensors
|
||||||
LLAMA_FTYPE_MOSTLY_Q5_0 = 8, // except 1d tensors
|
LLAMA_FTYPE_MOSTLY_Q5_0 = 8, // except 1d tensors
|
||||||
LLAMA_FTYPE_MOSTLY_Q5_1 = 9, // except 1d tensors
|
LLAMA_FTYPE_MOSTLY_Q5_1 = 9, // except 1d tensors
|
||||||
LLAMA_FTYPE_MOSTLY_Q2_K = 10, // except 1d tensors
|
|
||||||
LLAMA_FTYPE_MOSTLY_Q3_K_S = 11, // except 1d tensors
|
|
||||||
LLAMA_FTYPE_MOSTLY_Q3_K_M = 12, // except 1d tensors
|
|
||||||
LLAMA_FTYPE_MOSTLY_Q3_K_L = 13, // except 1d tensors
|
|
||||||
LLAMA_FTYPE_MOSTLY_Q4_K_S = 14, // except 1d tensors
|
|
||||||
LLAMA_FTYPE_MOSTLY_Q4_K_M = 15, // except 1d tensors
|
|
||||||
LLAMA_FTYPE_MOSTLY_Q5_K_S = 16, // except 1d tensors
|
|
||||||
LLAMA_FTYPE_MOSTLY_Q5_K_M = 17, // except 1d tensors
|
|
||||||
LLAMA_FTYPE_MOSTLY_Q6_K = 18, // except 1d tensors
|
|
||||||
LLAMA_FTYPE_MOSTLY_IQ2_XXS = 19, // except 1d tensors
|
|
||||||
LLAMA_FTYPE_MOSTLY_IQ2_XS = 20, // except 1d tensors
|
|
||||||
LLAMA_FTYPE_MOSTLY_Q2_K_S = 21, // except 1d tensors
|
|
||||||
LLAMA_FTYPE_MOSTLY_Q3_K_XS = 22, // except 1d tensors
|
|
||||||
LLAMA_FTYPE_MOSTLY_IQ3_XXS = 23, // except 1d tensors
|
|
||||||
|
|
||||||
LLAMA_FTYPE_GUESSED = 1024, // not specified in the model file
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum llama_rope_scaling_type {
|
LLAMA_API struct llama_context_params llama_context_default_params();
|
||||||
LLAMA_ROPE_SCALING_UNSPECIFIED = -1,
|
|
||||||
LLAMA_ROPE_SCALING_NONE = 0,
|
|
||||||
LLAMA_ROPE_SCALING_LINEAR = 1,
|
|
||||||
LLAMA_ROPE_SCALING_YARN = 2,
|
|
||||||
LLAMA_ROPE_SCALING_MAX_VALUE = LLAMA_ROPE_SCALING_YARN,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum llama_split_mode {
|
LLAMA_API bool llama_mmap_supported();
|
||||||
LLAMA_SPLIT_NONE = 0, // single GPU
|
LLAMA_API bool llama_mlock_supported();
|
||||||
LLAMA_SPLIT_LAYER = 1, // split layers and KV across GPUs
|
|
||||||
LLAMA_SPLIT_ROW = 2, // split rows across GPUs
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct llama_token_data {
|
|
||||||
llama_token id; // token id
|
|
||||||
float logit; // log-odds of the token
|
|
||||||
float p; // probability of the token
|
|
||||||
} llama_token_data;
|
|
||||||
|
|
||||||
typedef struct llama_token_data_array {
|
|
||||||
llama_token_data * data;
|
|
||||||
size_t size;
|
|
||||||
bool sorted;
|
|
||||||
} llama_token_data_array;
|
|
||||||
|
|
||||||
typedef bool (*llama_progress_callback)(float progress, void *ctx);
|
|
||||||
|
|
||||||
// Input data for llama_decode
|
|
||||||
// A llama_batch object can contain input about one or many sequences
|
|
||||||
// The provided arrays (i.e. token, embd, pos, etc.) must have size of n_tokens
|
|
||||||
//
|
|
||||||
// - token : the token ids of the input (used when embd is NULL)
|
|
||||||
// - embd : token embeddings (i.e. float vector of size n_embd) (used when token is NULL)
|
|
||||||
// - pos : the positions of the respective token in the sequence
|
|
||||||
// - seq_id : the sequence to which the respective token belongs
|
|
||||||
// - logits : if zero, the logits for the respective token will not be output
|
|
||||||
//
|
|
||||||
typedef struct llama_batch {
|
|
||||||
int32_t n_tokens;
|
|
||||||
|
|
||||||
llama_token * token;
|
|
||||||
float * embd;
|
|
||||||
llama_pos * pos;
|
|
||||||
int32_t * n_seq_id;
|
|
||||||
llama_seq_id ** seq_id;
|
|
||||||
int8_t * logits;
|
|
||||||
|
|
||||||
// NOTE: helpers for smooth API transition - can be deprecated in the future
|
|
||||||
// for future-proof code, use the above fields instead and ignore everything below
|
|
||||||
//
|
|
||||||
// pos[i] = all_pos_0 + i*all_pos_1
|
|
||||||
//
|
|
||||||
llama_pos all_pos_0; // used if pos == NULL
|
|
||||||
llama_pos all_pos_1; // used if pos == NULL
|
|
||||||
llama_seq_id all_seq_id; // used if seq_id == NULL
|
|
||||||
} llama_batch;
|
|
||||||
|
|
||||||
enum llama_model_kv_override_type {
|
|
||||||
LLAMA_KV_OVERRIDE_INT,
|
|
||||||
LLAMA_KV_OVERRIDE_FLOAT,
|
|
||||||
LLAMA_KV_OVERRIDE_BOOL,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct llama_model_kv_override {
|
|
||||||
char key[128];
|
|
||||||
enum llama_model_kv_override_type tag;
|
|
||||||
union {
|
|
||||||
int64_t int_value;
|
|
||||||
double float_value;
|
|
||||||
bool bool_value;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct llama_model_params {
|
|
||||||
int32_t n_gpu_layers; // number of layers to store in VRAM
|
|
||||||
enum llama_split_mode split_mode; // how to split the model across multiple GPUs
|
|
||||||
|
|
||||||
// main_gpu interpretation depends on split_mode:
|
|
||||||
// LLAMA_SPLIT_NONE: the GPU that is used for the entire model
|
|
||||||
// LLAMA_SPLIT_ROW: the GPU that is used for small tensors and intermediate results
|
|
||||||
// LLAMA_SPLIT_LAYER: ignored
|
|
||||||
int32_t main_gpu;
|
|
||||||
|
|
||||||
// proportion of the model (layers or rows) to offload to each GPU, size: llama_max_devices()
|
|
||||||
const float * tensor_split;
|
|
||||||
|
|
||||||
// Called with a progress value between 0.0 and 1.0. Pass NULL to disable.
|
|
||||||
// If the provided progress_callback returns true, model loading continues.
|
|
||||||
// If it returns false, model loading is immediately aborted.
|
|
||||||
llama_progress_callback progress_callback;
|
|
||||||
|
|
||||||
// context pointer passed to the progress callback
|
|
||||||
void * progress_callback_user_data;
|
|
||||||
|
|
||||||
// override key-value pairs of the model meta data
|
|
||||||
const struct llama_model_kv_override * kv_overrides;
|
|
||||||
|
|
||||||
// Keep the booleans together to avoid misalignment during copy-by-value.
|
|
||||||
bool vocab_only; // only load the vocabulary, no weights
|
|
||||||
bool use_mmap; // use mmap if possible
|
|
||||||
bool use_mlock; // force system to keep model in RAM
|
|
||||||
};
|
|
||||||
|
|
||||||
struct llama_context_params {
|
|
||||||
uint32_t seed; // RNG seed, -1 for random
|
|
||||||
uint32_t n_ctx; // text context, 0 = from model
|
|
||||||
uint32_t n_batch; // prompt processing maximum batch size
|
|
||||||
uint32_t n_threads; // number of threads to use for generation
|
|
||||||
uint32_t n_threads_batch; // number of threads to use for batch processing
|
|
||||||
int32_t rope_scaling_type; // RoPE scaling type, from `enum llama_rope_scaling_type`
|
|
||||||
|
|
||||||
// ref: https://github.com/ggerganov/llama.cpp/pull/2054
|
|
||||||
float rope_freq_base; // RoPE base frequency, 0 = from model
|
|
||||||
float rope_freq_scale; // RoPE frequency scaling factor, 0 = from model
|
|
||||||
float yarn_ext_factor; // YaRN extrapolation mix factor, negative = from model
|
|
||||||
float yarn_attn_factor; // YaRN magnitude scaling factor
|
|
||||||
float yarn_beta_fast; // YaRN low correction dim
|
|
||||||
float yarn_beta_slow; // YaRN high correction dim
|
|
||||||
uint32_t yarn_orig_ctx; // YaRN original context size
|
|
||||||
|
|
||||||
ggml_backend_sched_eval_callback cb_eval;
|
|
||||||
void * cb_eval_user_data;
|
|
||||||
|
|
||||||
enum ggml_type type_k; // data type for K cache
|
|
||||||
enum ggml_type type_v; // data type for V cache
|
|
||||||
|
|
||||||
// Keep the booleans together to avoid misalignment during copy-by-value.
|
|
||||||
bool mul_mat_q; // if true, use experimental mul_mat_q kernels (DEPRECATED - always true)
|
|
||||||
bool logits_all; // the llama_eval() call computes all logits, not just the last one (DEPRECATED - set llama_batch.logits instead)
|
|
||||||
bool embedding; // embedding mode only
|
|
||||||
bool offload_kqv; // whether to offload the KQV ops (including the KV cache) to GPU
|
|
||||||
};
|
|
||||||
|
|
||||||
// model quantization parameters
|
|
||||||
typedef struct llama_model_quantize_params {
|
|
||||||
int32_t nthread; // number of threads to use for quantizing, if <=0 will use std::thread::hardware_concurrency()
|
|
||||||
enum llama_ftype ftype; // quantize to this llama_ftype
|
|
||||||
bool allow_requantize; // allow quantizing non-f32/f16 tensors
|
|
||||||
bool quantize_output_tensor; // quantize output.weight
|
|
||||||
bool only_copy; // only copy tensors - ftype, allow_requantize and quantize_output_tensor are ignored
|
|
||||||
bool pure; // disable k-quant mixtures and quantize all tensors to the same type
|
|
||||||
void * imatrix; // pointer to importance matrix data
|
|
||||||
} llama_model_quantize_params;
|
|
||||||
|
|
||||||
// grammar types
|
|
||||||
struct llama_grammar;
|
|
||||||
|
|
||||||
// grammar element type
|
|
||||||
enum llama_gretype {
|
|
||||||
// end of rule definition
|
|
||||||
LLAMA_GRETYPE_END = 0,
|
|
||||||
|
|
||||||
// start of alternate definition for rule
|
|
||||||
LLAMA_GRETYPE_ALT = 1,
|
|
||||||
|
|
||||||
// non-terminal element: reference to rule
|
|
||||||
LLAMA_GRETYPE_RULE_REF = 2,
|
|
||||||
|
|
||||||
// terminal element: character (code point)
|
|
||||||
LLAMA_GRETYPE_CHAR = 3,
|
|
||||||
|
|
||||||
// inverse char(s) ([^a], [^a-b] [^abc])
|
|
||||||
LLAMA_GRETYPE_CHAR_NOT = 4,
|
|
||||||
|
|
||||||
// modifies a preceding LLAMA_GRETYPE_CHAR or LLAMA_GRETYPE_CHAR_ALT to
|
|
||||||
// be an inclusive range ([a-z])
|
|
||||||
LLAMA_GRETYPE_CHAR_RNG_UPPER = 5,
|
|
||||||
|
|
||||||
// modifies a preceding LLAMA_GRETYPE_CHAR or
|
|
||||||
// LLAMA_GRETYPE_CHAR_RNG_UPPER to add an alternate char to match ([ab], [a-zA])
|
|
||||||
LLAMA_GRETYPE_CHAR_ALT = 6,
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct llama_grammar_element {
|
|
||||||
enum llama_gretype type;
|
|
||||||
uint32_t value; // Unicode code point or rule ID
|
|
||||||
} llama_grammar_element;
|
|
||||||
|
|
||||||
// performance timing information
|
|
||||||
struct llama_timings {
|
|
||||||
double t_start_ms;
|
|
||||||
double t_end_ms;
|
|
||||||
double t_load_ms;
|
|
||||||
double t_sample_ms;
|
|
||||||
double t_p_eval_ms;
|
|
||||||
double t_eval_ms;
|
|
||||||
|
|
||||||
int32_t n_sample;
|
|
||||||
int32_t n_p_eval;
|
|
||||||
int32_t n_eval;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Helpers for getting default parameters
|
|
||||||
LLAMA_API struct llama_model_params llama_model_default_params(void);
|
|
||||||
LLAMA_API struct llama_context_params llama_context_default_params(void);
|
|
||||||
LLAMA_API struct llama_model_quantize_params llama_model_quantize_default_params(void);
|
|
||||||
|
|
||||||
|
// TODO: not great API - very likely to change
|
||||||
// Initialize the llama + ggml backend
|
// Initialize the llama + ggml backend
|
||||||
// If numa is true, use NUMA optimizations
|
|
||||||
// Call once at the start of the program
|
// Call once at the start of the program
|
||||||
LLAMA_API void llama_backend_init(bool numa);
|
LLAMA_API void llama_init_backend();
|
||||||
|
|
||||||
// Call once at the end of the program - currently only used for MPI
|
LLAMA_API int64_t llama_time_us();
|
||||||
LLAMA_API void llama_backend_free(void);
|
|
||||||
|
|
||||||
LLAMA_API struct llama_model * llama_load_model_from_file(
|
// Various functions for loading a ggml llama model.
|
||||||
|
// Allocate (almost) all memory needed for the model.
|
||||||
|
// Return NULL on failure
|
||||||
|
LLAMA_API struct llama_context * llama_init_from_file(
|
||||||
const char * path_model,
|
const char * path_model,
|
||||||
struct llama_model_params params);
|
|
||||||
|
|
||||||
LLAMA_API void llama_free_model(struct llama_model * model);
|
|
||||||
|
|
||||||
LLAMA_API struct llama_context * llama_new_context_with_model(
|
|
||||||
struct llama_model * model,
|
|
||||||
struct llama_context_params params);
|
struct llama_context_params params);
|
||||||
|
|
||||||
// Frees all allocated memory
|
// Frees all allocated memory
|
||||||
LLAMA_API void llama_free(struct llama_context * ctx);
|
LLAMA_API void llama_free(struct llama_context * ctx);
|
||||||
|
|
||||||
LLAMA_API int64_t llama_time_us(void);
|
// TODO: not great API - very likely to change
|
||||||
|
|
||||||
LLAMA_API size_t llama_max_devices(void);
|
|
||||||
|
|
||||||
LLAMA_API bool llama_supports_mmap (void);
|
|
||||||
LLAMA_API bool llama_supports_mlock (void);
|
|
||||||
LLAMA_API bool llama_supports_gpu_offload(void);
|
|
||||||
|
|
||||||
LLAMA_API DEPRECATED(bool llama_mmap_supported (void), "use llama_supports_mmap() instead");
|
|
||||||
LLAMA_API DEPRECATED(bool llama_mlock_supported(void), "use llama_supports_mlock() instead");
|
|
||||||
|
|
||||||
LLAMA_API const struct llama_model * llama_get_model(const struct llama_context * ctx);
|
|
||||||
|
|
||||||
LLAMA_API uint32_t llama_n_ctx (const struct llama_context * ctx);
|
|
||||||
LLAMA_API uint32_t llama_n_batch (const struct llama_context * ctx);
|
|
||||||
|
|
||||||
LLAMA_API enum llama_vocab_type llama_vocab_type(const struct llama_model * model);
|
|
||||||
|
|
||||||
LLAMA_API int32_t llama_n_vocab (const struct llama_model * model);
|
|
||||||
LLAMA_API int32_t llama_n_ctx_train(const struct llama_model * model);
|
|
||||||
LLAMA_API int32_t llama_n_embd (const struct llama_model * model);
|
|
||||||
|
|
||||||
// Get the model's RoPE frequency scaling factor
|
|
||||||
LLAMA_API float llama_rope_freq_scale_train(const struct llama_model * model);
|
|
||||||
|
|
||||||
// Functions to access the model's GGUF metadata scalar values
|
|
||||||
// - The functions return the length of the string on success, or -1 on failure
|
|
||||||
// - The output string is always null-terminated and cleared on failure
|
|
||||||
// - GGUF array values are not supported by these functions
|
|
||||||
|
|
||||||
// Get metadata value as a string by key name
|
|
||||||
LLAMA_API int32_t llama_model_meta_val_str(const struct llama_model * model, const char * key, char * buf, size_t buf_size);
|
|
||||||
|
|
||||||
// Get the number of metadata key/value pairs
|
|
||||||
LLAMA_API int32_t llama_model_meta_count(const struct llama_model * model);
|
|
||||||
|
|
||||||
// Get metadata key name by index
|
|
||||||
LLAMA_API int32_t llama_model_meta_key_by_index(const struct llama_model * model, int32_t i, char * buf, size_t buf_size);
|
|
||||||
|
|
||||||
// Get metadata value as a string by index
|
|
||||||
LLAMA_API int32_t llama_model_meta_val_str_by_index(const struct llama_model * model, int32_t i, char * buf, size_t buf_size);
|
|
||||||
|
|
||||||
// Get a string describing the model type
|
|
||||||
LLAMA_API int32_t llama_model_desc(const struct llama_model * model, char * buf, size_t buf_size);
|
|
||||||
|
|
||||||
// Returns the total size of all the tensors in the model in bytes
|
|
||||||
LLAMA_API uint64_t llama_model_size(const struct llama_model * model);
|
|
||||||
|
|
||||||
// Returns the total number of parameters in the model
|
|
||||||
LLAMA_API uint64_t llama_model_n_params(const struct llama_model * model);
|
|
||||||
|
|
||||||
// Get a llama model tensor
|
|
||||||
LLAMA_API struct ggml_tensor * llama_get_model_tensor(struct llama_model * model, const char * name);
|
|
||||||
|
|
||||||
// Returns 0 on success
|
// Returns 0 on success
|
||||||
LLAMA_API uint32_t llama_model_quantize(
|
// nthread - how many threads to use. If <=0, will use std::thread::hardware_concurrency(), else the number given
|
||||||
|
LLAMA_API int llama_model_quantize(
|
||||||
const char * fname_inp,
|
const char * fname_inp,
|
||||||
const char * fname_out,
|
const char * fname_out,
|
||||||
const llama_model_quantize_params * params);
|
enum llama_ftype ftype,
|
||||||
|
int nthread);
|
||||||
|
|
||||||
// Apply a LoRA adapter to a loaded model
|
// Apply a LoRA adapter to a loaded model
|
||||||
// path_base_model is the path to a higher quality model to use as a base for
|
// path_base_model is the path to a higher quality model to use as a base for
|
||||||
@ -389,135 +128,17 @@ extern "C" {
|
|||||||
// The model needs to be reloaded before applying a new adapter, otherwise the adapter
|
// The model needs to be reloaded before applying a new adapter, otherwise the adapter
|
||||||
// will be applied on top of the previous one
|
// will be applied on top of the previous one
|
||||||
// Returns 0 on success
|
// Returns 0 on success
|
||||||
LLAMA_API DEPRECATED(int32_t llama_apply_lora_from_file(
|
LLAMA_API int llama_apply_lora_from_file(
|
||||||
struct llama_context * ctx,
|
struct llama_context * ctx,
|
||||||
const char * path_lora,
|
const char * path_lora,
|
||||||
float scale,
|
|
||||||
const char * path_base_model,
|
const char * path_base_model,
|
||||||
int32_t n_threads),
|
int n_threads);
|
||||||
"use llama_model_apply_lora_from_file instead");
|
|
||||||
|
|
||||||
LLAMA_API int32_t llama_model_apply_lora_from_file(
|
// Returns the number of tokens in the KV cache
|
||||||
const struct llama_model * model,
|
LLAMA_API int llama_get_kv_cache_token_count(const struct llama_context * ctx);
|
||||||
const char * path_lora,
|
|
||||||
float scale,
|
|
||||||
const char * path_base_model,
|
|
||||||
int32_t n_threads);
|
|
||||||
|
|
||||||
//
|
// Sets the current rng seed.
|
||||||
// KV cache
|
LLAMA_API void llama_set_rng_seed(struct llama_context * ctx, int seed);
|
||||||
//
|
|
||||||
|
|
||||||
// Information associated with an individual cell in the KV cache view.
|
|
||||||
struct llama_kv_cache_view_cell {
|
|
||||||
// The position for this cell. Takes KV cache shifts into account.
|
|
||||||
// May be negative if the cell is not populated.
|
|
||||||
llama_pos pos;
|
|
||||||
};
|
|
||||||
|
|
||||||
// An updateable view of the KV cache.
|
|
||||||
struct llama_kv_cache_view {
|
|
||||||
// Number of KV cache cells. This will be the same as the context size.
|
|
||||||
int32_t n_cells;
|
|
||||||
|
|
||||||
// Maximum number of sequences that can exist in a cell. It's not an error
|
|
||||||
// if there are more sequences in a cell than this value, however they will
|
|
||||||
// not be visible in the view cells_sequences.
|
|
||||||
int32_t n_max_seq;
|
|
||||||
|
|
||||||
// Number of tokens in the cache. For example, if there are two populated
|
|
||||||
// cells, the first with 1 sequence id in it and the second with 2 sequence
|
|
||||||
// ids then you'll have 3 tokens.
|
|
||||||
int32_t token_count;
|
|
||||||
|
|
||||||
// Number of populated cache cells.
|
|
||||||
int32_t used_cells;
|
|
||||||
|
|
||||||
// Maximum contiguous empty slots in the cache.
|
|
||||||
int32_t max_contiguous;
|
|
||||||
|
|
||||||
// Index to the start of the max_contiguous slot range. Can be negative
|
|
||||||
// when cache is full.
|
|
||||||
int32_t max_contiguous_idx;
|
|
||||||
|
|
||||||
// Information for an individual cell.
|
|
||||||
struct llama_kv_cache_view_cell * cells;
|
|
||||||
|
|
||||||
// The sequences for each cell. There will be n_max_seq items per cell.
|
|
||||||
llama_seq_id * cells_sequences;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create an empty KV cache view. (use only for debugging purposes)
|
|
||||||
LLAMA_API struct llama_kv_cache_view llama_kv_cache_view_init(const struct llama_context * ctx, int32_t n_max_seq);
|
|
||||||
|
|
||||||
// Free a KV cache view. (use only for debugging purposes)
|
|
||||||
LLAMA_API void llama_kv_cache_view_free(struct llama_kv_cache_view * view);
|
|
||||||
|
|
||||||
// Update the KV cache view structure with the current state of the KV cache. (use only for debugging purposes)
|
|
||||||
LLAMA_API void llama_kv_cache_view_update(const struct llama_context * ctx, struct llama_kv_cache_view * view);
|
|
||||||
|
|
||||||
// Returns the number of tokens in the KV cache (slow, use only for debug)
|
|
||||||
// If a KV cell has multiple sequences assigned to it, it will be counted multiple times
|
|
||||||
LLAMA_API int32_t llama_get_kv_cache_token_count(const struct llama_context * ctx);
|
|
||||||
|
|
||||||
// Returns the number of used KV cells (i.e. have at least one sequence assigned to them)
|
|
||||||
LLAMA_API int32_t llama_get_kv_cache_used_cells(const struct llama_context * ctx);
|
|
||||||
|
|
||||||
// Clear the KV cache
|
|
||||||
LLAMA_API void llama_kv_cache_clear(
|
|
||||||
struct llama_context * ctx);
|
|
||||||
|
|
||||||
// Removes all tokens that belong to the specified sequence and have positions in [p0, p1)
|
|
||||||
// seq_id < 0 : match any sequence
|
|
||||||
// p0 < 0 : [0, p1]
|
|
||||||
// p1 < 0 : [p0, inf)
|
|
||||||
LLAMA_API void llama_kv_cache_seq_rm(
|
|
||||||
struct llama_context * ctx,
|
|
||||||
llama_seq_id seq_id,
|
|
||||||
llama_pos p0,
|
|
||||||
llama_pos p1);
|
|
||||||
|
|
||||||
// Copy all tokens that belong to the specified sequence to another sequence
|
|
||||||
// Note that this does not allocate extra KV cache memory - it simply assigns the tokens to the new sequence
|
|
||||||
// p0 < 0 : [0, p1]
|
|
||||||
// p1 < 0 : [p0, inf)
|
|
||||||
LLAMA_API void llama_kv_cache_seq_cp(
|
|
||||||
struct llama_context * ctx,
|
|
||||||
llama_seq_id seq_id_src,
|
|
||||||
llama_seq_id seq_id_dst,
|
|
||||||
llama_pos p0,
|
|
||||||
llama_pos p1);
|
|
||||||
|
|
||||||
// Removes all tokens that do not belong to the specified sequence
|
|
||||||
LLAMA_API void llama_kv_cache_seq_keep(
|
|
||||||
struct llama_context * ctx,
|
|
||||||
llama_seq_id seq_id);
|
|
||||||
|
|
||||||
// Adds relative position "delta" to all tokens that belong to the specified sequence and have positions in [p0, p1)
|
|
||||||
// If the KV cache is RoPEd, the KV data is updated accordingly
|
|
||||||
// p0 < 0 : [0, p1]
|
|
||||||
// p1 < 0 : [p0, inf)
|
|
||||||
LLAMA_API void llama_kv_cache_seq_shift(
|
|
||||||
struct llama_context * ctx,
|
|
||||||
llama_seq_id seq_id,
|
|
||||||
llama_pos p0,
|
|
||||||
llama_pos p1,
|
|
||||||
llama_pos delta);
|
|
||||||
|
|
||||||
// Integer division of the positions by factor of `d > 1`
|
|
||||||
// If the KV cache is RoPEd, the KV data is updated accordingly
|
|
||||||
// p0 < 0 : [0, p1]
|
|
||||||
// p1 < 0 : [p0, inf)
|
|
||||||
LLAMA_API void llama_kv_cache_seq_div(
|
|
||||||
struct llama_context * ctx,
|
|
||||||
llama_seq_id seq_id,
|
|
||||||
llama_pos p0,
|
|
||||||
llama_pos p1,
|
|
||||||
int d);
|
|
||||||
|
|
||||||
//
|
|
||||||
// State / sessions
|
|
||||||
//
|
|
||||||
|
|
||||||
// Returns the maximum size in bytes of the state (rng, logits, embedding
|
// Returns the maximum size in bytes of the state (rng, logits, embedding
|
||||||
// and kv_cache) - will often be smaller after compacting tokens
|
// and kv_cache) - will often be smaller after compacting tokens
|
||||||
@ -526,276 +147,85 @@ extern "C" {
|
|||||||
// Copies the state to the specified destination address.
|
// Copies the state to the specified destination address.
|
||||||
// Destination needs to have allocated enough memory.
|
// Destination needs to have allocated enough memory.
|
||||||
// Returns the number of bytes copied
|
// Returns the number of bytes copied
|
||||||
LLAMA_API size_t llama_copy_state_data(
|
LLAMA_API size_t llama_copy_state_data(struct llama_context * ctx, uint8_t * dst);
|
||||||
struct llama_context * ctx,
|
|
||||||
uint8_t * dst);
|
|
||||||
|
|
||||||
// Set the state reading from the specified address
|
// Set the state reading from the specified address
|
||||||
// Returns the number of bytes read
|
// Returns the number of bytes read
|
||||||
LLAMA_API size_t llama_set_state_data(
|
LLAMA_API size_t llama_set_state_data(struct llama_context * ctx, uint8_t * src);
|
||||||
struct llama_context * ctx,
|
|
||||||
uint8_t * src);
|
|
||||||
|
|
||||||
// Save/load session file
|
// Save/load session file
|
||||||
LLAMA_API bool llama_load_session_file(
|
LLAMA_API bool llama_load_session_file(struct llama_context * ctx, const char * path_session, llama_token * tokens_out, size_t n_token_capacity, size_t * n_token_count_out);
|
||||||
struct llama_context * ctx,
|
LLAMA_API bool llama_save_session_file(struct llama_context * ctx, const char * path_session, const llama_token * tokens, size_t n_token_count);
|
||||||
const char * path_session,
|
|
||||||
llama_token * tokens_out,
|
|
||||||
size_t n_token_capacity,
|
|
||||||
size_t * n_token_count_out);
|
|
||||||
|
|
||||||
LLAMA_API bool llama_save_session_file(
|
// Run the llama inference to obtain the logits and probabilities for the next token.
|
||||||
struct llama_context * ctx,
|
|
||||||
const char * path_session,
|
|
||||||
const llama_token * tokens,
|
|
||||||
size_t n_token_count);
|
|
||||||
|
|
||||||
//
|
|
||||||
// Decoding
|
|
||||||
//
|
|
||||||
|
|
||||||
// Run the llama inference to obtain the logits and probabilities for the next token(s).
|
|
||||||
// tokens + n_tokens is the provided batch of new tokens to process
|
// tokens + n_tokens is the provided batch of new tokens to process
|
||||||
// n_past is the number of tokens to use from previous eval calls
|
// n_past is the number of tokens to use from previous eval calls
|
||||||
// Returns 0 on success
|
// Returns 0 on success
|
||||||
// DEPRECATED: use llama_decode() instead
|
LLAMA_API int llama_eval(
|
||||||
LLAMA_API DEPRECATED(int llama_eval(
|
|
||||||
struct llama_context * ctx,
|
struct llama_context * ctx,
|
||||||
|
const llama_token * 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 a negative number on failure - the number of tokens that would have been returned
|
||||||
|
// TODO: not sure if correct
|
||||||
|
LLAMA_API int llama_tokenize(
|
||||||
|
struct llama_context * ctx,
|
||||||
|
const char * text,
|
||||||
llama_token * tokens,
|
llama_token * tokens,
|
||||||
int32_t n_tokens,
|
int n_max_tokens,
|
||||||
int32_t n_past),
|
bool add_bos);
|
||||||
"use llama_decode() instead");
|
|
||||||
|
|
||||||
// Same as llama_eval, but use float matrix input directly.
|
LLAMA_API int llama_n_vocab(const struct llama_context * ctx);
|
||||||
// DEPRECATED: use llama_decode() instead
|
LLAMA_API int llama_n_ctx (const struct llama_context * ctx);
|
||||||
LLAMA_API DEPRECATED(int llama_eval_embd(
|
LLAMA_API int llama_n_embd (const struct llama_context * ctx);
|
||||||
struct llama_context * ctx,
|
|
||||||
float * embd,
|
|
||||||
int32_t n_tokens,
|
|
||||||
int32_t n_past),
|
|
||||||
"use llama_decode() instead");
|
|
||||||
|
|
||||||
// Return batch for single sequence of tokens starting at pos_0
|
|
||||||
//
|
|
||||||
// NOTE: this is a helper function to facilitate transition to the new batch API - avoid using it
|
|
||||||
//
|
|
||||||
LLAMA_API struct llama_batch llama_batch_get_one(
|
|
||||||
llama_token * tokens,
|
|
||||||
int32_t n_tokens,
|
|
||||||
llama_pos pos_0,
|
|
||||||
llama_seq_id seq_id);
|
|
||||||
|
|
||||||
// Allocates a batch of tokens on the heap that can hold a maximum of n_tokens
|
|
||||||
// Each token can be assigned up to n_seq_max sequence ids
|
|
||||||
// The batch has to be freed with llama_batch_free()
|
|
||||||
// If embd != 0, llama_batch.embd will be allocated with size of n_tokens * embd * sizeof(float)
|
|
||||||
// Otherwise, llama_batch.token will be allocated to store n_tokens llama_token
|
|
||||||
// The rest of the llama_batch members are allocated with size n_tokens
|
|
||||||
// All members are left uninitialized
|
|
||||||
LLAMA_API struct llama_batch llama_batch_init(
|
|
||||||
int32_t n_tokens,
|
|
||||||
int32_t embd,
|
|
||||||
int32_t n_seq_max);
|
|
||||||
|
|
||||||
// Frees a batch of tokens allocated with llama_batch_init()
|
|
||||||
LLAMA_API void llama_batch_free(struct llama_batch batch);
|
|
||||||
|
|
||||||
// Positive return values does not mean a fatal error, but rather a warning.
|
|
||||||
// 0 - success
|
|
||||||
// 1 - could not find a KV slot for the batch (try reducing the size of the batch or increase the context)
|
|
||||||
// < 0 - error
|
|
||||||
LLAMA_API int32_t llama_decode(
|
|
||||||
struct llama_context * ctx,
|
|
||||||
struct llama_batch batch);
|
|
||||||
|
|
||||||
// Set the number of threads used for decoding
|
|
||||||
// n_threads is the number of threads used for generation (single token)
|
|
||||||
// n_threads_batch is the number of threads used for prompt and batch processing (multiple tokens)
|
|
||||||
LLAMA_API void llama_set_n_threads(struct llama_context * ctx, uint32_t n_threads, uint32_t n_threads_batch);
|
|
||||||
|
|
||||||
// Token logits obtained from the last call to llama_eval()
|
// Token logits obtained from the last call to llama_eval()
|
||||||
// The logits for the last token are stored in the last row
|
// The logits for the last token are stored in the last row
|
||||||
// Logits for which llama_batch.logits[i] == 0 are undefined
|
// Can be mutated in order to change the probabilities of the next token
|
||||||
// Rows: n_tokens provided with llama_batch
|
// Rows: n_tokens
|
||||||
// Cols: n_vocab
|
// Cols: n_vocab
|
||||||
LLAMA_API float * llama_get_logits(struct llama_context * ctx);
|
LLAMA_API float * llama_get_logits(struct llama_context * ctx);
|
||||||
|
|
||||||
// Logits for the ith token. Equivalent to:
|
|
||||||
// llama_get_logits(ctx) + i*n_vocab
|
|
||||||
LLAMA_API float * llama_get_logits_ith(struct llama_context * ctx, int32_t i);
|
|
||||||
|
|
||||||
// Get the embeddings for the input
|
// Get the embeddings for the input
|
||||||
// shape: [n_embd] (1-dimensional)
|
// shape: [n_embd] (1-dimensional)
|
||||||
LLAMA_API float * llama_get_embeddings(struct llama_context * ctx);
|
LLAMA_API float * llama_get_embeddings(struct llama_context * ctx);
|
||||||
|
|
||||||
//
|
// Token Id -> String. Uses the vocabulary in the provided context
|
||||||
// Vocab
|
LLAMA_API const char * llama_token_to_str(const struct llama_context * ctx, llama_token token);
|
||||||
//
|
|
||||||
|
|
||||||
LLAMA_API const char * llama_token_get_text(const struct llama_model * model, llama_token token);
|
|
||||||
|
|
||||||
LLAMA_API float llama_token_get_score(const struct llama_model * model, llama_token token);
|
|
||||||
|
|
||||||
LLAMA_API enum llama_token_type llama_token_get_type(const struct llama_model * model, llama_token token);
|
|
||||||
|
|
||||||
// Special tokens
|
// Special tokens
|
||||||
LLAMA_API llama_token llama_token_bos(const struct llama_model * model); // beginning-of-sentence
|
LLAMA_API llama_token llama_token_bos();
|
||||||
LLAMA_API llama_token llama_token_eos(const struct llama_model * model); // end-of-sentence
|
LLAMA_API llama_token llama_token_eos();
|
||||||
LLAMA_API llama_token llama_token_nl (const struct llama_model * model); // next-line
|
LLAMA_API llama_token llama_token_nl();
|
||||||
|
|
||||||
// Returns -1 if unknown, 1 for true or 0 for false.
|
|
||||||
LLAMA_API int32_t llama_add_bos_token(const struct llama_model * model);
|
|
||||||
|
|
||||||
// Returns -1 if unknown, 1 for true or 0 for false.
|
|
||||||
LLAMA_API int32_t llama_add_eos_token(const struct llama_model * model);
|
|
||||||
|
|
||||||
// codellama infill tokens
|
|
||||||
LLAMA_API llama_token llama_token_prefix(const struct llama_model * model); // Beginning of infill prefix
|
|
||||||
LLAMA_API llama_token llama_token_middle(const struct llama_model * model); // Beginning of infill middle
|
|
||||||
LLAMA_API llama_token llama_token_suffix(const struct llama_model * model); // Beginning of infill suffix
|
|
||||||
LLAMA_API llama_token llama_token_eot (const struct llama_model * model); // End of infill middle
|
|
||||||
|
|
||||||
//
|
|
||||||
// Tokenization
|
|
||||||
//
|
|
||||||
|
|
||||||
/// @details Convert the provided text into tokens.
|
|
||||||
/// @param tokens The tokens pointer must be large enough to hold the resulting tokens.
|
|
||||||
/// @return Returns the number of tokens on success, no more than n_max_tokens
|
|
||||||
/// @return Returns a negative number on failure - the number of tokens that would have been returned
|
|
||||||
/// @param special Allow tokenizing special and/or control tokens which otherwise are not exposed and treated as plaintext.
|
|
||||||
/// Does not insert a leading space.
|
|
||||||
LLAMA_API int32_t llama_tokenize(
|
|
||||||
const struct llama_model * model,
|
|
||||||
const char * text,
|
|
||||||
int32_t text_len,
|
|
||||||
llama_token * tokens,
|
|
||||||
int32_t n_max_tokens,
|
|
||||||
bool add_bos,
|
|
||||||
bool special);
|
|
||||||
|
|
||||||
// Token Id -> Piece.
|
|
||||||
// Uses the vocabulary in the provided context.
|
|
||||||
// Does not write null terminator to the buffer.
|
|
||||||
// User code is responsible to remove the leading whitespace of the first non-BOS token when decoding multiple tokens.
|
|
||||||
LLAMA_API int32_t llama_token_to_piece(
|
|
||||||
const struct llama_model * model,
|
|
||||||
llama_token token,
|
|
||||||
char * buf,
|
|
||||||
int32_t length);
|
|
||||||
|
|
||||||
//
|
|
||||||
// Grammar
|
|
||||||
//
|
|
||||||
|
|
||||||
LLAMA_API struct llama_grammar * llama_grammar_init(
|
|
||||||
const llama_grammar_element ** rules,
|
|
||||||
size_t n_rules,
|
|
||||||
size_t start_rule_index);
|
|
||||||
|
|
||||||
LLAMA_API void llama_grammar_free(struct llama_grammar * grammar);
|
|
||||||
|
|
||||||
LLAMA_API struct llama_grammar * llama_grammar_copy(const struct llama_grammar * grammar);
|
|
||||||
|
|
||||||
//
|
|
||||||
// Sampling functions
|
// Sampling functions
|
||||||
//
|
|
||||||
|
|
||||||
// Sets the current rng seed.
|
|
||||||
LLAMA_API void llama_set_rng_seed(struct llama_context * ctx, uint32_t seed);
|
|
||||||
|
|
||||||
/// @details Repetition penalty described in CTRL academic paper https://arxiv.org/abs/1909.05858, with negative logit fix.
|
/// @details Repetition penalty described in CTRL academic paper https://arxiv.org/abs/1909.05858, with negative logit fix.
|
||||||
|
LLAMA_API void llama_sample_repetition_penalty(struct llama_context * ctx, llama_token_data_array * candidates, const llama_token * last_tokens, size_t last_tokens_size, float penalty);
|
||||||
|
|
||||||
/// @details Frequency and presence penalties described in OpenAI API https://platform.openai.com/docs/api-reference/parameter-details.
|
/// @details Frequency and presence penalties described in OpenAI API https://platform.openai.com/docs/api-reference/parameter-details.
|
||||||
LLAMA_API void llama_sample_repetition_penalties(
|
LLAMA_API void llama_sample_frequency_and_presence_penalties(struct llama_context * ctx, llama_token_data_array * candidates, const llama_token * last_tokens, size_t last_tokens_size, float alpha_frequency, float alpha_presence);
|
||||||
struct llama_context * ctx,
|
|
||||||
llama_token_data_array * candidates,
|
|
||||||
const llama_token * last_tokens,
|
|
||||||
size_t penalty_last_n,
|
|
||||||
float penalty_repeat,
|
|
||||||
float penalty_freq,
|
|
||||||
float penalty_present);
|
|
||||||
|
|
||||||
/// @details Apply classifier-free guidance to the logits as described in academic paper "Stay on topic with Classifier-Free Guidance" https://arxiv.org/abs/2306.17806
|
|
||||||
/// @param logits Logits extracted from the original generation context.
|
|
||||||
/// @param logits_guidance Logits extracted from a separate context from the same model. Other than a negative prompt at the beginning, it should have all generated and user input tokens copied from the main context.
|
|
||||||
/// @param scale Guidance strength. 1.0f means no guidance. Higher values mean stronger guidance.
|
|
||||||
LLAMA_API void llama_sample_apply_guidance(
|
|
||||||
struct llama_context * ctx,
|
|
||||||
float * logits,
|
|
||||||
float * logits_guidance,
|
|
||||||
float scale);
|
|
||||||
|
|
||||||
LLAMA_API DEPRECATED(void llama_sample_classifier_free_guidance(
|
|
||||||
struct llama_context * ctx,
|
|
||||||
llama_token_data_array * candidates,
|
|
||||||
struct llama_context * guidance_ctx,
|
|
||||||
float scale),
|
|
||||||
"use llama_sample_apply_guidance() instead");
|
|
||||||
|
|
||||||
/// @details Sorts candidate tokens by their logits in descending order and calculate probabilities based on logits.
|
/// @details Sorts candidate tokens by their logits in descending order and calculate probabilities based on logits.
|
||||||
LLAMA_API void llama_sample_softmax(
|
LLAMA_API void llama_sample_softmax(struct llama_context * ctx, llama_token_data_array * candidates);
|
||||||
struct llama_context * ctx,
|
|
||||||
llama_token_data_array * candidates);
|
|
||||||
|
|
||||||
/// @details Top-K sampling described in academic paper "The Curious Case of Neural Text Degeneration" https://arxiv.org/abs/1904.09751
|
/// @details Top-K sampling described in academic paper "The Curious Case of Neural Text Degeneration" https://arxiv.org/abs/1904.09751
|
||||||
LLAMA_API void llama_sample_top_k(
|
LLAMA_API void llama_sample_top_k(struct llama_context * ctx, llama_token_data_array * candidates, int k, size_t min_keep);
|
||||||
struct llama_context * ctx,
|
|
||||||
llama_token_data_array * candidates,
|
|
||||||
int32_t k,
|
|
||||||
size_t min_keep);
|
|
||||||
|
|
||||||
/// @details Nucleus sampling described in academic paper "The Curious Case of Neural Text Degeneration" https://arxiv.org/abs/1904.09751
|
/// @details Nucleus sampling described in academic paper "The Curious Case of Neural Text Degeneration" https://arxiv.org/abs/1904.09751
|
||||||
LLAMA_API void llama_sample_top_p(
|
LLAMA_API void llama_sample_top_p(struct llama_context * ctx, llama_token_data_array * candidates, float p, size_t min_keep);
|
||||||
struct llama_context * ctx,
|
|
||||||
llama_token_data_array * candidates,
|
|
||||||
float p,
|
|
||||||
size_t min_keep);
|
|
||||||
|
|
||||||
/// @details Minimum P sampling as described in https://github.com/ggerganov/llama.cpp/pull/3841
|
|
||||||
LLAMA_API void llama_sample_min_p(
|
|
||||||
struct llama_context * ctx,
|
|
||||||
llama_token_data_array * candidates,
|
|
||||||
float p,
|
|
||||||
size_t min_keep);
|
|
||||||
|
|
||||||
/// @details Tail Free Sampling described in https://www.trentonbricken.com/Tail-Free-Sampling/.
|
/// @details Tail Free Sampling described in https://www.trentonbricken.com/Tail-Free-Sampling/.
|
||||||
LLAMA_API void llama_sample_tail_free(
|
LLAMA_API void llama_sample_tail_free(struct llama_context * ctx, llama_token_data_array * candidates, float z, size_t min_keep);
|
||||||
struct llama_context * ctx,
|
|
||||||
llama_token_data_array * candidates,
|
|
||||||
float z,
|
|
||||||
size_t min_keep);
|
|
||||||
|
|
||||||
/// @details Locally Typical Sampling implementation described in the paper https://arxiv.org/abs/2202.00666.
|
/// @details Locally Typical Sampling implementation described in the paper https://arxiv.org/abs/2202.00666.
|
||||||
LLAMA_API void llama_sample_typical(
|
LLAMA_API void llama_sample_typical(struct llama_context * ctx, llama_token_data_array * candidates, float p, size_t min_keep);
|
||||||
struct llama_context * ctx,
|
LLAMA_API void llama_sample_temperature(struct llama_context * ctx, llama_token_data_array * candidates, float temp);
|
||||||
llama_token_data_array * candidates,
|
|
||||||
float p,
|
|
||||||
size_t min_keep);
|
|
||||||
|
|
||||||
/// @details Dynamic temperature implementation described in the paper https://arxiv.org/abs/2309.02772.
|
|
||||||
LLAMA_API void llama_sample_entropy(
|
|
||||||
struct llama_context * ctx,
|
|
||||||
llama_token_data_array * candidates_p,
|
|
||||||
float min_temp,
|
|
||||||
float max_temp,
|
|
||||||
float exponent_val);
|
|
||||||
|
|
||||||
LLAMA_API void llama_sample_temp(
|
|
||||||
struct llama_context * ctx,
|
|
||||||
llama_token_data_array * candidates,
|
|
||||||
float temp);
|
|
||||||
|
|
||||||
LLAMA_API DEPRECATED(void llama_sample_temperature(
|
|
||||||
struct llama_context * ctx,
|
|
||||||
llama_token_data_array * candidates,
|
|
||||||
float temp),
|
|
||||||
"use llama_sample_temp instead");
|
|
||||||
|
|
||||||
/// @details Apply constraints from grammar
|
|
||||||
LLAMA_API void llama_sample_grammar(
|
|
||||||
struct llama_context * ctx,
|
|
||||||
llama_token_data_array * candidates,
|
|
||||||
const struct llama_grammar * grammar);
|
|
||||||
|
|
||||||
/// @details Mirostat 1.0 algorithm described in the paper https://arxiv.org/abs/2007.14966. Uses tokens instead of words.
|
/// @details Mirostat 1.0 algorithm described in the paper https://arxiv.org/abs/2007.14966. Uses tokens instead of words.
|
||||||
/// @param candidates A vector of `llama_token_data` containing the candidate tokens, their probabilities (p), and log-odds (logit) for the current position in the generated text.
|
/// @param candidates A vector of `llama_token_data` containing the candidate tokens, their probabilities (p), and log-odds (logit) for the current position in the generated text.
|
||||||
@ -803,102 +233,28 @@ extern "C" {
|
|||||||
/// @param eta The learning rate used to update `mu` based on the error between the target and observed surprisal of the sampled word. A larger learning rate will cause `mu` to be updated more quickly, while a smaller learning rate will result in slower updates.
|
/// @param eta The learning rate used to update `mu` based on the error between the target and observed surprisal of the sampled word. A larger learning rate will cause `mu` to be updated more quickly, while a smaller learning rate will result in slower updates.
|
||||||
/// @param m The number of tokens considered in the estimation of `s_hat`. This is an arbitrary value that is used to calculate `s_hat`, which in turn helps to calculate the value of `k`. In the paper, they use `m = 100`, but you can experiment with different values to see how it affects the performance of the algorithm.
|
/// @param m The number of tokens considered in the estimation of `s_hat`. This is an arbitrary value that is used to calculate `s_hat`, which in turn helps to calculate the value of `k`. In the paper, they use `m = 100`, but you can experiment with different values to see how it affects the performance of the algorithm.
|
||||||
/// @param mu Maximum cross-entropy. This value is initialized to be twice the target cross-entropy (`2 * tau`) and is updated in the algorithm based on the error between the target and observed surprisal.
|
/// @param mu Maximum cross-entropy. This value is initialized to be twice the target cross-entropy (`2 * tau`) and is updated in the algorithm based on the error between the target and observed surprisal.
|
||||||
LLAMA_API llama_token llama_sample_token_mirostat(
|
LLAMA_API llama_token llama_sample_token_mirostat(struct llama_context * ctx, llama_token_data_array * candidates, float tau, float eta, int m, float * mu);
|
||||||
struct llama_context * ctx,
|
|
||||||
llama_token_data_array * candidates,
|
|
||||||
float tau,
|
|
||||||
float eta,
|
|
||||||
int32_t m,
|
|
||||||
float * mu);
|
|
||||||
|
|
||||||
/// @details Mirostat 2.0 algorithm described in the paper https://arxiv.org/abs/2007.14966. Uses tokens instead of words.
|
/// @details Mirostat 2.0 algorithm described in the paper https://arxiv.org/abs/2007.14966. Uses tokens instead of words.
|
||||||
/// @param candidates A vector of `llama_token_data` containing the candidate tokens, their probabilities (p), and log-odds (logit) for the current position in the generated text.
|
/// @param candidates A vector of `llama_token_data` containing the candidate tokens, their probabilities (p), and log-odds (logit) for the current position in the generated text.
|
||||||
/// @param tau The target cross-entropy (or surprise) value you want to achieve for the generated text. A higher value corresponds to more surprising or less predictable text, while a lower value corresponds to less surprising or more predictable text.
|
/// @param tau The target cross-entropy (or surprise) value you want to achieve for the generated text. A higher value corresponds to more surprising or less predictable text, while a lower value corresponds to less surprising or more predictable text.
|
||||||
/// @param eta The learning rate used to update `mu` based on the error between the target and observed surprisal of the sampled word. A larger learning rate will cause `mu` to be updated more quickly, while a smaller learning rate will result in slower updates.
|
/// @param eta The learning rate used to update `mu` based on the error between the target and observed surprisal of the sampled word. A larger learning rate will cause `mu` to be updated more quickly, while a smaller learning rate will result in slower updates.
|
||||||
/// @param mu Maximum cross-entropy. This value is initialized to be twice the target cross-entropy (`2 * tau`) and is updated in the algorithm based on the error between the target and observed surprisal.
|
/// @param mu Maximum cross-entropy. This value is initialized to be twice the target cross-entropy (`2 * tau`) and is updated in the algorithm based on the error between the target and observed surprisal.
|
||||||
LLAMA_API llama_token llama_sample_token_mirostat_v2(
|
LLAMA_API llama_token llama_sample_token_mirostat_v2(struct llama_context * ctx, llama_token_data_array * candidates, float tau, float eta, float * mu);
|
||||||
struct llama_context * ctx,
|
|
||||||
llama_token_data_array * candidates,
|
|
||||||
float tau,
|
|
||||||
float eta,
|
|
||||||
float * mu);
|
|
||||||
|
|
||||||
/// @details Selects the token with the highest probability.
|
/// @details Selects the token with the highest probability.
|
||||||
/// Does not compute the token probabilities. Use llama_sample_softmax() instead.
|
LLAMA_API llama_token llama_sample_token_greedy(struct llama_context * ctx, llama_token_data_array * candidates);
|
||||||
LLAMA_API llama_token llama_sample_token_greedy(
|
|
||||||
struct llama_context * ctx,
|
|
||||||
llama_token_data_array * candidates);
|
|
||||||
|
|
||||||
/// @details Randomly selects a token from the candidates based on their probabilities.
|
/// @details Randomly selects a token from the candidates based on their probabilities.
|
||||||
LLAMA_API llama_token llama_sample_token(
|
LLAMA_API llama_token llama_sample_token(struct llama_context * ctx, llama_token_data_array * candidates);
|
||||||
struct llama_context * ctx,
|
|
||||||
llama_token_data_array * candidates);
|
|
||||||
|
|
||||||
/// @details Accepts the sampled token into the grammar
|
|
||||||
LLAMA_API void llama_grammar_accept_token(
|
|
||||||
struct llama_context * ctx,
|
|
||||||
struct llama_grammar * grammar,
|
|
||||||
llama_token token);
|
|
||||||
|
|
||||||
//
|
|
||||||
// Beam search
|
|
||||||
//
|
|
||||||
|
|
||||||
struct llama_beam_view {
|
|
||||||
const llama_token * tokens;
|
|
||||||
|
|
||||||
size_t n_tokens;
|
|
||||||
float p; // Cumulative beam probability (renormalized relative to all beams)
|
|
||||||
bool eob; // Callback should set this to true when a beam is at end-of-beam.
|
|
||||||
};
|
|
||||||
|
|
||||||
// Passed to beam_search_callback function.
|
|
||||||
// Whenever 0 < common_prefix_length, this number of tokens should be copied from any of the beams
|
|
||||||
// (e.g. beams[0]) as they will be removed (shifted) from all beams in all subsequent callbacks.
|
|
||||||
// These pointers are valid only during the synchronous callback, so should not be saved.
|
|
||||||
struct llama_beams_state {
|
|
||||||
struct llama_beam_view * beam_views;
|
|
||||||
|
|
||||||
size_t n_beams; // Number of elements in beam_views[].
|
|
||||||
size_t common_prefix_length; // Current max length of prefix tokens shared by all beams.
|
|
||||||
bool last_call; // True iff this is the last callback invocation.
|
|
||||||
};
|
|
||||||
|
|
||||||
// Type of pointer to the beam_search_callback function.
|
|
||||||
// void* callback_data is any custom data passed to llama_beam_search, that is subsequently
|
|
||||||
// passed back to beam_search_callback. This avoids having to use global variables in the callback.
|
|
||||||
typedef void (*llama_beam_search_callback_fn_t)(void * callback_data, struct llama_beams_state);
|
|
||||||
|
|
||||||
/// @details Deterministically returns entire sentence constructed by a beam search.
|
|
||||||
/// @param ctx Pointer to the llama_context.
|
|
||||||
/// @param callback Invoked for each iteration of the beam_search loop, passing in beams_state.
|
|
||||||
/// @param callback_data A pointer that is simply passed back to callback.
|
|
||||||
/// @param n_beams Number of beams to use.
|
|
||||||
/// @param n_past Number of tokens already evaluated.
|
|
||||||
/// @param n_predict Maximum number of tokens to predict. EOS may occur earlier.
|
|
||||||
LLAMA_API void llama_beam_search(
|
|
||||||
struct llama_context * ctx,
|
|
||||||
llama_beam_search_callback_fn_t callback,
|
|
||||||
void * callback_data,
|
|
||||||
size_t n_beams,
|
|
||||||
int32_t n_past,
|
|
||||||
int32_t n_predict);
|
|
||||||
|
|
||||||
// Performance information
|
// Performance information
|
||||||
LLAMA_API struct llama_timings llama_get_timings(struct llama_context * ctx);
|
|
||||||
|
|
||||||
LLAMA_API void llama_print_timings(struct llama_context * ctx);
|
LLAMA_API void llama_print_timings(struct llama_context * ctx);
|
||||||
LLAMA_API void llama_reset_timings(struct llama_context * ctx);
|
LLAMA_API void llama_reset_timings(struct llama_context * ctx);
|
||||||
|
|
||||||
// Print system information
|
// Print system information
|
||||||
LLAMA_API const char * llama_print_system_info(void);
|
LLAMA_API const char * llama_print_system_info(void);
|
||||||
|
|
||||||
// Set callback for all future logging events.
|
|
||||||
// If this is not called, or NULL is supplied, everything is output on stderr.
|
|
||||||
LLAMA_API void llama_log_set(ggml_log_callback log_callback, void * user_data);
|
|
||||||
|
|
||||||
LLAMA_API void llama_dump_timing_info_yaml(FILE * stream, const struct llama_context * ctx);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -908,13 +264,10 @@ extern "C" {
|
|||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
struct ggml_tensor;
|
struct ggml_tensor;
|
||||||
|
|
||||||
const std::vector<std::pair<std::string, struct ggml_tensor *>> & llama_internal_get_tensor_map(
|
std::vector<std::pair<std::string, struct ggml_tensor *>>& llama_internal_get_tensor_map(struct llama_context * ctx);
|
||||||
struct llama_context * ctx
|
|
||||||
);
|
|
||||||
|
|
||||||
#endif // LLAMA_API_INTERNAL
|
#endif
|
||||||
|
|
||||||
#endif // LLAMA_H
|
#endif // LLAMA_H
|
||||||
|
8
examples/talk-llama/speak
Executable file → Normal file
8
examples/talk-llama/speak
Executable file → Normal file
@ -9,14 +9,6 @@
|
|||||||
#
|
#
|
||||||
#espeak -v en-us+m$1 -s 225 -p 50 -a 200 -g 5 -k 5 "$2"
|
#espeak -v en-us+m$1 -s 225 -p 50 -a 200 -g 5 -k 5 "$2"
|
||||||
|
|
||||||
# piper
|
|
||||||
#
|
|
||||||
# https://github.com/rhasspy/piper
|
|
||||||
#
|
|
||||||
# Tested with Linux:
|
|
||||||
#
|
|
||||||
#echo "$2" | piper --model ~/en_US-lessac-medium.onnx --output-raw | aplay -q -r 22050 -f S16_LE -t raw -
|
|
||||||
|
|
||||||
# for Mac
|
# for Mac
|
||||||
say "$2"
|
say "$2"
|
||||||
|
|
||||||
|
@ -14,37 +14,15 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
std::vector<llama_token> llama_tokenize(struct llama_context * ctx, const std::string & text, bool add_bos) {
|
std::vector<llama_token> llama_tokenize(struct llama_context * ctx, const std::string & text, bool add_bos) {
|
||||||
auto * model = llama_get_model(ctx);
|
// initialize to prompt numer of chars, since n_tokens <= n_prompt_chars
|
||||||
|
std::vector<llama_token> res(text.size() + (int)add_bos);
|
||||||
|
int n = llama_tokenize(ctx, text.c_str(), res.data(), res.size(), add_bos);
|
||||||
|
assert(n >= 0);
|
||||||
|
res.resize(n);
|
||||||
|
|
||||||
// upper limit for the number of tokens
|
return res;
|
||||||
int n_tokens = text.length() + add_bos;
|
|
||||||
std::vector<llama_token> result(n_tokens);
|
|
||||||
n_tokens = llama_tokenize(model, text.data(), text.length(), result.data(), result.size(), add_bos, false);
|
|
||||||
if (n_tokens < 0) {
|
|
||||||
result.resize(-n_tokens);
|
|
||||||
int check = llama_tokenize(model, text.data(), text.length(), result.data(), result.size(), add_bos, false);
|
|
||||||
GGML_ASSERT(check == -n_tokens);
|
|
||||||
} else {
|
|
||||||
result.resize(n_tokens);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string llama_token_to_piece(const struct llama_context * ctx, llama_token token) {
|
|
||||||
std::vector<char> result(8, 0);
|
|
||||||
const int n_tokens = llama_token_to_piece(llama_get_model(ctx), token, result.data(), result.size());
|
|
||||||
if (n_tokens < 0) {
|
|
||||||
result.resize(-n_tokens);
|
|
||||||
int check = llama_token_to_piece(llama_get_model(ctx), token, result.data(), result.size());
|
|
||||||
GGML_ASSERT(check == -n_tokens);
|
|
||||||
} else {
|
|
||||||
result.resize(n_tokens);
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::string(result.data(), result.size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// command-line parameters
|
// command-line parameters
|
||||||
@ -54,7 +32,6 @@ struct whisper_params {
|
|||||||
int32_t capture_id = -1;
|
int32_t capture_id = -1;
|
||||||
int32_t max_tokens = 32;
|
int32_t max_tokens = 32;
|
||||||
int32_t audio_ctx = 0;
|
int32_t audio_ctx = 0;
|
||||||
int32_t n_gpu_layers = 999;
|
|
||||||
|
|
||||||
float vad_thold = 0.6f;
|
float vad_thold = 0.6f;
|
||||||
float freq_thold = 100.0f;
|
float freq_thold = 100.0f;
|
||||||
@ -65,12 +42,8 @@ struct whisper_params {
|
|||||||
bool print_energy = false;
|
bool print_energy = false;
|
||||||
bool no_timestamps = true;
|
bool no_timestamps = true;
|
||||||
bool verbose_prompt = false;
|
bool verbose_prompt = false;
|
||||||
bool use_gpu = true;
|
|
||||||
|
|
||||||
std::string person = "Georgi";
|
std::string person = "Georgi";
|
||||||
std::string bot_name = "LLaMA";
|
|
||||||
std::string wake_cmd = "";
|
|
||||||
std::string heard_ok = "";
|
|
||||||
std::string language = "en";
|
std::string language = "en";
|
||||||
std::string model_wsp = "models/ggml-base.en.bin";
|
std::string model_wsp = "models/ggml-base.en.bin";
|
||||||
std::string model_llama = "models/ggml-llama-7B.bin";
|
std::string model_llama = "models/ggml-llama-7B.bin";
|
||||||
@ -95,20 +68,15 @@ bool whisper_params_parse(int argc, char ** argv, whisper_params & params) {
|
|||||||
else if (arg == "-c" || arg == "--capture") { params.capture_id = 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 == "-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 == "-ac" || arg == "--audio-ctx") { params.audio_ctx = std::stoi(argv[++i]); }
|
||||||
else if (arg == "-ngl" || arg == "--n-gpu-layers") { params.n_gpu_layers = std::stoi(argv[++i]); }
|
|
||||||
else if (arg == "-vth" || arg == "--vad-thold") { params.vad_thold = std::stof(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 == "-fth" || arg == "--freq-thold") { params.freq_thold = std::stof(argv[++i]); }
|
||||||
else if (arg == "-su" || arg == "--speed-up") { params.speed_up = true; }
|
else if (arg == "-su" || arg == "--speed-up") { params.speed_up = true; }
|
||||||
else if (arg == "-tr" || arg == "--translate") { params.translate = true; }
|
else if (arg == "-tr" || arg == "--translate") { params.translate = true; }
|
||||||
else if (arg == "-ps" || arg == "--print-special") { params.print_special = 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 == "-pe" || arg == "--print-energy") { params.print_energy = true; }
|
||||||
else if (arg == "-vp" || arg == "--verbose-prompt") { params.verbose_prompt = true; }
|
else if (arg == "--verbose-prompt") { params.verbose_prompt = true; }
|
||||||
else if (arg == "-ng" || arg == "--no-gpu") { params.use_gpu = false; }
|
|
||||||
else if (arg == "-p" || arg == "--person") { params.person = argv[++i]; }
|
else if (arg == "-p" || arg == "--person") { params.person = argv[++i]; }
|
||||||
else if (arg == "-bn" || arg == "--bot-name") { params.bot_name = argv[++i]; }
|
else if (arg == "--session") { params.path_session = argv[++i];}
|
||||||
else if (arg == "--session") { params.path_session = argv[++i]; }
|
|
||||||
else if (arg == "-w" || arg == "--wake-command") { params.wake_cmd = argv[++i]; }
|
|
||||||
else if (arg == "-ho" || arg == "--heard-ok") { params.heard_ok = argv[++i]; }
|
|
||||||
else if (arg == "-l" || arg == "--language") { params.language = argv[++i]; }
|
else if (arg == "-l" || arg == "--language") { params.language = argv[++i]; }
|
||||||
else if (arg == "-mw" || arg == "--model-whisper") { params.model_wsp = argv[++i]; }
|
else if (arg == "-mw" || arg == "--model-whisper") { params.model_wsp = argv[++i]; }
|
||||||
else if (arg == "-ml" || arg == "--model-llama") { params.model_llama = argv[++i]; }
|
else if (arg == "-ml" || arg == "--model-llama") { params.model_llama = argv[++i]; }
|
||||||
@ -121,7 +89,6 @@ bool whisper_params_parse(int argc, char ** argv, whisper_params & params) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (arg == "-f" || arg == "--file") { params.fname_out = argv[++i]; }
|
else if (arg == "-f" || arg == "--file") { params.fname_out = argv[++i]; }
|
||||||
else if (arg == "-ng" || arg == "--no-gpu") { params.use_gpu = false; }
|
|
||||||
else {
|
else {
|
||||||
fprintf(stderr, "error: unknown argument: %s\n", arg.c_str());
|
fprintf(stderr, "error: unknown argument: %s\n", arg.c_str());
|
||||||
whisper_print_usage(argc, argv, params);
|
whisper_print_usage(argc, argv, params);
|
||||||
@ -143,25 +110,20 @@ void whisper_print_usage(int /*argc*/, char ** argv, const whisper_params & para
|
|||||||
fprintf(stderr, " -c ID, --capture ID [%-7d] capture device ID\n", params.capture_id);
|
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, " -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, " -ac N, --audio-ctx N [%-7d] audio context size (0 - all)\n", params.audio_ctx);
|
||||||
fprintf(stderr, " -ngl N, --n-gpu-layers N [%-7d] number of layers to store in VRAM\n", params.n_gpu_layers);
|
|
||||||
fprintf(stderr, " -vth N, --vad-thold N [%-7.2f] voice activity detection threshold\n", params.vad_thold);
|
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, " -fth N, --freq-thold N [%-7.2f] high-pass frequency cutoff\n", params.freq_thold);
|
||||||
fprintf(stderr, " -su, --speed-up [%-7s] speed up audio by x2 (reduced accuracy)\n", params.speed_up ? "true" : "false");
|
fprintf(stderr, " -su, --speed-up [%-7s] speed up audio by x2 (reduced accuracy)\n", params.speed_up ? "true" : "false");
|
||||||
fprintf(stderr, " -tr, --translate [%-7s] translate from source language to english\n", params.translate ? "true" : "false");
|
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, " -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, " -pe, --print-energy [%-7s] print sound energy (for debugging)\n", params.print_energy ? "true" : "false");
|
||||||
fprintf(stderr, " -vp, --verbose-prompt [%-7s] print prompt at start\n", params.verbose_prompt ? "true" : "false");
|
|
||||||
fprintf(stderr, " -ng, --no-gpu [%-7s] disable GPU\n", params.use_gpu ? "false" : "true");
|
|
||||||
fprintf(stderr, " -p NAME, --person NAME [%-7s] person name (for prompt selection)\n", params.person.c_str());
|
fprintf(stderr, " -p NAME, --person NAME [%-7s] person name (for prompt selection)\n", params.person.c_str());
|
||||||
fprintf(stderr, " -bn NAME, --bot-name NAME [%-7s] bot name (to display)\n", params.bot_name.c_str());
|
|
||||||
fprintf(stderr, " -w TEXT, --wake-command T [%-7s] wake-up command to listen for\n", params.wake_cmd.c_str());
|
|
||||||
fprintf(stderr, " -ho TEXT, --heard-ok TEXT [%-7s] said by TTS before generating reply\n", params.heard_ok.c_str());
|
|
||||||
fprintf(stderr, " -l LANG, --language LANG [%-7s] spoken language\n", params.language.c_str());
|
fprintf(stderr, " -l LANG, --language LANG [%-7s] spoken language\n", params.language.c_str());
|
||||||
fprintf(stderr, " -mw FILE, --model-whisper [%-7s] whisper model file\n", params.model_wsp.c_str());
|
fprintf(stderr, " -mw FILE, --model-whisper [%-7s] whisper model file\n", params.model_wsp.c_str());
|
||||||
fprintf(stderr, " -ml FILE, --model-llama [%-7s] llama model file\n", params.model_llama.c_str());
|
fprintf(stderr, " -ml FILE, --model-llama [%-7s] llama model file\n", params.model_llama.c_str());
|
||||||
fprintf(stderr, " -s FILE, --speak TEXT [%-7s] command for TTS\n", params.speak.c_str());
|
fprintf(stderr, " -s FILE, --speak TEXT [%-7s] command for TTS\n", params.speak.c_str());
|
||||||
fprintf(stderr, " --prompt-file FNAME [%-7s] file with custom prompt to start dialog\n", "");
|
fprintf(stderr, " --prompt-file FNAME [%-7s] file with custom prompt to start dialog\n", "");
|
||||||
fprintf(stderr, " --session FNAME file to cache model state in (may be large!) (default: none)\n");
|
fprintf(stderr, " --session FNAME file to cache model state in (may be large!) (default: none)\n");
|
||||||
|
fprintf(stderr, " --verbose-prompt [%-7s] print prompt at start\n", params.verbose_prompt ? "true" : "false");
|
||||||
fprintf(stderr, " -f FNAME, --file FNAME [%-7s] text output file name\n", params.fname_out.c_str());
|
fprintf(stderr, " -f FNAME, --file FNAME [%-7s] text output file name\n", params.fname_out.c_str());
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
}
|
}
|
||||||
@ -234,18 +196,6 @@ std::string transcribe(
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> get_words(const std::string &txt) {
|
|
||||||
std::vector<std::string> words;
|
|
||||||
|
|
||||||
std::istringstream iss(txt);
|
|
||||||
std::string word;
|
|
||||||
while (iss >> word) {
|
|
||||||
words.push_back(word);
|
|
||||||
}
|
|
||||||
|
|
||||||
return words;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string k_prompt_whisper = R"(A conversation with a person called {1}.)";
|
const std::string k_prompt_whisper = R"(A conversation with a person called {1}.)";
|
||||||
|
|
||||||
const std::string k_prompt_llama = R"(Text transcript of a never ending dialog, where {0} interacts with an AI assistant named {1}.
|
const std::string k_prompt_llama = R"(Text transcript of a never ending dialog, where {0} interacts with an AI assistant named {1}.
|
||||||
@ -273,7 +223,7 @@ int main(int argc, char ** argv) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.language != "auto" && whisper_lang_id(params.language.c_str()) == -1) {
|
if (whisper_lang_id(params.language.c_str()) == -1) {
|
||||||
fprintf(stderr, "error: unknown language '%s'\n", params.language.c_str());
|
fprintf(stderr, "error: unknown language '%s'\n", params.language.c_str());
|
||||||
whisper_print_usage(argc, argv, params);
|
whisper_print_usage(argc, argv, params);
|
||||||
exit(0);
|
exit(0);
|
||||||
@ -281,32 +231,20 @@ int main(int argc, char ** argv) {
|
|||||||
|
|
||||||
// whisper init
|
// whisper init
|
||||||
|
|
||||||
struct whisper_context_params cparams = whisper_context_default_params();
|
struct whisper_context * ctx_wsp = whisper_init_from_file(params.model_wsp.c_str());
|
||||||
cparams.use_gpu = params.use_gpu;
|
|
||||||
|
|
||||||
struct whisper_context * ctx_wsp = whisper_init_from_file_with_params(params.model_wsp.c_str(), cparams);
|
|
||||||
|
|
||||||
// llama init
|
// llama init
|
||||||
|
|
||||||
llama_backend_init(true);
|
llama_init_backend();
|
||||||
|
|
||||||
auto lmparams = llama_model_default_params();
|
auto lparams = llama_context_default_params();
|
||||||
if (!params.use_gpu) {
|
|
||||||
lmparams.n_gpu_layers = 0;
|
|
||||||
} else {
|
|
||||||
lmparams.n_gpu_layers = params.n_gpu_layers;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct llama_model * model_llama = llama_load_model_from_file(params.model_llama.c_str(), lmparams);
|
|
||||||
|
|
||||||
llama_context_params lcparams = llama_context_default_params();
|
|
||||||
|
|
||||||
// tune these to your liking
|
// tune these to your liking
|
||||||
lcparams.n_ctx = 2048;
|
lparams.n_ctx = 2048;
|
||||||
lcparams.seed = 1;
|
lparams.seed = 1;
|
||||||
lcparams.n_threads = params.n_threads;
|
lparams.f16_kv = true;
|
||||||
|
|
||||||
struct llama_context * ctx_llama = llama_new_context_with_model(model_llama, lcparams);
|
struct llama_context * ctx_llama = llama_init_from_file(params.model_llama.c_str(), lparams);
|
||||||
|
|
||||||
// print some info about the processing
|
// print some info about the processing
|
||||||
{
|
{
|
||||||
@ -329,6 +267,7 @@ int main(int argc, char ** argv) {
|
|||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// init audio
|
// init audio
|
||||||
|
|
||||||
audio_async audio(30*1000);
|
audio_async audio(30*1000);
|
||||||
@ -339,17 +278,20 @@ int main(int argc, char ** argv) {
|
|||||||
|
|
||||||
audio.resume();
|
audio.resume();
|
||||||
|
|
||||||
|
int n_iter = 0;
|
||||||
|
|
||||||
bool is_running = true;
|
bool is_running = true;
|
||||||
bool force_speak = false;
|
bool force_speak = false;
|
||||||
|
|
||||||
float prob0 = 0.0f;
|
float prob0 = 0.0f;
|
||||||
|
|
||||||
const std::string chat_symb = ":";
|
const std::string chat_symb = ":";
|
||||||
|
const std::string bot_name = "LLaMA";
|
||||||
|
|
||||||
std::vector<float> pcmf32_cur;
|
std::vector<float> pcmf32_cur;
|
||||||
std::vector<float> pcmf32_prompt;
|
std::vector<float> pcmf32_prompt;
|
||||||
|
|
||||||
const std::string prompt_whisper = ::replace(k_prompt_whisper, "{1}", params.bot_name);
|
const std::string prompt_whisper = ::replace(k_prompt_whisper, "{1}", bot_name);
|
||||||
|
|
||||||
// construct the initial prompt for LLaMA inference
|
// construct the initial prompt for LLaMA inference
|
||||||
std::string prompt_llama = params.prompt.empty() ? k_prompt_llama : params.prompt;
|
std::string prompt_llama = params.prompt.empty() ? k_prompt_llama : params.prompt;
|
||||||
@ -358,7 +300,7 @@ int main(int argc, char ** argv) {
|
|||||||
prompt_llama.insert(0, 1, ' ');
|
prompt_llama.insert(0, 1, ' ');
|
||||||
|
|
||||||
prompt_llama = ::replace(prompt_llama, "{0}", params.person);
|
prompt_llama = ::replace(prompt_llama, "{0}", params.person);
|
||||||
prompt_llama = ::replace(prompt_llama, "{1}", params.bot_name);
|
prompt_llama = ::replace(prompt_llama, "{1}", bot_name);
|
||||||
|
|
||||||
{
|
{
|
||||||
// get time string
|
// get time string
|
||||||
@ -401,7 +343,7 @@ int main(int argc, char ** argv) {
|
|||||||
if (fp != NULL) {
|
if (fp != NULL) {
|
||||||
std::fclose(fp);
|
std::fclose(fp);
|
||||||
|
|
||||||
session_tokens.resize(llama_n_ctx(ctx_llama));
|
session_tokens.resize(lparams.n_ctx);
|
||||||
size_t n_token_count_out = 0;
|
size_t n_token_count_out = 0;
|
||||||
if (!llama_load_session_file(ctx_llama, path_session.c_str(), session_tokens.data(), session_tokens.capacity(), &n_token_count_out)) {
|
if (!llama_load_session_file(ctx_llama, path_session.c_str(), session_tokens.data(), session_tokens.capacity(), &n_token_count_out)) {
|
||||||
fprintf(stderr, "%s: error: failed to load session file '%s'\n", __func__, path_session.c_str());
|
fprintf(stderr, "%s: error: failed to load session file '%s'\n", __func__, path_session.c_str());
|
||||||
@ -423,7 +365,7 @@ int main(int argc, char ** argv) {
|
|||||||
printf("\n");
|
printf("\n");
|
||||||
printf("%s : initializing - please wait ...\n", __func__);
|
printf("%s : initializing - please wait ...\n", __func__);
|
||||||
|
|
||||||
if (llama_eval(ctx_llama, embd_inp.data(), embd_inp.size(), 0)) {
|
if (llama_eval(ctx_llama, embd_inp.data(), embd_inp.size(), 0, params.n_threads)) {
|
||||||
fprintf(stderr, "%s : failed to eval\n", __func__);
|
fprintf(stderr, "%s : failed to eval\n", __func__);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -460,16 +402,6 @@ int main(int argc, char ** argv) {
|
|||||||
bool need_to_save_session = !path_session.empty() && n_matching_session_tokens < (embd_inp.size() * 3 / 4);
|
bool need_to_save_session = !path_session.empty() && n_matching_session_tokens < (embd_inp.size() * 3 / 4);
|
||||||
|
|
||||||
printf("%s : done! start speaking in the microphone\n", __func__);
|
printf("%s : done! start speaking in the microphone\n", __func__);
|
||||||
|
|
||||||
// show wake command if enabled
|
|
||||||
const std::string wake_cmd = params.wake_cmd;
|
|
||||||
const int wake_cmd_length = get_words(wake_cmd).size();
|
|
||||||
const bool use_wake_cmd = wake_cmd_length > 0;
|
|
||||||
|
|
||||||
if (use_wake_cmd) {
|
|
||||||
printf("%s : the wake-up command is: '%s%s%s'\n", __func__, "\033[1m", wake_cmd.c_str(), "\033[0m");
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("\n");
|
printf("\n");
|
||||||
printf("%s%s", params.person.c_str(), chat_symb.c_str());
|
printf("%s%s", params.person.c_str(), chat_symb.c_str());
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
@ -515,41 +447,10 @@ int main(int argc, char ** argv) {
|
|||||||
|
|
||||||
audio.get(params.voice_ms, pcmf32_cur);
|
audio.get(params.voice_ms, pcmf32_cur);
|
||||||
|
|
||||||
std::string all_heard;
|
|
||||||
|
|
||||||
if (!force_speak) {
|
|
||||||
all_heard = ::trim(::transcribe(ctx_wsp, params, pcmf32_cur, prompt_whisper, prob0, t_ms));
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto words = get_words(all_heard);
|
|
||||||
|
|
||||||
std::string wake_cmd_heard;
|
|
||||||
std::string text_heard;
|
std::string text_heard;
|
||||||
|
|
||||||
for (int i = 0; i < (int) words.size(); ++i) {
|
if (!force_speak) {
|
||||||
if (i < wake_cmd_length) {
|
text_heard = ::trim(::transcribe(ctx_wsp, params, pcmf32_cur, prompt_whisper, prob0, t_ms));
|
||||||
wake_cmd_heard += words[i] + " ";
|
|
||||||
} else {
|
|
||||||
text_heard += words[i] + " ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if audio starts with the wake-up command if enabled
|
|
||||||
if (use_wake_cmd) {
|
|
||||||
const float sim = similarity(wake_cmd_heard, wake_cmd);
|
|
||||||
|
|
||||||
if ((sim < 0.7f) || (text_heard.empty())) {
|
|
||||||
audio.clear();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// optionally give audio feedback that the current text is being processed
|
|
||||||
if (!params.heard_ok.empty()) {
|
|
||||||
int ret = system((params.speak + " " + std::to_string(voice_id) + " '" + params.heard_ok + "'").c_str());
|
|
||||||
if (ret != 0) {
|
|
||||||
fprintf(stderr, "%s: failed to speak\n", __func__);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove text between brackets using regex
|
// remove text between brackets using regex
|
||||||
@ -586,7 +487,7 @@ int main(int argc, char ** argv) {
|
|||||||
force_speak = false;
|
force_speak = false;
|
||||||
|
|
||||||
text_heard.insert(0, 1, ' ');
|
text_heard.insert(0, 1, ' ');
|
||||||
text_heard += "\n" + params.bot_name + chat_symb;
|
text_heard += "\n" + bot_name + chat_symb;
|
||||||
fprintf(stdout, "%s%s%s", "\033[1m", text_heard.c_str(), "\033[0m");
|
fprintf(stdout, "%s%s%s", "\033[1m", text_heard.c_str(), "\033[0m");
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
|
||||||
@ -613,7 +514,7 @@ int main(int argc, char ** argv) {
|
|||||||
//printf("\n---\n");
|
//printf("\n---\n");
|
||||||
//printf("resetting: '");
|
//printf("resetting: '");
|
||||||
//for (int i = 0; i < (int) embd.size(); i++) {
|
//for (int i = 0; i < (int) embd.size(); i++) {
|
||||||
// printf("%s", llama_token_to_piece(ctx_llama, embd[i]));
|
// printf("%s", llama_token_to_str(ctx_llama, embd[i]));
|
||||||
//}
|
//}
|
||||||
//printf("'\n");
|
//printf("'\n");
|
||||||
//printf("\n---\n");
|
//printf("\n---\n");
|
||||||
@ -647,7 +548,7 @@ int main(int argc, char ** argv) {
|
|||||||
n_session_consumed = session_tokens.size();
|
n_session_consumed = session_tokens.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (llama_eval(ctx_llama, embd.data(), embd.size(), n_past)) {
|
if (llama_eval(ctx_llama, embd.data(), embd.size(), n_past, params.n_threads)) {
|
||||||
fprintf(stderr, "%s : failed to eval\n", __func__);
|
fprintf(stderr, "%s : failed to eval\n", __func__);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -679,9 +580,9 @@ int main(int argc, char ** argv) {
|
|||||||
|
|
||||||
{
|
{
|
||||||
auto logits = llama_get_logits(ctx_llama);
|
auto logits = llama_get_logits(ctx_llama);
|
||||||
auto n_vocab = llama_n_vocab(model_llama);
|
auto n_vocab = llama_n_vocab(ctx_llama);
|
||||||
|
|
||||||
logits[llama_token_eos(model_llama)] = 0;
|
logits[llama_token_eos()] = 0;
|
||||||
|
|
||||||
std::vector<llama_token_data> candidates;
|
std::vector<llama_token_data> candidates;
|
||||||
candidates.reserve(n_vocab);
|
candidates.reserve(n_vocab);
|
||||||
@ -692,13 +593,13 @@ int main(int argc, char ** argv) {
|
|||||||
llama_token_data_array candidates_p = { candidates.data(), candidates.size(), false };
|
llama_token_data_array candidates_p = { candidates.data(), candidates.size(), false };
|
||||||
|
|
||||||
// apply repeat penalty
|
// apply repeat penalty
|
||||||
const float nl_logit = logits[llama_token_nl(model_llama)];
|
const float nl_logit = logits[llama_token_nl()];
|
||||||
|
|
||||||
llama_sample_repetition_penalties(ctx_llama, &candidates_p,
|
llama_sample_repetition_penalty(ctx_llama, &candidates_p,
|
||||||
embd_inp.data() + std::max(0, n_past - repeat_last_n),
|
embd_inp.data() + std::max(0, n_past - repeat_last_n),
|
||||||
repeat_last_n, repeat_penalty, 0.0, 0.0f);
|
repeat_last_n, repeat_penalty);
|
||||||
|
|
||||||
logits[llama_token_nl(model_llama)] = nl_logit;
|
logits[llama_token_nl()] = nl_logit;
|
||||||
|
|
||||||
if (temp <= 0) {
|
if (temp <= 0) {
|
||||||
// Greedy sampling
|
// Greedy sampling
|
||||||
@ -707,28 +608,27 @@ int main(int argc, char ** argv) {
|
|||||||
// Temperature sampling
|
// Temperature sampling
|
||||||
llama_sample_top_k(ctx_llama, &candidates_p, top_k, 1);
|
llama_sample_top_k(ctx_llama, &candidates_p, top_k, 1);
|
||||||
llama_sample_top_p(ctx_llama, &candidates_p, top_p, 1);
|
llama_sample_top_p(ctx_llama, &candidates_p, top_p, 1);
|
||||||
llama_sample_temp (ctx_llama, &candidates_p, temp);
|
llama_sample_temperature(ctx_llama, &candidates_p, temp);
|
||||||
id = llama_sample_token(ctx_llama, &candidates_p);
|
id = llama_sample_token(ctx_llama, &candidates_p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id != llama_token_eos(model_llama)) {
|
if (id != llama_token_eos()) {
|
||||||
// add it to the context
|
// add it to the context
|
||||||
embd.push_back(id);
|
embd.push_back(id);
|
||||||
|
|
||||||
text_to_speak += llama_token_to_piece(ctx_llama, id);
|
text_to_speak += llama_token_to_str(ctx_llama, id);
|
||||||
|
|
||||||
printf("%s", llama_token_to_piece(ctx_llama, id).c_str());
|
printf("%s", llama_token_to_str(ctx_llama, id));
|
||||||
fflush(stdout);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::string last_output;
|
std::string last_output;
|
||||||
for (int i = embd_inp.size() - 16; i < (int) embd_inp.size(); i++) {
|
for (int i = embd_inp.size() - 16; i < (int) embd_inp.size(); i++) {
|
||||||
last_output += llama_token_to_piece(ctx_llama, embd_inp[i]);
|
last_output += llama_token_to_str(ctx_llama, embd_inp[i]);
|
||||||
}
|
}
|
||||||
last_output += llama_token_to_piece(ctx_llama, embd[0]);
|
last_output += llama_token_to_str(ctx_llama, embd[0]);
|
||||||
|
|
||||||
for (std::string & antiprompt : antiprompts) {
|
for (std::string & antiprompt : antiprompts) {
|
||||||
if (last_output.find(antiprompt.c_str(), last_output.length() - antiprompt.length(), antiprompt.length()) != std::string::npos) {
|
if (last_output.find(antiprompt.c_str(), last_output.length() - antiprompt.length(), antiprompt.length()) != std::string::npos) {
|
||||||
@ -748,13 +648,15 @@ int main(int argc, char ** argv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
text_to_speak = ::replace(text_to_speak, "'", "'\"'\"'");
|
text_to_speak = ::replace(text_to_speak, "\"", "");
|
||||||
int ret = system((params.speak + " " + std::to_string(voice_id) + " '" + text_to_speak + "'").c_str());
|
int ret = system((params.speak + " " + std::to_string(voice_id) + " \"" + text_to_speak + "\"").c_str());
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
fprintf(stderr, "%s: failed to speak\n", __func__);
|
fprintf(stderr, "%s: failed to speak\n", __func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
audio.clear();
|
audio.clear();
|
||||||
|
|
||||||
|
++n_iter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,463 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
static const std::vector<std::pair<uint32_t, uint32_t>> digit_ranges = {
|
|
||||||
{0x30, 0x39}, {0xB2, 0xB3}, {0xB9, 0xB9}, {0x660, 0x669}, {0x6F0, 0x6F9}, {0x7C0, 0x7C9}, {0x966, 0x96F}, {0x9E6, 0x9EF}, {0xA66, 0xA6F}, {0xAE6, 0xAEF}, {0xB66, 0xB6F}, {0xBE6, 0xBEF}, {0xC66, 0xC6F},
|
|
||||||
{0xCE6, 0xCEF}, {0xD66, 0xD6F}, {0xDE6, 0xDEF}, {0xE50, 0xE59}, {0xED0, 0xED9}, {0xF20, 0xF29}, {0x1040, 0x1049}, {0x1090, 0x1099}, {0x1369, 0x1371}, {0x17E0, 0x17E9}, {0x1810, 0x1819}, {0x1946, 0x194F},
|
|
||||||
{0x19D0, 0x19DA}, {0x1A80, 0x1A89}, {0x1A90, 0x1A99}, {0x1B50, 0x1B59}, {0x1BB0, 0x1BB9}, {0x1C40, 0x1C49}, {0x1C50, 0x1C59}, {0x2070, 0x2070}, {0x2074, 0x2079}, {0x2080, 0x2089}, {0x2460, 0x2468},
|
|
||||||
{0x2474, 0x247C}, {0x2488, 0x2490}, {0x24EA, 0x24EA}, {0x24F5, 0x24FD}, {0x24FF, 0x24FF}, {0x2776, 0x277E}, {0x2780, 0x2788}, {0x278A, 0x2792}, {0xA620, 0xA629}, {0xA8D0, 0xA8D9}, {0xA900, 0xA909},
|
|
||||||
{0xA9D0, 0xA9D9}, {0xA9F0, 0xA9F9}, {0xAA50, 0xAA59}, {0xABF0, 0xABF9}, {0xFF10, 0xFF19}, {0x104A0, 0x104A9}, {0x10A40, 0x10A43}, {0x10D30, 0x10D39}, {0x10E60, 0x10E68}, {0x11052, 0x1105A},
|
|
||||||
{0x11066, 0x1106F}, {0x110F0, 0x110F9}, {0x11136, 0x1113F}, {0x111D0, 0x111D9}, {0x112F0, 0x112F9}, {0x11450, 0x11459}, {0x114D0, 0x114D9}, {0x11650, 0x11659}, {0x116C0, 0x116C9}, {0x11730, 0x11739},
|
|
||||||
{0x118E0, 0x118E9}, {0x11950, 0x11959}, {0x11C50, 0x11C59}, {0x11D50, 0x11D59}, {0x11DA0, 0x11DA9}, {0x16A60, 0x16A69}, {0x16B50, 0x16B59}, {0x1D7CE, 0x1D7FF}, {0x1E140, 0x1E149}, {0x1E2F0, 0x1E2F9},
|
|
||||||
{0x1E950, 0x1E959}, {0x1F100, 0x1F10A}, {0x1FBF0, 0x1FBF9},
|
|
||||||
};
|
|
||||||
|
|
||||||
static const std::vector<std::pair<uint32_t, uint32_t>> letter_ranges = {
|
|
||||||
{0x41, 0x5A}, {0x61, 0x7A}, {0xAA, 0xAA}, {0xB5, 0xB5}, {0xBA, 0xBA}, {0xC0, 0xD6}, {0xD8, 0xF6}, {0xF8, 0x2C1}, {0x2C6, 0x2D1}, {0x2E0, 0x2E4}, {0x2EC, 0x2EC}, {0x2EE, 0x2EE}, {0x370, 0x374},
|
|
||||||
{0x376, 0x377}, {0x37A, 0x37D}, {0x37F, 0x37F}, {0x386, 0x386}, {0x388, 0x38A}, {0x38C, 0x38C}, {0x38E, 0x3A1}, {0x3A3, 0x3F5}, {0x3F7, 0x481}, {0x48A, 0x52F}, {0x531, 0x556}, {0x559, 0x559},
|
|
||||||
{0x560, 0x588}, {0x5D0, 0x5EA}, {0x5EF, 0x5F2}, {0x620, 0x64A}, {0x66E, 0x66F}, {0x671, 0x6D3}, {0x6D5, 0x6D5}, {0x6E5, 0x6E6}, {0x6EE, 0x6EF}, {0x6FA, 0x6FC}, {0x6FF, 0x6FF}, {0x710, 0x710},
|
|
||||||
{0x712, 0x72F}, {0x74D, 0x7A5}, {0x7B1, 0x7B1}, {0x7CA, 0x7EA}, {0x7F4, 0x7F5}, {0x7FA, 0x7FA}, {0x800, 0x815}, {0x81A, 0x81A}, {0x824, 0x824}, {0x828, 0x828}, {0x840, 0x858}, {0x860, 0x86A},
|
|
||||||
{0x8A0, 0x8B4}, {0x8B6, 0x8C7}, {0x904, 0x939}, {0x93D, 0x93D}, {0x950, 0x950}, {0x958, 0x961}, {0x971, 0x980}, {0x985, 0x98C}, {0x98F, 0x990}, {0x993, 0x9A8}, {0x9AA, 0x9B0}, {0x9B2, 0x9B2},
|
|
||||||
{0x9B6, 0x9B9}, {0x9BD, 0x9BD}, {0x9CE, 0x9CE}, {0x9DC, 0x9DD}, {0x9DF, 0x9E1}, {0x9F0, 0x9F1}, {0x9FC, 0x9FC}, {0xA05, 0xA0A}, {0xA0F, 0xA10}, {0xA13, 0xA28}, {0xA2A, 0xA30}, {0xA32, 0xA33},
|
|
||||||
{0xA35, 0xA36}, {0xA38, 0xA39}, {0xA59, 0xA5C}, {0xA5E, 0xA5E}, {0xA72, 0xA74}, {0xA85, 0xA8D}, {0xA8F, 0xA91}, {0xA93, 0xAA8}, {0xAAA, 0xAB0}, {0xAB2, 0xAB3}, {0xAB5, 0xAB9}, {0xABD, 0xABD},
|
|
||||||
{0xAD0, 0xAD0}, {0xAE0, 0xAE1}, {0xAF9, 0xAF9}, {0xB05, 0xB0C}, {0xB0F, 0xB10}, {0xB13, 0xB28}, {0xB2A, 0xB30}, {0xB32, 0xB33}, {0xB35, 0xB39}, {0xB3D, 0xB3D}, {0xB5C, 0xB5D}, {0xB5F, 0xB61},
|
|
||||||
{0xB71, 0xB71}, {0xB83, 0xB83}, {0xB85, 0xB8A}, {0xB8E, 0xB90}, {0xB92, 0xB95}, {0xB99, 0xB9A}, {0xB9C, 0xB9C}, {0xB9E, 0xB9F}, {0xBA3, 0xBA4}, {0xBA8, 0xBAA}, {0xBAE, 0xBB9}, {0xBD0, 0xBD0},
|
|
||||||
{0xC05, 0xC0C}, {0xC0E, 0xC10}, {0xC12, 0xC28}, {0xC2A, 0xC39}, {0xC3D, 0xC3D}, {0xC58, 0xC5A}, {0xC60, 0xC61}, {0xC80, 0xC80}, {0xC85, 0xC8C}, {0xC8E, 0xC90}, {0xC92, 0xCA8}, {0xCAA, 0xCB3},
|
|
||||||
{0xCB5, 0xCB9}, {0xCBD, 0xCBD}, {0xCDE, 0xCDE}, {0xCE0, 0xCE1}, {0xCF1, 0xCF2}, {0xD04, 0xD0C}, {0xD0E, 0xD10}, {0xD12, 0xD3A}, {0xD3D, 0xD3D}, {0xD4E, 0xD4E}, {0xD54, 0xD56}, {0xD5F, 0xD61},
|
|
||||||
{0xD7A, 0xD7F}, {0xD85, 0xD96}, {0xD9A, 0xDB1}, {0xDB3, 0xDBB}, {0xDBD, 0xDBD}, {0xDC0, 0xDC6}, {0xE01, 0xE30}, {0xE32, 0xE33}, {0xE40, 0xE46}, {0xE81, 0xE82}, {0xE84, 0xE84}, {0xE86, 0xE8A},
|
|
||||||
{0xE8C, 0xEA3}, {0xEA5, 0xEA5}, {0xEA7, 0xEB0}, {0xEB2, 0xEB3}, {0xEBD, 0xEBD}, {0xEC0, 0xEC4}, {0xEC6, 0xEC6}, {0xEDC, 0xEDF}, {0xF00, 0xF00}, {0xF40, 0xF47}, {0xF49, 0xF6C}, {0xF88, 0xF8C},
|
|
||||||
{0x1000, 0x102A}, {0x103F, 0x103F}, {0x1050, 0x1055}, {0x105A, 0x105D}, {0x1061, 0x1061}, {0x1065, 0x1066}, {0x106E, 0x1070}, {0x1075, 0x1081}, {0x108E, 0x108E}, {0x10A0, 0x10C5}, {0x10C7, 0x10C7},
|
|
||||||
{0x10CD, 0x10CD}, {0x10D0, 0x10FA}, {0x10FC, 0x1248}, {0x124A, 0x124D}, {0x1250, 0x1256}, {0x1258, 0x1258}, {0x125A, 0x125D}, {0x1260, 0x1288}, {0x128A, 0x128D}, {0x1290, 0x12B0}, {0x12B2, 0x12B5},
|
|
||||||
{0x12B8, 0x12BE}, {0x12C0, 0x12C0}, {0x12C2, 0x12C5}, {0x12C8, 0x12D6}, {0x12D8, 0x1310}, {0x1312, 0x1315}, {0x1318, 0x135A}, {0x1380, 0x138F}, {0x13A0, 0x13F5}, {0x13F8, 0x13FD}, {0x1401, 0x166C},
|
|
||||||
{0x166F, 0x167F}, {0x1681, 0x169A}, {0x16A0, 0x16EA}, {0x16F1, 0x16F8}, {0x1700, 0x170C}, {0x170E, 0x1711}, {0x1720, 0x1731}, {0x1740, 0x1751}, {0x1760, 0x176C}, {0x176E, 0x1770}, {0x1780, 0x17B3},
|
|
||||||
{0x17D7, 0x17D7}, {0x17DC, 0x17DC}, {0x1820, 0x1878}, {0x1880, 0x1884}, {0x1887, 0x18A8}, {0x18AA, 0x18AA}, {0x18B0, 0x18F5}, {0x1900, 0x191E}, {0x1950, 0x196D}, {0x1970, 0x1974}, {0x1980, 0x19AB},
|
|
||||||
{0x19B0, 0x19C9}, {0x1A00, 0x1A16}, {0x1A20, 0x1A54}, {0x1AA7, 0x1AA7}, {0x1B05, 0x1B33}, {0x1B45, 0x1B4B}, {0x1B83, 0x1BA0}, {0x1BAE, 0x1BAF}, {0x1BBA, 0x1BE5}, {0x1C00, 0x1C23}, {0x1C4D, 0x1C4F},
|
|
||||||
{0x1C5A, 0x1C7D}, {0x1C80, 0x1C88}, {0x1C90, 0x1CBA}, {0x1CBD, 0x1CBF}, {0x1CE9, 0x1CEC}, {0x1CEE, 0x1CF3}, {0x1CF5, 0x1CF6}, {0x1CFA, 0x1CFA}, {0x1D00, 0x1DBF}, {0x1E00, 0x1F15}, {0x1F18, 0x1F1D},
|
|
||||||
{0x1F20, 0x1F45}, {0x1F48, 0x1F4D}, {0x1F50, 0x1F57}, {0x1F59, 0x1F59}, {0x1F5B, 0x1F5B}, {0x1F5D, 0x1F5D}, {0x1F5F, 0x1F7D}, {0x1F80, 0x1FB4}, {0x1FB6, 0x1FBC}, {0x1FBE, 0x1FBE}, {0x1FC2, 0x1FC4},
|
|
||||||
{0x1FC6, 0x1FCC}, {0x1FD0, 0x1FD3}, {0x1FD6, 0x1FDB}, {0x1FE0, 0x1FEC}, {0x1FF2, 0x1FF4}, {0x1FF6, 0x1FFC}, {0x2071, 0x2071}, {0x207F, 0x207F}, {0x2090, 0x209C}, {0x2102, 0x2102}, {0x2107, 0x2107},
|
|
||||||
{0x210A, 0x2113}, {0x2115, 0x2115}, {0x2119, 0x211D}, {0x2124, 0x2124}, {0x2126, 0x2126}, {0x2128, 0x2128}, {0x212A, 0x212D}, {0x212F, 0x2139}, {0x213C, 0x213F}, {0x2145, 0x2149}, {0x214E, 0x214E},
|
|
||||||
{0x2183, 0x2184}, {0x2C00, 0x2C2E}, {0x2C30, 0x2C5E}, {0x2C60, 0x2CE4}, {0x2CEB, 0x2CEE}, {0x2CF2, 0x2CF3}, {0x2D00, 0x2D25}, {0x2D27, 0x2D27}, {0x2D2D, 0x2D2D}, {0x2D30, 0x2D67}, {0x2D6F, 0x2D6F},
|
|
||||||
{0x2D80, 0x2D96}, {0x2DA0, 0x2DA6}, {0x2DA8, 0x2DAE}, {0x2DB0, 0x2DB6}, {0x2DB8, 0x2DBE}, {0x2DC0, 0x2DC6}, {0x2DC8, 0x2DCE}, {0x2DD0, 0x2DD6}, {0x2DD8, 0x2DDE}, {0x2E2F, 0x2E2F}, {0x3005, 0x3006},
|
|
||||||
{0x3031, 0x3035}, {0x303B, 0x303C}, {0x3041, 0x3096}, {0x309D, 0x309F}, {0x30A1, 0x30FA}, {0x30FC, 0x30FF}, {0x3105, 0x312F}, {0x3131, 0x318E}, {0x31A0, 0x31BF}, {0x31F0, 0x31FF}, {0x3400, 0x4DBF},
|
|
||||||
{0x4E00, 0x9FFC}, {0xA000, 0xA48C}, {0xA4D0, 0xA4FD}, {0xA500, 0xA60C}, {0xA610, 0xA61F}, {0xA62A, 0xA62B}, {0xA640, 0xA66E}, {0xA67F, 0xA69D}, {0xA6A0, 0xA6E5}, {0xA717, 0xA71F}, {0xA722, 0xA788},
|
|
||||||
{0xA78B, 0xA7BF}, {0xA7C2, 0xA7CA}, {0xA7F5, 0xA801}, {0xA803, 0xA805}, {0xA807, 0xA80A}, {0xA80C, 0xA822}, {0xA840, 0xA873}, {0xA882, 0xA8B3}, {0xA8F2, 0xA8F7}, {0xA8FB, 0xA8FB}, {0xA8FD, 0xA8FE},
|
|
||||||
{0xA90A, 0xA925}, {0xA930, 0xA946}, {0xA960, 0xA97C}, {0xA984, 0xA9B2}, {0xA9CF, 0xA9CF}, {0xA9E0, 0xA9E4}, {0xA9E6, 0xA9EF}, {0xA9FA, 0xA9FE}, {0xAA00, 0xAA28}, {0xAA40, 0xAA42}, {0xAA44, 0xAA4B},
|
|
||||||
{0xAA60, 0xAA76}, {0xAA7A, 0xAA7A}, {0xAA7E, 0xAAAF}, {0xAAB1, 0xAAB1}, {0xAAB5, 0xAAB6}, {0xAAB9, 0xAABD}, {0xAAC0, 0xAAC0}, {0xAAC2, 0xAAC2}, {0xAADB, 0xAADD}, {0xAAE0, 0xAAEA}, {0xAAF2, 0xAAF4},
|
|
||||||
{0xAB01, 0xAB06}, {0xAB09, 0xAB0E}, {0xAB11, 0xAB16}, {0xAB20, 0xAB26}, {0xAB28, 0xAB2E}, {0xAB30, 0xAB5A}, {0xAB5C, 0xAB69}, {0xAB70, 0xABE2}, {0xAC00, 0xD7A3}, {0xD7B0, 0xD7C6}, {0xD7CB, 0xD7FB},
|
|
||||||
{0xF900, 0xFA6D}, {0xFA70, 0xFAD9}, {0xFB00, 0xFB06}, {0xFB13, 0xFB17}, {0xFB1D, 0xFB1D}, {0xFB1F, 0xFB28}, {0xFB2A, 0xFB36}, {0xFB38, 0xFB3C}, {0xFB3E, 0xFB3E}, {0xFB40, 0xFB41}, {0xFB43, 0xFB44},
|
|
||||||
{0xFB46, 0xFBB1}, {0xFBD3, 0xFD3D}, {0xFD50, 0xFD8F}, {0xFD92, 0xFDC7}, {0xFDF0, 0xFDFB}, {0xFE70, 0xFE74}, {0xFE76, 0xFEFC}, {0xFF21, 0xFF3A}, {0xFF41, 0xFF5A}, {0xFF66, 0xFFBE}, {0xFFC2, 0xFFC7},
|
|
||||||
{0xFFCA, 0xFFCF}, {0xFFD2, 0xFFD7}, {0xFFDA, 0xFFDC}, {0x10000, 0x1000B}, {0x1000D, 0x10026}, {0x10028, 0x1003A}, {0x1003C, 0x1003D}, {0x1003F, 0x1004D}, {0x10050, 0x1005D}, {0x10080, 0x100FA},
|
|
||||||
{0x10280, 0x1029C}, {0x102A0, 0x102D0}, {0x10300, 0x1031F}, {0x1032D, 0x10340}, {0x10342, 0x10349}, {0x10350, 0x10375}, {0x10380, 0x1039D}, {0x103A0, 0x103C3}, {0x103C8, 0x103CF}, {0x10400, 0x1049D},
|
|
||||||
{0x104B0, 0x104D3}, {0x104D8, 0x104FB}, {0x10500, 0x10527}, {0x10530, 0x10563}, {0x10600, 0x10736}, {0x10740, 0x10755}, {0x10760, 0x10767}, {0x10800, 0x10805}, {0x10808, 0x10808}, {0x1080A, 0x10835},
|
|
||||||
{0x10837, 0x10838}, {0x1083C, 0x1083C}, {0x1083F, 0x10855}, {0x10860, 0x10876}, {0x10880, 0x1089E}, {0x108E0, 0x108F2}, {0x108F4, 0x108F5}, {0x10900, 0x10915}, {0x10920, 0x10939}, {0x10980, 0x109B7},
|
|
||||||
{0x109BE, 0x109BF}, {0x10A00, 0x10A00}, {0x10A10, 0x10A13}, {0x10A15, 0x10A17}, {0x10A19, 0x10A35}, {0x10A60, 0x10A7C}, {0x10A80, 0x10A9C}, {0x10AC0, 0x10AC7}, {0x10AC9, 0x10AE4}, {0x10B00, 0x10B35},
|
|
||||||
{0x10B40, 0x10B55}, {0x10B60, 0x10B72}, {0x10B80, 0x10B91}, {0x10C00, 0x10C48}, {0x10C80, 0x10CB2}, {0x10CC0, 0x10CF2}, {0x10D00, 0x10D23}, {0x10E80, 0x10EA9}, {0x10EB0, 0x10EB1}, {0x10F00, 0x10F1C},
|
|
||||||
{0x10F27, 0x10F27}, {0x10F30, 0x10F45}, {0x10FB0, 0x10FC4}, {0x10FE0, 0x10FF6}, {0x11003, 0x11037}, {0x11083, 0x110AF}, {0x110D0, 0x110E8}, {0x11103, 0x11126}, {0x11144, 0x11144}, {0x11147, 0x11147},
|
|
||||||
{0x11150, 0x11172}, {0x11176, 0x11176}, {0x11183, 0x111B2}, {0x111C1, 0x111C4}, {0x111DA, 0x111DA}, {0x111DC, 0x111DC}, {0x11200, 0x11211}, {0x11213, 0x1122B}, {0x11280, 0x11286}, {0x11288, 0x11288},
|
|
||||||
{0x1128A, 0x1128D}, {0x1128F, 0x1129D}, {0x1129F, 0x112A8}, {0x112B0, 0x112DE}, {0x11305, 0x1130C}, {0x1130F, 0x11310}, {0x11313, 0x11328}, {0x1132A, 0x11330}, {0x11332, 0x11333}, {0x11335, 0x11339},
|
|
||||||
{0x1133D, 0x1133D}, {0x11350, 0x11350}, {0x1135D, 0x11361}, {0x11400, 0x11434}, {0x11447, 0x1144A}, {0x1145F, 0x11461}, {0x11480, 0x114AF}, {0x114C4, 0x114C5}, {0x114C7, 0x114C7}, {0x11580, 0x115AE},
|
|
||||||
{0x115D8, 0x115DB}, {0x11600, 0x1162F}, {0x11644, 0x11644}, {0x11680, 0x116AA}, {0x116B8, 0x116B8}, {0x11700, 0x1171A}, {0x11800, 0x1182B}, {0x118A0, 0x118DF}, {0x118FF, 0x11906}, {0x11909, 0x11909},
|
|
||||||
{0x1190C, 0x11913}, {0x11915, 0x11916}, {0x11918, 0x1192F}, {0x1193F, 0x1193F}, {0x11941, 0x11941}, {0x119A0, 0x119A7}, {0x119AA, 0x119D0}, {0x119E1, 0x119E1}, {0x119E3, 0x119E3}, {0x11A00, 0x11A00},
|
|
||||||
{0x11A0B, 0x11A32}, {0x11A3A, 0x11A3A}, {0x11A50, 0x11A50}, {0x11A5C, 0x11A89}, {0x11A9D, 0x11A9D}, {0x11AC0, 0x11AF8}, {0x11C00, 0x11C08}, {0x11C0A, 0x11C2E}, {0x11C40, 0x11C40}, {0x11C72, 0x11C8F},
|
|
||||||
{0x11D00, 0x11D06}, {0x11D08, 0x11D09}, {0x11D0B, 0x11D30}, {0x11D46, 0x11D46}, {0x11D60, 0x11D65}, {0x11D67, 0x11D68}, {0x11D6A, 0x11D89}, {0x11D98, 0x11D98}, {0x11EE0, 0x11EF2}, {0x11FB0, 0x11FB0},
|
|
||||||
{0x12000, 0x12399}, {0x12480, 0x12543}, {0x13000, 0x1342E}, {0x14400, 0x14646}, {0x16800, 0x16A38}, {0x16A40, 0x16A5E}, {0x16AD0, 0x16AED}, {0x16B00, 0x16B2F}, {0x16B40, 0x16B43}, {0x16B63, 0x16B77},
|
|
||||||
{0x16B7D, 0x16B8F}, {0x16E40, 0x16E7F}, {0x16F00, 0x16F4A}, {0x16F50, 0x16F50}, {0x16F93, 0x16F9F}, {0x16FE0, 0x16FE1}, {0x16FE3, 0x16FE3}, {0x17000, 0x187F7}, {0x18800, 0x18CD5}, {0x18D00, 0x18D08},
|
|
||||||
{0x1B000, 0x1B11E}, {0x1B150, 0x1B152}, {0x1B164, 0x1B167}, {0x1B170, 0x1B2FB}, {0x1BC00, 0x1BC6A}, {0x1BC70, 0x1BC7C}, {0x1BC80, 0x1BC88}, {0x1BC90, 0x1BC99}, {0x1D400, 0x1D454}, {0x1D456, 0x1D49C},
|
|
||||||
{0x1D49E, 0x1D49F}, {0x1D4A2, 0x1D4A2}, {0x1D4A5, 0x1D4A6}, {0x1D4A9, 0x1D4AC}, {0x1D4AE, 0x1D4B9}, {0x1D4BB, 0x1D4BB}, {0x1D4BD, 0x1D4C3}, {0x1D4C5, 0x1D505}, {0x1D507, 0x1D50A}, {0x1D50D, 0x1D514},
|
|
||||||
{0x1D516, 0x1D51C}, {0x1D51E, 0x1D539}, {0x1D53B, 0x1D53E}, {0x1D540, 0x1D544}, {0x1D546, 0x1D546}, {0x1D54A, 0x1D550}, {0x1D552, 0x1D6A5}, {0x1D6A8, 0x1D6C0}, {0x1D6C2, 0x1D6DA}, {0x1D6DC, 0x1D6FA},
|
|
||||||
{0x1D6FC, 0x1D714}, {0x1D716, 0x1D734}, {0x1D736, 0x1D74E}, {0x1D750, 0x1D76E}, {0x1D770, 0x1D788}, {0x1D78A, 0x1D7A8}, {0x1D7AA, 0x1D7C2}, {0x1D7C4, 0x1D7CB}, {0x1E100, 0x1E12C}, {0x1E137, 0x1E13D},
|
|
||||||
{0x1E14E, 0x1E14E}, {0x1E2C0, 0x1E2EB}, {0x1E800, 0x1E8C4}, {0x1E900, 0x1E943}, {0x1E94B, 0x1E94B}, {0x1EE00, 0x1EE03}, {0x1EE05, 0x1EE1F}, {0x1EE21, 0x1EE22}, {0x1EE24, 0x1EE24}, {0x1EE27, 0x1EE27},
|
|
||||||
{0x1EE29, 0x1EE32}, {0x1EE34, 0x1EE37}, {0x1EE39, 0x1EE39}, {0x1EE3B, 0x1EE3B}, {0x1EE42, 0x1EE42}, {0x1EE47, 0x1EE47}, {0x1EE49, 0x1EE49}, {0x1EE4B, 0x1EE4B}, {0x1EE4D, 0x1EE4F}, {0x1EE51, 0x1EE52},
|
|
||||||
{0x1EE54, 0x1EE54}, {0x1EE57, 0x1EE57}, {0x1EE59, 0x1EE59}, {0x1EE5B, 0x1EE5B}, {0x1EE5D, 0x1EE5D}, {0x1EE5F, 0x1EE5F}, {0x1EE61, 0x1EE62}, {0x1EE64, 0x1EE64}, {0x1EE67, 0x1EE6A}, {0x1EE6C, 0x1EE72},
|
|
||||||
{0x1EE74, 0x1EE77}, {0x1EE79, 0x1EE7C}, {0x1EE7E, 0x1EE7E}, {0x1EE80, 0x1EE89}, {0x1EE8B, 0x1EE9B}, {0x1EEA1, 0x1EEA3}, {0x1EEA5, 0x1EEA9}, {0x1EEAB, 0x1EEBB}, {0x20000, 0x2A6DD}, {0x2A700, 0x2B734},
|
|
||||||
{0x2B740, 0x2B81D}, {0x2B820, 0x2CEA1}, {0x2CEB0, 0x2EBE0}, {0x2F800, 0x2FA1D}, {0x30000, 0x3134A},
|
|
||||||
};
|
|
||||||
|
|
||||||
static const std::vector<std::pair<uint32_t, uint32_t>> whitespace_ranges = {
|
|
||||||
{0x9, 0xD}, {0x1C, 0x20}, {0x85, 0x85}, {0xA0, 0xA0}, {0x1680, 0x1680}, {0x2000, 0x200A}, {0x2028, 0x2029}, {0x202F, 0x202F}, {0x205F, 0x205F}, {0x3000, 0x3000},
|
|
||||||
};
|
|
||||||
|
|
||||||
static const std::vector<std::pair<uint32_t, uint32_t>> accent_mark_ranges = {
|
|
||||||
{0x300, 0x36F}, {0x483, 0x489}, {0x591, 0x5BD}, {0x5BF, 0x5BF}, {0x5C1, 0x5C2}, {0x5C4, 0x5C5}, {0x5C7, 0x5C7}, {0x610, 0x61A}, {0x64B, 0x65F}, {0x670, 0x670}, {0x6D6, 0x6DC}, {0x6DF, 0x6E4},
|
|
||||||
{0x6E7, 0x6E8}, {0x6EA, 0x6ED}, {0x711, 0x711}, {0x730, 0x74A}, {0x7A6, 0x7B0}, {0x7EB, 0x7F3}, {0x7FD, 0x7FD}, {0x816, 0x819}, {0x81B, 0x823}, {0x825, 0x827}, {0x829, 0x82D}, {0x859, 0x85B},
|
|
||||||
{0x8D3, 0x8E1}, {0x8E3, 0x903}, {0x93A, 0x93C}, {0x93E, 0x94F}, {0x951, 0x957}, {0x962, 0x963}, {0x981, 0x983}, {0x9BC, 0x9BC}, {0x9BE, 0x9C4}, {0x9C7, 0x9C8}, {0x9CB, 0x9CD}, {0x9D7, 0x9D7},
|
|
||||||
{0x9E2, 0x9E3}, {0x9FE, 0x9FE}, {0xA01, 0xA03}, {0xA3C, 0xA3C}, {0xA3E, 0xA42}, {0xA47, 0xA48}, {0xA4B, 0xA4D}, {0xA51, 0xA51}, {0xA70, 0xA71}, {0xA75, 0xA75}, {0xA81, 0xA83}, {0xABC, 0xABC},
|
|
||||||
{0xABE, 0xAC5}, {0xAC7, 0xAC9}, {0xACB, 0xACD}, {0xAE2, 0xAE3}, {0xAFA, 0xAFF}, {0xB01, 0xB03}, {0xB3C, 0xB3C}, {0xB3E, 0xB44}, {0xB47, 0xB48}, {0xB4B, 0xB4D}, {0xB55, 0xB57}, {0xB62, 0xB63},
|
|
||||||
{0xB82, 0xB82}, {0xBBE, 0xBC2}, {0xBC6, 0xBC8}, {0xBCA, 0xBCD}, {0xBD7, 0xBD7}, {0xC00, 0xC04}, {0xC3E, 0xC44}, {0xC46, 0xC48}, {0xC4A, 0xC4D}, {0xC55, 0xC56}, {0xC62, 0xC63}, {0xC81, 0xC83},
|
|
||||||
{0xCBC, 0xCBC}, {0xCBE, 0xCC4}, {0xCC6, 0xCC8}, {0xCCA, 0xCCD}, {0xCD5, 0xCD6}, {0xCE2, 0xCE3}, {0xD00, 0xD03}, {0xD3B, 0xD3C}, {0xD3E, 0xD44}, {0xD46, 0xD48}, {0xD4A, 0xD4D}, {0xD57, 0xD57},
|
|
||||||
{0xD62, 0xD63}, {0xD81, 0xD83}, {0xDCA, 0xDCA}, {0xDCF, 0xDD4}, {0xDD6, 0xDD6}, {0xDD8, 0xDDF}, {0xDF2, 0xDF3}, {0xE31, 0xE31}, {0xE34, 0xE3A}, {0xE47, 0xE4E}, {0xEB1, 0xEB1}, {0xEB4, 0xEBC},
|
|
||||||
{0xEC8, 0xECD}, {0xF18, 0xF19}, {0xF35, 0xF35}, {0xF37, 0xF37}, {0xF39, 0xF39}, {0xF3E, 0xF3F}, {0xF71, 0xF84}, {0xF86, 0xF87}, {0xF8D, 0xF97}, {0xF99, 0xFBC}, {0xFC6, 0xFC6}, {0x102B, 0x103E},
|
|
||||||
{0x1056, 0x1059}, {0x105E, 0x1060}, {0x1062, 0x1064}, {0x1067, 0x106D}, {0x1071, 0x1074}, {0x1082, 0x108D}, {0x108F, 0x108F}, {0x109A, 0x109D}, {0x135D, 0x135F}, {0x1712, 0x1714}, {0x1732, 0x1734},
|
|
||||||
{0x1752, 0x1753}, {0x1772, 0x1773}, {0x17B4, 0x17D3}, {0x17DD, 0x17DD}, {0x180B, 0x180D}, {0x1885, 0x1886}, {0x18A9, 0x18A9}, {0x1920, 0x192B}, {0x1930, 0x193B}, {0x1A17, 0x1A1B}, {0x1A55, 0x1A5E},
|
|
||||||
{0x1A60, 0x1A7C}, {0x1A7F, 0x1A7F}, {0x1AB0, 0x1AC0}, {0x1B00, 0x1B04}, {0x1B34, 0x1B44}, {0x1B6B, 0x1B73}, {0x1B80, 0x1B82}, {0x1BA1, 0x1BAD}, {0x1BE6, 0x1BF3}, {0x1C24, 0x1C37}, {0x1CD0, 0x1CD2},
|
|
||||||
{0x1CD4, 0x1CE8}, {0x1CED, 0x1CED}, {0x1CF4, 0x1CF4}, {0x1CF7, 0x1CF9}, {0x1DC0, 0x1DF9}, {0x1DFB, 0x1DFF}, {0x20D0, 0x20F0}, {0x2CEF, 0x2CF1}, {0x2D7F, 0x2D7F}, {0x2DE0, 0x2DFF}, {0x302A, 0x302F},
|
|
||||||
{0x3099, 0x309A}, {0xA66F, 0xA672}, {0xA674, 0xA67D}, {0xA69E, 0xA69F}, {0xA6F0, 0xA6F1}, {0xA802, 0xA802}, {0xA806, 0xA806}, {0xA80B, 0xA80B}, {0xA823, 0xA827}, {0xA82C, 0xA82C}, {0xA880, 0xA881},
|
|
||||||
{0xA8B4, 0xA8C5}, {0xA8E0, 0xA8F1}, {0xA8FF, 0xA8FF}, {0xA926, 0xA92D}, {0xA947, 0xA953}, {0xA980, 0xA983}, {0xA9B3, 0xA9C0}, {0xA9E5, 0xA9E5}, {0xAA29, 0xAA36}, {0xAA43, 0xAA43}, {0xAA4C, 0xAA4D},
|
|
||||||
{0xAA7B, 0xAA7D}, {0xAAB0, 0xAAB0}, {0xAAB2, 0xAAB4}, {0xAAB7, 0xAAB8}, {0xAABE, 0xAABF}, {0xAAC1, 0xAAC1}, {0xAAEB, 0xAAEF}, {0xAAF5, 0xAAF6}, {0xABE3, 0xABEA}, {0xABEC, 0xABED}, {0xFB1E, 0xFB1E},
|
|
||||||
{0xFE00, 0xFE0F}, {0xFE20, 0xFE2F}, {0x101FD, 0x101FD}, {0x102E0, 0x102E0}, {0x10376, 0x1037A}, {0x10A01, 0x10A03}, {0x10A05, 0x10A06}, {0x10A0C, 0x10A0F}, {0x10A38, 0x10A3A}, {0x10A3F, 0x10A3F},
|
|
||||||
{0x10AE5, 0x10AE6}, {0x10D24, 0x10D27}, {0x10EAB, 0x10EAC}, {0x10F46, 0x10F50}, {0x11000, 0x11002}, {0x11038, 0x11046}, {0x1107F, 0x11082}, {0x110B0, 0x110BA}, {0x11100, 0x11102}, {0x11127, 0x11134},
|
|
||||||
{0x11145, 0x11146}, {0x11173, 0x11173}, {0x11180, 0x11182}, {0x111B3, 0x111C0}, {0x111C9, 0x111CC}, {0x111CE, 0x111CF}, {0x1122C, 0x11237}, {0x1123E, 0x1123E}, {0x112DF, 0x112EA}, {0x11300, 0x11303},
|
|
||||||
{0x1133B, 0x1133C}, {0x1133E, 0x11344}, {0x11347, 0x11348}, {0x1134B, 0x1134D}, {0x11357, 0x11357}, {0x11362, 0x11363}, {0x11366, 0x1136C}, {0x11370, 0x11374}, {0x11435, 0x11446}, {0x1145E, 0x1145E},
|
|
||||||
{0x114B0, 0x114C3}, {0x115AF, 0x115B5}, {0x115B8, 0x115C0}, {0x115DC, 0x115DD}, {0x11630, 0x11640}, {0x116AB, 0x116B7}, {0x1171D, 0x1172B}, {0x1182C, 0x1183A}, {0x11930, 0x11935}, {0x11937, 0x11938},
|
|
||||||
{0x1193B, 0x1193E}, {0x11940, 0x11940}, {0x11942, 0x11943}, {0x119D1, 0x119D7}, {0x119DA, 0x119E0}, {0x119E4, 0x119E4}, {0x11A01, 0x11A0A}, {0x11A33, 0x11A39}, {0x11A3B, 0x11A3E}, {0x11A47, 0x11A47},
|
|
||||||
{0x11A51, 0x11A5B}, {0x11A8A, 0x11A99}, {0x11C2F, 0x11C36}, {0x11C38, 0x11C3F}, {0x11C92, 0x11CA7}, {0x11CA9, 0x11CB6}, {0x11D31, 0x11D36}, {0x11D3A, 0x11D3A}, {0x11D3C, 0x11D3D}, {0x11D3F, 0x11D45},
|
|
||||||
{0x11D47, 0x11D47}, {0x11D8A, 0x11D8E}, {0x11D90, 0x11D91}, {0x11D93, 0x11D97}, {0x11EF3, 0x11EF6}, {0x16AF0, 0x16AF4}, {0x16B30, 0x16B36}, {0x16F4F, 0x16F4F}, {0x16F51, 0x16F87}, {0x16F8F, 0x16F92},
|
|
||||||
{0x16FE4, 0x16FE4}, {0x16FF0, 0x16FF1}, {0x1BC9D, 0x1BC9E}, {0x1D165, 0x1D169}, {0x1D16D, 0x1D172}, {0x1D17B, 0x1D182}, {0x1D185, 0x1D18B}, {0x1D1AA, 0x1D1AD}, {0x1D242, 0x1D244}, {0x1DA00, 0x1DA36},
|
|
||||||
{0x1DA3B, 0x1DA6C}, {0x1DA75, 0x1DA75}, {0x1DA84, 0x1DA84}, {0x1DA9B, 0x1DA9F}, {0x1DAA1, 0x1DAAF}, {0x1E000, 0x1E006}, {0x1E008, 0x1E018}, {0x1E01B, 0x1E021}, {0x1E023, 0x1E024}, {0x1E026, 0x1E02A},
|
|
||||||
{0x1E130, 0x1E136}, {0x1E2EC, 0x1E2EF}, {0x1E8D0, 0x1E8D6}, {0x1E944, 0x1E94A}, {0xE0100, 0xE01EF},
|
|
||||||
};
|
|
||||||
|
|
||||||
static const std::vector<std::pair<uint32_t, uint32_t>> punctuation_ranges = {
|
|
||||||
{0x21, 0x23}, {0x25, 0x2A}, {0x2C, 0x2F}, {0x3A, 0x3B}, {0x3F, 0x40}, {0x5B, 0x5D}, {0x5F, 0x5F}, {0x7B, 0x7B}, {0x7D, 0x7D}, {0xA1, 0xA1}, {0xA7, 0xA7}, {0xAB, 0xAB}, {0xB6, 0xB7}, {0xBB, 0xBB},
|
|
||||||
{0xBF, 0xBF}, {0x37E, 0x37E}, {0x387, 0x387}, {0x55A, 0x55F}, {0x589, 0x58A}, {0x5BE, 0x5BE}, {0x5C0, 0x5C0}, {0x5C3, 0x5C3}, {0x5C6, 0x5C6}, {0x5F3, 0x5F4}, {0x609, 0x60A}, {0x60C, 0x60D},
|
|
||||||
{0x61B, 0x61B}, {0x61E, 0x61F}, {0x66A, 0x66D}, {0x6D4, 0x6D4}, {0x700, 0x70D}, {0x7F7, 0x7F9}, {0x830, 0x83E}, {0x85E, 0x85E}, {0x964, 0x965}, {0x970, 0x970}, {0x9FD, 0x9FD}, {0xA76, 0xA76},
|
|
||||||
{0xAF0, 0xAF0}, {0xC77, 0xC77}, {0xC84, 0xC84}, {0xDF4, 0xDF4}, {0xE4F, 0xE4F}, {0xE5A, 0xE5B}, {0xF04, 0xF12}, {0xF14, 0xF14}, {0xF3A, 0xF3D}, {0xF85, 0xF85}, {0xFD0, 0xFD4}, {0xFD9, 0xFDA},
|
|
||||||
{0x104A, 0x104F}, {0x10FB, 0x10FB}, {0x1360, 0x1368}, {0x1400, 0x1400}, {0x166E, 0x166E}, {0x169B, 0x169C}, {0x16EB, 0x16ED}, {0x1735, 0x1736}, {0x17D4, 0x17D6}, {0x17D8, 0x17DA}, {0x1800, 0x180A},
|
|
||||||
{0x1944, 0x1945}, {0x1A1E, 0x1A1F}, {0x1AA0, 0x1AA6}, {0x1AA8, 0x1AAD}, {0x1B5A, 0x1B60}, {0x1BFC, 0x1BFF}, {0x1C3B, 0x1C3F}, {0x1C7E, 0x1C7F}, {0x1CC0, 0x1CC7}, {0x1CD3, 0x1CD3}, {0x2010, 0x2027},
|
|
||||||
{0x2030, 0x2043}, {0x2045, 0x2051}, {0x2053, 0x205E}, {0x207D, 0x207E}, {0x208D, 0x208E}, {0x2308, 0x230B}, {0x2329, 0x232A}, {0x2768, 0x2775}, {0x27C5, 0x27C6}, {0x27E6, 0x27EF}, {0x2983, 0x2998},
|
|
||||||
{0x29D8, 0x29DB}, {0x29FC, 0x29FD}, {0x2CF9, 0x2CFC}, {0x2CFE, 0x2CFF}, {0x2D70, 0x2D70}, {0x2E00, 0x2E2E}, {0x2E30, 0x2E4F}, {0x2E52, 0x2E52}, {0x3001, 0x3003}, {0x3008, 0x3011}, {0x3014, 0x301F},
|
|
||||||
{0x3030, 0x3030}, {0x303D, 0x303D}, {0x30A0, 0x30A0}, {0x30FB, 0x30FB}, {0xA4FE, 0xA4FF}, {0xA60D, 0xA60F}, {0xA673, 0xA673}, {0xA67E, 0xA67E}, {0xA6F2, 0xA6F7}, {0xA874, 0xA877}, {0xA8CE, 0xA8CF},
|
|
||||||
{0xA8F8, 0xA8FA}, {0xA8FC, 0xA8FC}, {0xA92E, 0xA92F}, {0xA95F, 0xA95F}, {0xA9C1, 0xA9CD}, {0xA9DE, 0xA9DF}, {0xAA5C, 0xAA5F}, {0xAADE, 0xAADF}, {0xAAF0, 0xAAF1}, {0xABEB, 0xABEB}, {0xFD3E, 0xFD3F},
|
|
||||||
{0xFE10, 0xFE19}, {0xFE30, 0xFE52}, {0xFE54, 0xFE61}, {0xFE63, 0xFE63}, {0xFE68, 0xFE68}, {0xFE6A, 0xFE6B}, {0xFF01, 0xFF03}, {0xFF05, 0xFF0A}, {0xFF0C, 0xFF0F}, {0xFF1A, 0xFF1B}, {0xFF1F, 0xFF20},
|
|
||||||
{0xFF3B, 0xFF3D}, {0xFF3F, 0xFF3F}, {0xFF5B, 0xFF5B}, {0xFF5D, 0xFF5D}, {0xFF5F, 0xFF65}, {0x10100, 0x10102}, {0x1039F, 0x1039F}, {0x103D0, 0x103D0}, {0x1056F, 0x1056F}, {0x10857, 0x10857},
|
|
||||||
{0x1091F, 0x1091F}, {0x1093F, 0x1093F}, {0x10A50, 0x10A58}, {0x10A7F, 0x10A7F}, {0x10AF0, 0x10AF6}, {0x10B39, 0x10B3F}, {0x10B99, 0x10B9C}, {0x10EAD, 0x10EAD}, {0x10F55, 0x10F59}, {0x11047, 0x1104D},
|
|
||||||
{0x110BB, 0x110BC}, {0x110BE, 0x110C1}, {0x11140, 0x11143}, {0x11174, 0x11175}, {0x111C5, 0x111C8}, {0x111CD, 0x111CD}, {0x111DB, 0x111DB}, {0x111DD, 0x111DF}, {0x11238, 0x1123D}, {0x112A9, 0x112A9},
|
|
||||||
{0x1144B, 0x1144F}, {0x1145A, 0x1145B}, {0x1145D, 0x1145D}, {0x114C6, 0x114C6}, {0x115C1, 0x115D7}, {0x11641, 0x11643}, {0x11660, 0x1166C}, {0x1173C, 0x1173E}, {0x1183B, 0x1183B}, {0x11944, 0x11946},
|
|
||||||
{0x119E2, 0x119E2}, {0x11A3F, 0x11A46}, {0x11A9A, 0x11A9C}, {0x11A9E, 0x11AA2}, {0x11C41, 0x11C45}, {0x11C70, 0x11C71}, {0x11EF7, 0x11EF8}, {0x11FFF, 0x11FFF}, {0x12470, 0x12474}, {0x16A6E, 0x16A6F},
|
|
||||||
{0x16AF5, 0x16AF5}, {0x16B37, 0x16B3B}, {0x16B44, 0x16B44}, {0x16E97, 0x16E9A}, {0x16FE2, 0x16FE2}, {0x1BC9F, 0x1BC9F}, {0x1DA87, 0x1DA8B}, {0x1E95E, 0x1E95F},
|
|
||||||
};
|
|
||||||
|
|
||||||
static const std::vector<std::pair<uint32_t, uint32_t>> symbol_ranges = {
|
|
||||||
{0x24, 0x24}, {0x2B, 0x2B}, {0x3C, 0x3E}, {0x5E, 0x5E}, {0x60, 0x60}, {0x7C, 0x7C}, {0x7E, 0x7E}, {0xA2, 0xA6}, {0xA8, 0xA9}, {0xAC, 0xAC}, {0xAE, 0xB1}, {0xB4, 0xB4}, {0xB8, 0xB8}, {0xD7, 0xD7},
|
|
||||||
{0xF7, 0xF7}, {0x2C2, 0x2C5}, {0x2D2, 0x2DF}, {0x2E5, 0x2EB}, {0x2ED, 0x2ED}, {0x2EF, 0x2FF}, {0x375, 0x375}, {0x384, 0x385}, {0x3F6, 0x3F6}, {0x482, 0x482}, {0x58D, 0x58F}, {0x606, 0x608},
|
|
||||||
{0x60B, 0x60B}, {0x60E, 0x60F}, {0x6DE, 0x6DE}, {0x6E9, 0x6E9}, {0x6FD, 0x6FE}, {0x7F6, 0x7F6}, {0x7FE, 0x7FF}, {0x9F2, 0x9F3}, {0x9FA, 0x9FB}, {0xAF1, 0xAF1}, {0xB70, 0xB70}, {0xBF3, 0xBFA},
|
|
||||||
{0xC7F, 0xC7F}, {0xD4F, 0xD4F}, {0xD79, 0xD79}, {0xE3F, 0xE3F}, {0xF01, 0xF03}, {0xF13, 0xF13}, {0xF15, 0xF17}, {0xF1A, 0xF1F}, {0xF34, 0xF34}, {0xF36, 0xF36}, {0xF38, 0xF38}, {0xFBE, 0xFC5},
|
|
||||||
{0xFC7, 0xFCC}, {0xFCE, 0xFCF}, {0xFD5, 0xFD8}, {0x109E, 0x109F}, {0x1390, 0x1399}, {0x166D, 0x166D}, {0x17DB, 0x17DB}, {0x1940, 0x1940}, {0x19DE, 0x19FF}, {0x1B61, 0x1B6A}, {0x1B74, 0x1B7C},
|
|
||||||
{0x1FBD, 0x1FBD}, {0x1FBF, 0x1FC1}, {0x1FCD, 0x1FCF}, {0x1FDD, 0x1FDF}, {0x1FED, 0x1FEF}, {0x1FFD, 0x1FFE}, {0x2044, 0x2044}, {0x2052, 0x2052}, {0x207A, 0x207C}, {0x208A, 0x208C}, {0x20A0, 0x20BF},
|
|
||||||
{0x2100, 0x2101}, {0x2103, 0x2106}, {0x2108, 0x2109}, {0x2114, 0x2114}, {0x2116, 0x2118}, {0x211E, 0x2123}, {0x2125, 0x2125}, {0x2127, 0x2127}, {0x2129, 0x2129}, {0x212E, 0x212E}, {0x213A, 0x213B},
|
|
||||||
{0x2140, 0x2144}, {0x214A, 0x214D}, {0x214F, 0x214F}, {0x218A, 0x218B}, {0x2190, 0x2307}, {0x230C, 0x2328}, {0x232B, 0x2426}, {0x2440, 0x244A}, {0x249C, 0x24E9}, {0x2500, 0x2767}, {0x2794, 0x27C4},
|
|
||||||
{0x27C7, 0x27E5}, {0x27F0, 0x2982}, {0x2999, 0x29D7}, {0x29DC, 0x29FB}, {0x29FE, 0x2B73}, {0x2B76, 0x2B95}, {0x2B97, 0x2BFF}, {0x2CE5, 0x2CEA}, {0x2E50, 0x2E51}, {0x2E80, 0x2E99}, {0x2E9B, 0x2EF3},
|
|
||||||
{0x2F00, 0x2FD5}, {0x2FF0, 0x2FFB}, {0x3004, 0x3004}, {0x3012, 0x3013}, {0x3020, 0x3020}, {0x3036, 0x3037}, {0x303E, 0x303F}, {0x309B, 0x309C}, {0x3190, 0x3191}, {0x3196, 0x319F}, {0x31C0, 0x31E3},
|
|
||||||
{0x3200, 0x321E}, {0x322A, 0x3247}, {0x3250, 0x3250}, {0x3260, 0x327F}, {0x328A, 0x32B0}, {0x32C0, 0x33FF}, {0x4DC0, 0x4DFF}, {0xA490, 0xA4C6}, {0xA700, 0xA716}, {0xA720, 0xA721}, {0xA789, 0xA78A},
|
|
||||||
{0xA828, 0xA82B}, {0xA836, 0xA839}, {0xAA77, 0xAA79}, {0xAB5B, 0xAB5B}, {0xAB6A, 0xAB6B}, {0xFB29, 0xFB29}, {0xFBB2, 0xFBC1}, {0xFDFC, 0xFDFD}, {0xFE62, 0xFE62}, {0xFE64, 0xFE66}, {0xFE69, 0xFE69},
|
|
||||||
{0xFF04, 0xFF04}, {0xFF0B, 0xFF0B}, {0xFF1C, 0xFF1E}, {0xFF3E, 0xFF3E}, {0xFF40, 0xFF40}, {0xFF5C, 0xFF5C}, {0xFF5E, 0xFF5E}, {0xFFE0, 0xFFE6}, {0xFFE8, 0xFFEE}, {0xFFFC, 0xFFFD}, {0x10137, 0x1013F},
|
|
||||||
{0x10179, 0x10189}, {0x1018C, 0x1018E}, {0x10190, 0x1019C}, {0x101A0, 0x101A0}, {0x101D0, 0x101FC}, {0x10877, 0x10878}, {0x10AC8, 0x10AC8}, {0x1173F, 0x1173F}, {0x11FD5, 0x11FF1}, {0x16B3C, 0x16B3F},
|
|
||||||
{0x16B45, 0x16B45}, {0x1BC9C, 0x1BC9C}, {0x1D000, 0x1D0F5}, {0x1D100, 0x1D126}, {0x1D129, 0x1D164}, {0x1D16A, 0x1D16C}, {0x1D183, 0x1D184}, {0x1D18C, 0x1D1A9}, {0x1D1AE, 0x1D1E8}, {0x1D200, 0x1D241},
|
|
||||||
{0x1D245, 0x1D245}, {0x1D300, 0x1D356}, {0x1D6C1, 0x1D6C1}, {0x1D6DB, 0x1D6DB}, {0x1D6FB, 0x1D6FB}, {0x1D715, 0x1D715}, {0x1D735, 0x1D735}, {0x1D74F, 0x1D74F}, {0x1D76F, 0x1D76F}, {0x1D789, 0x1D789},
|
|
||||||
{0x1D7A9, 0x1D7A9}, {0x1D7C3, 0x1D7C3}, {0x1D800, 0x1D9FF}, {0x1DA37, 0x1DA3A}, {0x1DA6D, 0x1DA74}, {0x1DA76, 0x1DA83}, {0x1DA85, 0x1DA86}, {0x1E14F, 0x1E14F}, {0x1E2FF, 0x1E2FF}, {0x1ECAC, 0x1ECAC},
|
|
||||||
{0x1ECB0, 0x1ECB0}, {0x1ED2E, 0x1ED2E}, {0x1EEF0, 0x1EEF1}, {0x1F000, 0x1F02B}, {0x1F030, 0x1F093}, {0x1F0A0, 0x1F0AE}, {0x1F0B1, 0x1F0BF}, {0x1F0C1, 0x1F0CF}, {0x1F0D1, 0x1F0F5}, {0x1F10D, 0x1F1AD},
|
|
||||||
{0x1F1E6, 0x1F202}, {0x1F210, 0x1F23B}, {0x1F240, 0x1F248}, {0x1F250, 0x1F251}, {0x1F260, 0x1F265}, {0x1F300, 0x1F6D7}, {0x1F6E0, 0x1F6EC}, {0x1F6F0, 0x1F6FC}, {0x1F700, 0x1F773}, {0x1F780, 0x1F7D8},
|
|
||||||
{0x1F7E0, 0x1F7EB}, {0x1F800, 0x1F80B}, {0x1F810, 0x1F847}, {0x1F850, 0x1F859}, {0x1F860, 0x1F887}, {0x1F890, 0x1F8AD}, {0x1F8B0, 0x1F8B1}, {0x1F900, 0x1F978}, {0x1F97A, 0x1F9CB}, {0x1F9CD, 0x1FA53},
|
|
||||||
{0x1FA60, 0x1FA6D}, {0x1FA70, 0x1FA74}, {0x1FA78, 0x1FA7A}, {0x1FA80, 0x1FA86}, {0x1FA90, 0x1FAA8}, {0x1FAB0, 0x1FAB6}, {0x1FAC0, 0x1FAC2}, {0x1FAD0, 0x1FAD6}, {0x1FB00, 0x1FB92}, {0x1FB94, 0x1FBCA},
|
|
||||||
};
|
|
||||||
|
|
||||||
static const std::vector<std::pair<uint32_t, uint32_t>> control_ranges = {
|
|
||||||
{0x0, 0x8}, {0xE, 0x1B}, {0x7F, 0x84}, {0x86, 0x9F}, {0xAD, 0xAD}, {0x378, 0x379}, {0x380, 0x383}, {0x38B, 0x38B}, {0x38D, 0x38D}, {0x3A2, 0x3A2}, {0x530, 0x530}, {0x557, 0x558}, {0x58B, 0x58C},
|
|
||||||
{0x590, 0x590}, {0x5C8, 0x5CF}, {0x5EB, 0x5EE}, {0x5F5, 0x605}, {0x61C, 0x61D}, {0x6DD, 0x6DD}, {0x70E, 0x70F}, {0x74B, 0x74C}, {0x7B2, 0x7BF}, {0x7FB, 0x7FC}, {0x82E, 0x82F}, {0x83F, 0x83F},
|
|
||||||
{0x85C, 0x85D}, {0x85F, 0x85F}, {0x86B, 0x89F}, {0x8B5, 0x8B5}, {0x8C8, 0x8D2}, {0x8E2, 0x8E2}, {0x984, 0x984}, {0x98D, 0x98E}, {0x991, 0x992}, {0x9A9, 0x9A9}, {0x9B1, 0x9B1}, {0x9B3, 0x9B5},
|
|
||||||
{0x9BA, 0x9BB}, {0x9C5, 0x9C6}, {0x9C9, 0x9CA}, {0x9CF, 0x9D6}, {0x9D8, 0x9DB}, {0x9DE, 0x9DE}, {0x9E4, 0x9E5}, {0x9FF, 0xA00}, {0xA04, 0xA04}, {0xA0B, 0xA0E}, {0xA11, 0xA12}, {0xA29, 0xA29},
|
|
||||||
{0xA31, 0xA31}, {0xA34, 0xA34}, {0xA37, 0xA37}, {0xA3A, 0xA3B}, {0xA3D, 0xA3D}, {0xA43, 0xA46}, {0xA49, 0xA4A}, {0xA4E, 0xA50}, {0xA52, 0xA58}, {0xA5D, 0xA5D}, {0xA5F, 0xA65}, {0xA77, 0xA80},
|
|
||||||
{0xA84, 0xA84}, {0xA8E, 0xA8E}, {0xA92, 0xA92}, {0xAA9, 0xAA9}, {0xAB1, 0xAB1}, {0xAB4, 0xAB4}, {0xABA, 0xABB}, {0xAC6, 0xAC6}, {0xACA, 0xACA}, {0xACE, 0xACF}, {0xAD1, 0xADF}, {0xAE4, 0xAE5},
|
|
||||||
{0xAF2, 0xAF8}, {0xB00, 0xB00}, {0xB04, 0xB04}, {0xB0D, 0xB0E}, {0xB11, 0xB12}, {0xB29, 0xB29}, {0xB31, 0xB31}, {0xB34, 0xB34}, {0xB3A, 0xB3B}, {0xB45, 0xB46}, {0xB49, 0xB4A}, {0xB4E, 0xB54},
|
|
||||||
{0xB58, 0xB5B}, {0xB5E, 0xB5E}, {0xB64, 0xB65}, {0xB78, 0xB81}, {0xB84, 0xB84}, {0xB8B, 0xB8D}, {0xB91, 0xB91}, {0xB96, 0xB98}, {0xB9B, 0xB9B}, {0xB9D, 0xB9D}, {0xBA0, 0xBA2}, {0xBA5, 0xBA7},
|
|
||||||
{0xBAB, 0xBAD}, {0xBBA, 0xBBD}, {0xBC3, 0xBC5}, {0xBC9, 0xBC9}, {0xBCE, 0xBCF}, {0xBD1, 0xBD6}, {0xBD8, 0xBE5}, {0xBFB, 0xBFF}, {0xC0D, 0xC0D}, {0xC11, 0xC11}, {0xC29, 0xC29}, {0xC3A, 0xC3C},
|
|
||||||
{0xC45, 0xC45}, {0xC49, 0xC49}, {0xC4E, 0xC54}, {0xC57, 0xC57}, {0xC5B, 0xC5F}, {0xC64, 0xC65}, {0xC70, 0xC76}, {0xC8D, 0xC8D}, {0xC91, 0xC91}, {0xCA9, 0xCA9}, {0xCB4, 0xCB4}, {0xCBA, 0xCBB},
|
|
||||||
{0xCC5, 0xCC5}, {0xCC9, 0xCC9}, {0xCCE, 0xCD4}, {0xCD7, 0xCDD}, {0xCDF, 0xCDF}, {0xCE4, 0xCE5}, {0xCF0, 0xCF0}, {0xCF3, 0xCFF}, {0xD0D, 0xD0D}, {0xD11, 0xD11}, {0xD45, 0xD45}, {0xD49, 0xD49},
|
|
||||||
{0xD50, 0xD53}, {0xD64, 0xD65}, {0xD80, 0xD80}, {0xD84, 0xD84}, {0xD97, 0xD99}, {0xDB2, 0xDB2}, {0xDBC, 0xDBC}, {0xDBE, 0xDBF}, {0xDC7, 0xDC9}, {0xDCB, 0xDCE}, {0xDD5, 0xDD5}, {0xDD7, 0xDD7},
|
|
||||||
{0xDE0, 0xDE5}, {0xDF0, 0xDF1}, {0xDF5, 0xE00}, {0xE3B, 0xE3E}, {0xE5C, 0xE80}, {0xE83, 0xE83}, {0xE85, 0xE85}, {0xE8B, 0xE8B}, {0xEA4, 0xEA4}, {0xEA6, 0xEA6}, {0xEBE, 0xEBF}, {0xEC5, 0xEC5},
|
|
||||||
{0xEC7, 0xEC7}, {0xECE, 0xECF}, {0xEDA, 0xEDB}, {0xEE0, 0xEFF}, {0xF48, 0xF48}, {0xF6D, 0xF70}, {0xF98, 0xF98}, {0xFBD, 0xFBD}, {0xFCD, 0xFCD}, {0xFDB, 0xFFF}, {0x10C6, 0x10C6}, {0x10C8, 0x10CC},
|
|
||||||
{0x10CE, 0x10CF}, {0x1249, 0x1249}, {0x124E, 0x124F}, {0x1257, 0x1257}, {0x1259, 0x1259}, {0x125E, 0x125F}, {0x1289, 0x1289}, {0x128E, 0x128F}, {0x12B1, 0x12B1}, {0x12B6, 0x12B7}, {0x12BF, 0x12BF},
|
|
||||||
{0x12C1, 0x12C1}, {0x12C6, 0x12C7}, {0x12D7, 0x12D7}, {0x1311, 0x1311}, {0x1316, 0x1317}, {0x135B, 0x135C}, {0x137D, 0x137F}, {0x139A, 0x139F}, {0x13F6, 0x13F7}, {0x13FE, 0x13FF}, {0x169D, 0x169F},
|
|
||||||
{0x16F9, 0x16FF}, {0x170D, 0x170D}, {0x1715, 0x171F}, {0x1737, 0x173F}, {0x1754, 0x175F}, {0x176D, 0x176D}, {0x1771, 0x1771}, {0x1774, 0x177F}, {0x17DE, 0x17DF}, {0x17EA, 0x17EF}, {0x17FA, 0x17FF},
|
|
||||||
{0x180E, 0x180F}, {0x181A, 0x181F}, {0x1879, 0x187F}, {0x18AB, 0x18AF}, {0x18F6, 0x18FF}, {0x191F, 0x191F}, {0x192C, 0x192F}, {0x193C, 0x193F}, {0x1941, 0x1943}, {0x196E, 0x196F}, {0x1975, 0x197F},
|
|
||||||
{0x19AC, 0x19AF}, {0x19CA, 0x19CF}, {0x19DB, 0x19DD}, {0x1A1C, 0x1A1D}, {0x1A5F, 0x1A5F}, {0x1A7D, 0x1A7E}, {0x1A8A, 0x1A8F}, {0x1A9A, 0x1A9F}, {0x1AAE, 0x1AAF}, {0x1AC1, 0x1AFF}, {0x1B4C, 0x1B4F},
|
|
||||||
{0x1B7D, 0x1B7F}, {0x1BF4, 0x1BFB}, {0x1C38, 0x1C3A}, {0x1C4A, 0x1C4C}, {0x1C89, 0x1C8F}, {0x1CBB, 0x1CBC}, {0x1CC8, 0x1CCF}, {0x1CFB, 0x1CFF}, {0x1DFA, 0x1DFA}, {0x1F16, 0x1F17}, {0x1F1E, 0x1F1F},
|
|
||||||
{0x1F46, 0x1F47}, {0x1F4E, 0x1F4F}, {0x1F58, 0x1F58}, {0x1F5A, 0x1F5A}, {0x1F5C, 0x1F5C}, {0x1F5E, 0x1F5E}, {0x1F7E, 0x1F7F}, {0x1FB5, 0x1FB5}, {0x1FC5, 0x1FC5}, {0x1FD4, 0x1FD5}, {0x1FDC, 0x1FDC},
|
|
||||||
{0x1FF0, 0x1FF1}, {0x1FF5, 0x1FF5}, {0x1FFF, 0x1FFF}, {0x200B, 0x200F}, {0x202A, 0x202E}, {0x2060, 0x206F}, {0x2072, 0x2073}, {0x208F, 0x208F}, {0x209D, 0x209F}, {0x20C0, 0x20CF}, {0x20F1, 0x20FF},
|
|
||||||
{0x218C, 0x218F}, {0x2427, 0x243F}, {0x244B, 0x245F}, {0x2B74, 0x2B75}, {0x2B96, 0x2B96}, {0x2C2F, 0x2C2F}, {0x2C5F, 0x2C5F}, {0x2CF4, 0x2CF8}, {0x2D26, 0x2D26}, {0x2D28, 0x2D2C}, {0x2D2E, 0x2D2F},
|
|
||||||
{0x2D68, 0x2D6E}, {0x2D71, 0x2D7E}, {0x2D97, 0x2D9F}, {0x2DA7, 0x2DA7}, {0x2DAF, 0x2DAF}, {0x2DB7, 0x2DB7}, {0x2DBF, 0x2DBF}, {0x2DC7, 0x2DC7}, {0x2DCF, 0x2DCF}, {0x2DD7, 0x2DD7}, {0x2DDF, 0x2DDF},
|
|
||||||
{0x2E53, 0x2E7F}, {0x2E9A, 0x2E9A}, {0x2EF4, 0x2EFF}, {0x2FD6, 0x2FEF}, {0x2FFC, 0x2FFF}, {0x3040, 0x3040}, {0x3097, 0x3098}, {0x3100, 0x3104}, {0x3130, 0x3130}, {0x318F, 0x318F}, {0x31E4, 0x31EF},
|
|
||||||
{0x321F, 0x321F}, {0x9FFD, 0x9FFF}, {0xA48D, 0xA48F}, {0xA4C7, 0xA4CF}, {0xA62C, 0xA63F}, {0xA6F8, 0xA6FF}, {0xA7C0, 0xA7C1}, {0xA7CB, 0xA7F4}, {0xA82D, 0xA82F}, {0xA83A, 0xA83F}, {0xA878, 0xA87F},
|
|
||||||
{0xA8C6, 0xA8CD}, {0xA8DA, 0xA8DF}, {0xA954, 0xA95E}, {0xA97D, 0xA97F}, {0xA9CE, 0xA9CE}, {0xA9DA, 0xA9DD}, {0xA9FF, 0xA9FF}, {0xAA37, 0xAA3F}, {0xAA4E, 0xAA4F}, {0xAA5A, 0xAA5B}, {0xAAC3, 0xAADA},
|
|
||||||
{0xAAF7, 0xAB00}, {0xAB07, 0xAB08}, {0xAB0F, 0xAB10}, {0xAB17, 0xAB1F}, {0xAB27, 0xAB27}, {0xAB2F, 0xAB2F}, {0xAB6C, 0xAB6F}, {0xABEE, 0xABEF}, {0xABFA, 0xABFF}, {0xD7A4, 0xD7AF}, {0xD7C7, 0xD7CA},
|
|
||||||
{0xD7FC, 0xF8FF}, {0xFA6E, 0xFA6F}, {0xFADA, 0xFAFF}, {0xFB07, 0xFB12}, {0xFB18, 0xFB1C}, {0xFB37, 0xFB37}, {0xFB3D, 0xFB3D}, {0xFB3F, 0xFB3F}, {0xFB42, 0xFB42}, {0xFB45, 0xFB45}, {0xFBC2, 0xFBD2},
|
|
||||||
{0xFD40, 0xFD4F}, {0xFD90, 0xFD91}, {0xFDC8, 0xFDEF}, {0xFDFE, 0xFDFF}, {0xFE1A, 0xFE1F}, {0xFE53, 0xFE53}, {0xFE67, 0xFE67}, {0xFE6C, 0xFE6F}, {0xFE75, 0xFE75}, {0xFEFD, 0xFF00}, {0xFFBF, 0xFFC1},
|
|
||||||
{0xFFC8, 0xFFC9}, {0xFFD0, 0xFFD1}, {0xFFD8, 0xFFD9}, {0xFFDD, 0xFFDF}, {0xFFE7, 0xFFE7}, {0xFFEF, 0xFFFB}, {0xFFFE, 0xFFFF}, {0x1000C, 0x1000C}, {0x10027, 0x10027}, {0x1003B, 0x1003B},
|
|
||||||
{0x1003E, 0x1003E}, {0x1004E, 0x1004F}, {0x1005E, 0x1007F}, {0x100FB, 0x100FF}, {0x10103, 0x10106}, {0x10134, 0x10136}, {0x1018F, 0x1018F}, {0x1019D, 0x1019F}, {0x101A1, 0x101CF}, {0x101FE, 0x1027F},
|
|
||||||
{0x1029D, 0x1029F}, {0x102D1, 0x102DF}, {0x102FC, 0x102FF}, {0x10324, 0x1032C}, {0x1034B, 0x1034F}, {0x1037B, 0x1037F}, {0x1039E, 0x1039E}, {0x103C4, 0x103C7}, {0x103D6, 0x103FF}, {0x1049E, 0x1049F},
|
|
||||||
{0x104AA, 0x104AF}, {0x104D4, 0x104D7}, {0x104FC, 0x104FF}, {0x10528, 0x1052F}, {0x10564, 0x1056E}, {0x10570, 0x105FF}, {0x10737, 0x1073F}, {0x10756, 0x1075F}, {0x10768, 0x107FF}, {0x10806, 0x10807},
|
|
||||||
{0x10809, 0x10809}, {0x10836, 0x10836}, {0x10839, 0x1083B}, {0x1083D, 0x1083E}, {0x10856, 0x10856}, {0x1089F, 0x108A6}, {0x108B0, 0x108DF}, {0x108F3, 0x108F3}, {0x108F6, 0x108FA}, {0x1091C, 0x1091E},
|
|
||||||
{0x1093A, 0x1093E}, {0x10940, 0x1097F}, {0x109B8, 0x109BB}, {0x109D0, 0x109D1}, {0x10A04, 0x10A04}, {0x10A07, 0x10A0B}, {0x10A14, 0x10A14}, {0x10A18, 0x10A18}, {0x10A36, 0x10A37}, {0x10A3B, 0x10A3E},
|
|
||||||
{0x10A49, 0x10A4F}, {0x10A59, 0x10A5F}, {0x10AA0, 0x10ABF}, {0x10AE7, 0x10AEA}, {0x10AF7, 0x10AFF}, {0x10B36, 0x10B38}, {0x10B56, 0x10B57}, {0x10B73, 0x10B77}, {0x10B92, 0x10B98}, {0x10B9D, 0x10BA8},
|
|
||||||
{0x10BB0, 0x10BFF}, {0x10C49, 0x10C7F}, {0x10CB3, 0x10CBF}, {0x10CF3, 0x10CF9}, {0x10D28, 0x10D2F}, {0x10D3A, 0x10E5F}, {0x10E7F, 0x10E7F}, {0x10EAA, 0x10EAA}, {0x10EAE, 0x10EAF}, {0x10EB2, 0x10EFF},
|
|
||||||
{0x10F28, 0x10F2F}, {0x10F5A, 0x10FAF}, {0x10FCC, 0x10FDF}, {0x10FF7, 0x10FFF}, {0x1104E, 0x11051}, {0x11070, 0x1107E}, {0x110BD, 0x110BD}, {0x110C2, 0x110CF}, {0x110E9, 0x110EF}, {0x110FA, 0x110FF},
|
|
||||||
{0x11135, 0x11135}, {0x11148, 0x1114F}, {0x11177, 0x1117F}, {0x111E0, 0x111E0}, {0x111F5, 0x111FF}, {0x11212, 0x11212}, {0x1123F, 0x1127F}, {0x11287, 0x11287}, {0x11289, 0x11289}, {0x1128E, 0x1128E},
|
|
||||||
{0x1129E, 0x1129E}, {0x112AA, 0x112AF}, {0x112EB, 0x112EF}, {0x112FA, 0x112FF}, {0x11304, 0x11304}, {0x1130D, 0x1130E}, {0x11311, 0x11312}, {0x11329, 0x11329}, {0x11331, 0x11331}, {0x11334, 0x11334},
|
|
||||||
{0x1133A, 0x1133A}, {0x11345, 0x11346}, {0x11349, 0x1134A}, {0x1134E, 0x1134F}, {0x11351, 0x11356}, {0x11358, 0x1135C}, {0x11364, 0x11365}, {0x1136D, 0x1136F}, {0x11375, 0x113FF}, {0x1145C, 0x1145C},
|
|
||||||
{0x11462, 0x1147F}, {0x114C8, 0x114CF}, {0x114DA, 0x1157F}, {0x115B6, 0x115B7}, {0x115DE, 0x115FF}, {0x11645, 0x1164F}, {0x1165A, 0x1165F}, {0x1166D, 0x1167F}, {0x116B9, 0x116BF}, {0x116CA, 0x116FF},
|
|
||||||
{0x1171B, 0x1171C}, {0x1172C, 0x1172F}, {0x11740, 0x117FF}, {0x1183C, 0x1189F}, {0x118F3, 0x118FE}, {0x11907, 0x11908}, {0x1190A, 0x1190B}, {0x11914, 0x11914}, {0x11917, 0x11917}, {0x11936, 0x11936},
|
|
||||||
{0x11939, 0x1193A}, {0x11947, 0x1194F}, {0x1195A, 0x1199F}, {0x119A8, 0x119A9}, {0x119D8, 0x119D9}, {0x119E5, 0x119FF}, {0x11A48, 0x11A4F}, {0x11AA3, 0x11ABF}, {0x11AF9, 0x11BFF}, {0x11C09, 0x11C09},
|
|
||||||
{0x11C37, 0x11C37}, {0x11C46, 0x11C4F}, {0x11C6D, 0x11C6F}, {0x11C90, 0x11C91}, {0x11CA8, 0x11CA8}, {0x11CB7, 0x11CFF}, {0x11D07, 0x11D07}, {0x11D0A, 0x11D0A}, {0x11D37, 0x11D39}, {0x11D3B, 0x11D3B},
|
|
||||||
{0x11D3E, 0x11D3E}, {0x11D48, 0x11D4F}, {0x11D5A, 0x11D5F}, {0x11D66, 0x11D66}, {0x11D69, 0x11D69}, {0x11D8F, 0x11D8F}, {0x11D92, 0x11D92}, {0x11D99, 0x11D9F}, {0x11DAA, 0x11EDF}, {0x11EF9, 0x11FAF},
|
|
||||||
{0x11FB1, 0x11FBF}, {0x11FF2, 0x11FFE}, {0x1239A, 0x123FF}, {0x1246F, 0x1246F}, {0x12475, 0x1247F}, {0x12544, 0x12FFF}, {0x1342F, 0x143FF}, {0x14647, 0x167FF}, {0x16A39, 0x16A3F}, {0x16A5F, 0x16A5F},
|
|
||||||
{0x16A6A, 0x16A6D}, {0x16A70, 0x16ACF}, {0x16AEE, 0x16AEF}, {0x16AF6, 0x16AFF}, {0x16B46, 0x16B4F}, {0x16B5A, 0x16B5A}, {0x16B62, 0x16B62}, {0x16B78, 0x16B7C}, {0x16B90, 0x16E3F}, {0x16E9B, 0x16EFF},
|
|
||||||
{0x16F4B, 0x16F4E}, {0x16F88, 0x16F8E}, {0x16FA0, 0x16FDF}, {0x16FE5, 0x16FEF}, {0x16FF2, 0x16FFF}, {0x187F8, 0x187FF}, {0x18CD6, 0x18CFF}, {0x18D09, 0x1AFFF}, {0x1B11F, 0x1B14F}, {0x1B153, 0x1B163},
|
|
||||||
{0x1B168, 0x1B16F}, {0x1B2FC, 0x1BBFF}, {0x1BC6B, 0x1BC6F}, {0x1BC7D, 0x1BC7F}, {0x1BC89, 0x1BC8F}, {0x1BC9A, 0x1BC9B}, {0x1BCA0, 0x1CFFF}, {0x1D0F6, 0x1D0FF}, {0x1D127, 0x1D128}, {0x1D173, 0x1D17A},
|
|
||||||
{0x1D1E9, 0x1D1FF}, {0x1D246, 0x1D2DF}, {0x1D2F4, 0x1D2FF}, {0x1D357, 0x1D35F}, {0x1D379, 0x1D3FF}, {0x1D455, 0x1D455}, {0x1D49D, 0x1D49D}, {0x1D4A0, 0x1D4A1}, {0x1D4A3, 0x1D4A4}, {0x1D4A7, 0x1D4A8},
|
|
||||||
{0x1D4AD, 0x1D4AD}, {0x1D4BA, 0x1D4BA}, {0x1D4BC, 0x1D4BC}, {0x1D4C4, 0x1D4C4}, {0x1D506, 0x1D506}, {0x1D50B, 0x1D50C}, {0x1D515, 0x1D515}, {0x1D51D, 0x1D51D}, {0x1D53A, 0x1D53A}, {0x1D53F, 0x1D53F},
|
|
||||||
{0x1D545, 0x1D545}, {0x1D547, 0x1D549}, {0x1D551, 0x1D551}, {0x1D6A6, 0x1D6A7}, {0x1D7CC, 0x1D7CD}, {0x1DA8C, 0x1DA9A}, {0x1DAA0, 0x1DAA0}, {0x1DAB0, 0x1DFFF}, {0x1E007, 0x1E007}, {0x1E019, 0x1E01A},
|
|
||||||
{0x1E022, 0x1E022}, {0x1E025, 0x1E025}, {0x1E02B, 0x1E0FF}, {0x1E12D, 0x1E12F}, {0x1E13E, 0x1E13F}, {0x1E14A, 0x1E14D}, {0x1E150, 0x1E2BF}, {0x1E2FA, 0x1E2FE}, {0x1E300, 0x1E7FF}, {0x1E8C5, 0x1E8C6},
|
|
||||||
{0x1E8D7, 0x1E8FF}, {0x1E94C, 0x1E94F}, {0x1E95A, 0x1E95D}, {0x1E960, 0x1EC70}, {0x1ECB5, 0x1ED00}, {0x1ED3E, 0x1EDFF}, {0x1EE04, 0x1EE04}, {0x1EE20, 0x1EE20}, {0x1EE23, 0x1EE23}, {0x1EE25, 0x1EE26},
|
|
||||||
{0x1EE28, 0x1EE28}, {0x1EE33, 0x1EE33}, {0x1EE38, 0x1EE38}, {0x1EE3A, 0x1EE3A}, {0x1EE3C, 0x1EE41}, {0x1EE43, 0x1EE46}, {0x1EE48, 0x1EE48}, {0x1EE4A, 0x1EE4A}, {0x1EE4C, 0x1EE4C}, {0x1EE50, 0x1EE50},
|
|
||||||
{0x1EE53, 0x1EE53}, {0x1EE55, 0x1EE56}, {0x1EE58, 0x1EE58}, {0x1EE5A, 0x1EE5A}, {0x1EE5C, 0x1EE5C}, {0x1EE5E, 0x1EE5E}, {0x1EE60, 0x1EE60}, {0x1EE63, 0x1EE63}, {0x1EE65, 0x1EE66}, {0x1EE6B, 0x1EE6B},
|
|
||||||
{0x1EE73, 0x1EE73}, {0x1EE78, 0x1EE78}, {0x1EE7D, 0x1EE7D}, {0x1EE7F, 0x1EE7F}, {0x1EE8A, 0x1EE8A}, {0x1EE9C, 0x1EEA0}, {0x1EEA4, 0x1EEA4}, {0x1EEAA, 0x1EEAA}, {0x1EEBC, 0x1EEEF}, {0x1EEF2, 0x1EFFF},
|
|
||||||
{0x1F02C, 0x1F02F}, {0x1F094, 0x1F09F}, {0x1F0AF, 0x1F0B0}, {0x1F0C0, 0x1F0C0}, {0x1F0D0, 0x1F0D0}, {0x1F0F6, 0x1F0FF}, {0x1F1AE, 0x1F1E5}, {0x1F203, 0x1F20F}, {0x1F23C, 0x1F23F}, {0x1F249, 0x1F24F},
|
|
||||||
{0x1F252, 0x1F25F}, {0x1F266, 0x1F2FF}, {0x1F6D8, 0x1F6DF}, {0x1F6ED, 0x1F6EF}, {0x1F6FD, 0x1F6FF}, {0x1F774, 0x1F77F}, {0x1F7D9, 0x1F7DF}, {0x1F7EC, 0x1F7FF}, {0x1F80C, 0x1F80F}, {0x1F848, 0x1F84F},
|
|
||||||
{0x1F85A, 0x1F85F}, {0x1F888, 0x1F88F}, {0x1F8AE, 0x1F8AF}, {0x1F8B2, 0x1F8FF}, {0x1F979, 0x1F979}, {0x1F9CC, 0x1F9CC}, {0x1FA54, 0x1FA5F}, {0x1FA6E, 0x1FA6F}, {0x1FA75, 0x1FA77}, {0x1FA7B, 0x1FA7F},
|
|
||||||
{0x1FA87, 0x1FA8F}, {0x1FAA9, 0x1FAAF}, {0x1FAB7, 0x1FABF}, {0x1FAC3, 0x1FACF}, {0x1FAD7, 0x1FAFF}, {0x1FB93, 0x1FB93}, {0x1FBCB, 0x1FBEF}, {0x1FBFA, 0x1FFFF}, {0x2A6DE, 0x2A6FF}, {0x2B735, 0x2B73F},
|
|
||||||
{0x2B81E, 0x2B81F}, {0x2CEA2, 0x2CEAF}, {0x2EBE1, 0x2F7FF}, {0x2FA1E, 0x2FFFF}, {0x3134B, 0xE00FF}, {0xE01F0, 0x10FFFF},
|
|
||||||
};
|
|
||||||
|
|
||||||
static std::string codepoint_to_utf8(uint32_t cp) {
|
|
||||||
std::string result;
|
|
||||||
if (/* 0x00 <= cp && */ cp <= 0x7f) {
|
|
||||||
result.push_back(cp);
|
|
||||||
}
|
|
||||||
else if (0x80 <= cp && cp <= 0x7ff) {
|
|
||||||
result.push_back(0xc0 | ((cp >> 6) & 0x1f));
|
|
||||||
result.push_back(0x80 | (cp & 0x3f));
|
|
||||||
}
|
|
||||||
else if (0x800 <= cp && cp <= 0xffff) {
|
|
||||||
result.push_back(0xe0 | ((cp >> 12) & 0x0f));
|
|
||||||
result.push_back(0x80 | ((cp >> 6) & 0x3f));
|
|
||||||
result.push_back(0x80 | (cp & 0x3f));
|
|
||||||
}
|
|
||||||
else if (0x10000 <= cp && cp <= 0x10ffff) {
|
|
||||||
result.push_back(0xf0 | ((cp >> 18) & 0x07));
|
|
||||||
result.push_back(0x80 | ((cp >> 12) & 0x3f));
|
|
||||||
result.push_back(0x80 | ((cp >> 6) & 0x3f));
|
|
||||||
result.push_back(0x80 | (cp & 0x3f));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw std::invalid_argument("invalid codepoint");
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string codepoints_to_utf8(const std::vector<uint32_t> & cps) {
|
|
||||||
std::string result;
|
|
||||||
for (size_t i = 0; i < cps.size(); ++i) {
|
|
||||||
result.append(codepoint_to_utf8(cps[i]));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t codepoint_from_utf8(const std::string & utf8, size_t & offset) {
|
|
||||||
assert(offset < utf8.size());
|
|
||||||
if (!(utf8[offset + 0] & 0x80)) {
|
|
||||||
auto result = utf8[offset + 0];
|
|
||||||
offset += 1;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
else if (!(utf8[offset + 0] & 0x40)) {
|
|
||||||
throw std::invalid_argument("invalid character");
|
|
||||||
}
|
|
||||||
else if (!(utf8[offset + 0] & 0x20)) {
|
|
||||||
if (offset + 1 >= utf8.size() || ! ((utf8[offset + 1] & 0xc0) == 0x80))
|
|
||||||
throw std::invalid_argument("invalid character");
|
|
||||||
auto result = ((utf8[offset + 0] & 0x1f) << 6) | (utf8[offset + 1] & 0x3f);
|
|
||||||
offset += 2;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
else if (!(utf8[offset + 0] & 0x10)) {
|
|
||||||
if (offset + 2 >= utf8.size() || ! ((utf8[offset + 1] & 0xc0) == 0x80) || ! ((utf8[offset + 2] & 0xc0) == 0x80))
|
|
||||||
throw std::invalid_argument("invalid character");
|
|
||||||
auto result = ((utf8[offset + 0] & 0x0f) << 12) | ((utf8[offset + 1] & 0x3f) << 6) | (utf8[offset + 2] & 0x3f);
|
|
||||||
offset += 3;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
else if (!(utf8[offset + 0] & 0x08)) {
|
|
||||||
if (offset + 3 >= utf8.size() || ! ((utf8[offset + 1] & 0xc0) == 0x80) || ! ((utf8[offset + 2] & 0xc0) == 0x80) || !((utf8[offset + 3] & 0xc0) == 0x80))
|
|
||||||
throw std::invalid_argument("invalid character");
|
|
||||||
auto result = ((utf8[offset + 0] & 0x07) << 18) | ((utf8[offset + 1] & 0x3f) << 12) | ((utf8[offset + 2] & 0x3f) << 6) | (utf8[offset + 3] & 0x3f);
|
|
||||||
offset += 4;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
throw std::invalid_argument("invalid string");
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::vector<uint32_t> codepoints_from_utf8(const std::string & utf8) {
|
|
||||||
std::vector<uint32_t> result;
|
|
||||||
size_t offset = 0;
|
|
||||||
while (offset < utf8.size()) {
|
|
||||||
result.push_back(codepoint_from_utf8(utf8, offset));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::vector<uint16_t> codepoint_to_utf16(uint32_t cp) {
|
|
||||||
std::vector<uint16_t> result;
|
|
||||||
if (/* 0x0000 <= cp && */ cp <= 0xffff) {
|
|
||||||
result.emplace_back(cp);
|
|
||||||
}
|
|
||||||
else if (0x10000 <= cp && cp <= 0x10ffff) {
|
|
||||||
result.emplace_back(0xd800 | ((cp - 0x10000) >> 10));
|
|
||||||
result.emplace_back(0xdc00 | ((cp - 0x10000) & 0x03ff));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw std::invalid_argument("invalid codepoint");
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::vector<uint16_t> codepoints_to_utf16(const std::vector<uint32_t> & cps) {
|
|
||||||
std::vector<uint16_t> result;
|
|
||||||
for (size_t i = 0; i < cps.size(); ++i) {
|
|
||||||
auto temp = codepoint_to_utf16(cps[i]);
|
|
||||||
result.insert(result.end(), temp.begin(), temp.end());
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t codepoint_from_utf16(const std::vector<uint16_t> & utf16, size_t & offset) {
|
|
||||||
assert(offset < utf16.size());
|
|
||||||
if (((utf16[0] >> 10) << 10) != 0xd800) {
|
|
||||||
auto result = utf16[offset + 0];
|
|
||||||
offset += 1;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (offset + 1 >= utf16.size() || !((utf16[1] & 0xdc00) == 0xdc00))
|
|
||||||
throw std::invalid_argument("invalid character");
|
|
||||||
auto result = 0x10000 + (((utf16[0] & 0x03ff) << 10) | (utf16[1] & 0x03ff));
|
|
||||||
offset += 2;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
throw std::invalid_argument("invalid string");
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::vector<uint32_t> codepoints_from_utf16(const std::vector<uint16_t> & utf16) {
|
|
||||||
std::vector<uint32_t> result;
|
|
||||||
size_t offset = 0;
|
|
||||||
while (offset < utf16.size())
|
|
||||||
result.push_back(codepoint_from_utf16(utf16, offset));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define CODEPOINT_TYPE_UNIDENTIFIED 0
|
|
||||||
#define CODEPOINT_TYPE_DIGIT 1
|
|
||||||
#define CODEPOINT_TYPE_LETTER 2
|
|
||||||
#define CODEPOINT_TYPE_WHITESPACE 3
|
|
||||||
#define CODEPOINT_TYPE_ACCENT_MARK 4
|
|
||||||
#define CODEPOINT_TYPE_PUNCTUATION 5
|
|
||||||
#define CODEPOINT_TYPE_SYMBOL 6
|
|
||||||
#define CODEPOINT_TYPE_CONTROL 7
|
|
||||||
|
|
||||||
static std::unordered_map<uint32_t, int> codepoint_type_map() {
|
|
||||||
std::unordered_map<uint32_t, int> codepoint_types;
|
|
||||||
for (auto p : digit_ranges) {
|
|
||||||
for(auto i = p.first; i <= p.second; ++ i)
|
|
||||||
codepoint_types[i] = CODEPOINT_TYPE_DIGIT;
|
|
||||||
}
|
|
||||||
for(auto p : letter_ranges) {
|
|
||||||
for(auto i = p.first; i <= p.second; ++ i)
|
|
||||||
codepoint_types[i] = CODEPOINT_TYPE_LETTER;
|
|
||||||
}
|
|
||||||
for(auto p : whitespace_ranges) {
|
|
||||||
for(auto i = p.first; i <= p.second; ++ i)
|
|
||||||
codepoint_types[i] = CODEPOINT_TYPE_WHITESPACE;
|
|
||||||
}
|
|
||||||
for(auto p : accent_mark_ranges) {
|
|
||||||
for(auto i = p.first; i <= p.second; ++ i)
|
|
||||||
codepoint_types[i] = CODEPOINT_TYPE_ACCENT_MARK;
|
|
||||||
}
|
|
||||||
for(auto p : punctuation_ranges) {
|
|
||||||
for(auto i = p.first; i <= p.second; ++ i)
|
|
||||||
codepoint_types[i] = CODEPOINT_TYPE_PUNCTUATION;
|
|
||||||
}
|
|
||||||
for (auto p : symbol_ranges) {
|
|
||||||
for (auto i = p.first; i <= p.second; ++i)
|
|
||||||
codepoint_types[i] = CODEPOINT_TYPE_SYMBOL;
|
|
||||||
}
|
|
||||||
for(auto p : control_ranges) {
|
|
||||||
for(auto i = p.first; i <= p.second; ++ i)
|
|
||||||
codepoint_types[i] = CODEPOINT_TYPE_CONTROL;
|
|
||||||
}
|
|
||||||
return codepoint_types;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int codepoint_type(uint32_t cp) {
|
|
||||||
static std::unordered_map<uint32_t, int> codepoint_types = codepoint_type_map();
|
|
||||||
return codepoint_types[cp];
|
|
||||||
}
|
|
||||||
|
|
||||||
static int codepoint_type(const std::string & utf8) {
|
|
||||||
if (utf8.length() == 0)
|
|
||||||
return CODEPOINT_TYPE_UNIDENTIFIED;
|
|
||||||
size_t offset = 0;
|
|
||||||
return codepoint_type(codepoint_from_utf8(utf8, offset));
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::unordered_map<uint8_t, std::string> bytes_to_unicode_map_bpe() {
|
|
||||||
std::unordered_map<uint8_t, std::string> map;
|
|
||||||
for (int ch = u'!'; ch <= u'~'; ++ch) {
|
|
||||||
assert(0 <= ch && ch < 256);
|
|
||||||
map[ch] = codepoint_to_utf8(ch);
|
|
||||||
}
|
|
||||||
for (int ch = u'¡'; ch <= u'¬'; ++ch) {
|
|
||||||
assert(0 <= ch && ch < 256);
|
|
||||||
map[ch] = codepoint_to_utf8(ch);
|
|
||||||
}
|
|
||||||
for (int ch = u'®'; ch <= u'ÿ'; ++ch) {
|
|
||||||
assert(0 <= ch && ch < 256);
|
|
||||||
map[ch] = codepoint_to_utf8(ch);
|
|
||||||
}
|
|
||||||
auto n = 0;
|
|
||||||
for (int ch = 0; ch < 256; ++ch) {
|
|
||||||
if (map.find(ch) == map.end()) {
|
|
||||||
map[ch] = codepoint_to_utf8(256 + n);
|
|
||||||
++n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string bytes_to_unicode_bpe(uint8_t byte) {
|
|
||||||
static std::unordered_map<uint8_t, std::string> map = bytes_to_unicode_map_bpe();
|
|
||||||
return map.at(byte);
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::unordered_map<std::string, uint8_t> unicode_to_bytes_map_bpe() {
|
|
||||||
std::unordered_map<std::string, uint8_t> map;
|
|
||||||
for (int ch = u'!'; ch <= u'~'; ++ch) {
|
|
||||||
assert(0 <= ch && ch < 256);
|
|
||||||
map[codepoint_to_utf8(ch)] = ch;
|
|
||||||
}
|
|
||||||
for (int ch = u'¡'; ch <= u'¬'; ++ch) {
|
|
||||||
assert(0 <= ch && ch < 256);
|
|
||||||
map[codepoint_to_utf8(ch)] = ch;
|
|
||||||
}
|
|
||||||
for (int ch = u'®'; ch <= u'ÿ'; ++ch) {
|
|
||||||
assert(0 <= ch && ch < 256);
|
|
||||||
map[codepoint_to_utf8(ch)] = ch;
|
|
||||||
}
|
|
||||||
auto n = 0;
|
|
||||||
for (int ch = 0; ch < 256; ++ch) {
|
|
||||||
if (map.find(codepoint_to_utf8(ch)) == map.end()) {
|
|
||||||
map[codepoint_to_utf8(256 + n)] = ch;
|
|
||||||
++n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t unicode_to_bytes_bpe(const std::string & utf8) {
|
|
||||||
static std::unordered_map<std::string, uint8_t> map = unicode_to_bytes_map_bpe();
|
|
||||||
return map.at(utf8);
|
|
||||||
}
|
|
||||||
|
|
@ -271,7 +271,7 @@ EMSCRIPTEN_BINDINGS(talk) {
|
|||||||
emscripten::function("init", emscripten::optional_override([](const std::string & path_model) {
|
emscripten::function("init", emscripten::optional_override([](const std::string & path_model) {
|
||||||
for (size_t i = 0; i < g_contexts.size(); ++i) {
|
for (size_t i = 0; i < g_contexts.size(); ++i) {
|
||||||
if (g_contexts[i] == nullptr) {
|
if (g_contexts[i] == nullptr) {
|
||||||
g_contexts[i] = whisper_init_from_file_with_params(path_model.c_str(), whisper_context_default_params());
|
g_contexts[i] = whisper_init_from_file(path_model.c_str());
|
||||||
if (g_contexts[i] != nullptr) {
|
if (g_contexts[i] != nullptr) {
|
||||||
g_running = true;
|
g_running = true;
|
||||||
if (g_worker.joinable()) {
|
if (g_worker.joinable()) {
|
||||||
|
@ -155,33 +155,33 @@ bool gpt2_model_load(const std::string & fname, gpt2_model & model, gpt_vocab &
|
|||||||
const int n_ctx = hparams.n_ctx;
|
const int n_ctx = hparams.n_ctx;
|
||||||
const int n_vocab = hparams.n_vocab;
|
const int n_vocab = hparams.n_vocab;
|
||||||
|
|
||||||
ctx_size += ggml_row_size(GGML_TYPE_F32, n_embd); // ln_f_g
|
ctx_size += n_embd*ggml_type_sizef(GGML_TYPE_F32); // ln_f_g
|
||||||
ctx_size += ggml_row_size(GGML_TYPE_F32, n_embd); // ln_f_b
|
ctx_size += n_embd*ggml_type_sizef(GGML_TYPE_F32); // ln_f_b
|
||||||
|
|
||||||
ctx_size += n_vocab*ggml_row_size(wtype, n_embd); // wte
|
ctx_size += n_vocab*n_embd*ggml_type_sizef(wtype); // wte
|
||||||
ctx_size += n_ctx*ggml_row_size(GGML_TYPE_F32, n_embd); // wpe
|
ctx_size += n_ctx*n_embd*ggml_type_sizef(GGML_TYPE_F32); // wpe
|
||||||
ctx_size += n_vocab*ggml_row_size(wtype, n_embd); // lm_head
|
ctx_size += n_vocab*n_embd*ggml_type_sizef(wtype); // lm_head
|
||||||
|
|
||||||
ctx_size += n_layer*(ggml_row_size(GGML_TYPE_F32, n_embd)); // ln_1_g
|
ctx_size += n_layer*(n_embd*ggml_type_sizef(GGML_TYPE_F32)); // ln_1_g
|
||||||
ctx_size += n_layer*(ggml_row_size(GGML_TYPE_F32, n_embd)); // ln_1_b
|
ctx_size += n_layer*(n_embd*ggml_type_sizef(GGML_TYPE_F32)); // ln_1_b
|
||||||
|
|
||||||
ctx_size += n_layer*(ggml_row_size(GGML_TYPE_F32, n_embd)); // ln_2_g
|
ctx_size += n_layer*(n_embd*ggml_type_sizef(GGML_TYPE_F32)); // ln_2_g
|
||||||
ctx_size += n_layer*(ggml_row_size(GGML_TYPE_F32, n_embd)); // ln_2_b
|
ctx_size += n_layer*(n_embd*ggml_type_sizef(GGML_TYPE_F32)); // ln_2_b
|
||||||
|
|
||||||
ctx_size += n_layer*(ggml_row_size(wtype, 3*n_embd*n_embd)); // c_attn_attn_w
|
ctx_size += n_layer*(3*n_embd*n_embd*ggml_type_sizef(wtype)); // c_attn_attn_w
|
||||||
ctx_size += n_layer*(ggml_row_size(GGML_TYPE_F32, 3*n_embd)); // c_attn_attn_b
|
ctx_size += n_layer*( 3*n_embd*ggml_type_sizef(GGML_TYPE_F32)); // c_attn_attn_b
|
||||||
|
|
||||||
ctx_size += n_layer*(ggml_row_size(wtype, n_embd*n_embd)); // c_attn_proj_w
|
ctx_size += n_layer*(n_embd*n_embd*ggml_type_sizef(wtype)); // c_attn_proj_w
|
||||||
ctx_size += n_layer*(ggml_row_size(GGML_TYPE_F32, n_embd)); // c_attn_proj_b
|
ctx_size += n_layer*( n_embd*ggml_type_sizef(GGML_TYPE_F32)); // c_attn_proj_b
|
||||||
|
|
||||||
ctx_size += n_layer*(ggml_row_size(wtype, 4*n_embd*n_embd)); // c_mlp_fc_w
|
ctx_size += n_layer*(4*n_embd*n_embd*ggml_type_sizef(wtype)); // c_mlp_fc_w
|
||||||
ctx_size += n_layer*(ggml_row_size(GGML_TYPE_F32, 4*n_embd)); // c_mlp_fc_b
|
ctx_size += n_layer*( 4*n_embd*ggml_type_sizef(GGML_TYPE_F32)); // c_mlp_fc_b
|
||||||
|
|
||||||
ctx_size += n_layer*(ggml_row_size(wtype, 4*n_embd*n_embd)); // c_mlp_proj_w
|
ctx_size += n_layer*(4*n_embd*n_embd*ggml_type_sizef(wtype)); // c_mlp_proj_w
|
||||||
ctx_size += n_layer*(ggml_row_size(GGML_TYPE_F32, n_embd)); // c_mlp_proj_b
|
ctx_size += n_layer*( n_embd*ggml_type_sizef(GGML_TYPE_F32)); // c_mlp_proj_b
|
||||||
|
|
||||||
ctx_size += n_ctx*n_layer*ggml_row_size(GGML_TYPE_F32, n_embd); // memory_k
|
ctx_size += n_ctx*n_layer*n_embd*ggml_type_sizef(GGML_TYPE_F32); // memory_k
|
||||||
ctx_size += n_ctx*n_layer*ggml_row_size(GGML_TYPE_F32, n_embd); // memory_v
|
ctx_size += n_ctx*n_layer*n_embd*ggml_type_sizef(GGML_TYPE_F32); // memory_v
|
||||||
|
|
||||||
ctx_size += (6 + 12*n_layer)*256; // object overhead
|
ctx_size += (6 + 12*n_layer)*256; // object overhead
|
||||||
|
|
||||||
@ -524,7 +524,8 @@ bool gpt2_eval(
|
|||||||
struct ggml_tensor * KQ_scaled =
|
struct ggml_tensor * KQ_scaled =
|
||||||
ggml_scale(ctx0,
|
ggml_scale(ctx0,
|
||||||
KQ,
|
KQ,
|
||||||
1.0f/sqrt(float(n_embd)/n_head));
|
ggml_new_f32(ctx0, 1.0f/sqrt(float(n_embd)/n_head))
|
||||||
|
);
|
||||||
|
|
||||||
// KQ_masked = mask_past(KQ_scaled)
|
// KQ_masked = mask_past(KQ_scaled)
|
||||||
// [n_past + N, N, 12]
|
// [n_past + N, N, 12]
|
||||||
|
@ -31,7 +31,7 @@ To run this, you will need a ggml GPT-2 model: [instructions](https://github.com
|
|||||||
Alternatively, you can simply download the smallest ggml GPT-2 117M model (240 MB) like this:
|
Alternatively, you can simply download the smallest ggml GPT-2 117M model (240 MB) like this:
|
||||||
|
|
||||||
```
|
```
|
||||||
wget --quiet --show-progress -O models/ggml-gpt-2-117M.bin https://huggingface.co/ggerganov/ggml/resolve/main/ggml-model-gpt-2-117M.bin
|
wget --quiet --show-progress -O models/ggml-gpt-2-117M.bin https://huggingface.co/ggerganov/ggml/raw/main/ggml-model-gpt-2-117M.bin
|
||||||
```
|
```
|
||||||
|
|
||||||
## TTS
|
## TTS
|
||||||
|
@ -121,13 +121,13 @@ bool gpt2_model_load(const std::string & fname, gpt2_model & model, gpt_vocab &
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char word[129];
|
std::string word;
|
||||||
|
|
||||||
for (int i = 0; i < n_vocab; i++) {
|
for (int i = 0; i < n_vocab; i++) {
|
||||||
uint32_t len;
|
uint32_t len;
|
||||||
fin.read((char *) &len, sizeof(len));
|
fin.read((char *) &len, sizeof(len));
|
||||||
word[len] = '\0';
|
|
||||||
fin.read((char *) word, len);
|
word.resize(len);
|
||||||
|
fin.read((char *) word.data(), len);
|
||||||
|
|
||||||
vocab.token_to_id[word] = i;
|
vocab.token_to_id[word] = i;
|
||||||
vocab.id_to_token[i] = word;
|
vocab.id_to_token[i] = word;
|
||||||
@ -155,33 +155,33 @@ bool gpt2_model_load(const std::string & fname, gpt2_model & model, gpt_vocab &
|
|||||||
const int n_ctx = hparams.n_ctx;
|
const int n_ctx = hparams.n_ctx;
|
||||||
const int n_vocab = hparams.n_vocab;
|
const int n_vocab = hparams.n_vocab;
|
||||||
|
|
||||||
ctx_size += ggml_row_size(GGML_TYPE_F32, n_embd); // ln_f_g
|
ctx_size += n_embd*ggml_type_sizef(GGML_TYPE_F32); // ln_f_g
|
||||||
ctx_size += ggml_row_size(GGML_TYPE_F32, n_embd); // ln_f_b
|
ctx_size += n_embd*ggml_type_sizef(GGML_TYPE_F32); // ln_f_b
|
||||||
|
|
||||||
ctx_size += n_vocab*ggml_row_size(wtype, n_embd); // wte
|
ctx_size += n_vocab*n_embd*ggml_type_sizef(wtype); // wte
|
||||||
ctx_size += n_ctx*ggml_row_size(GGML_TYPE_F32, n_embd); // wpe
|
ctx_size += n_ctx*n_embd*ggml_type_sizef(GGML_TYPE_F32); // wpe
|
||||||
ctx_size += n_vocab*ggml_row_size(wtype, n_embd); // lm_head
|
ctx_size += n_vocab*n_embd*ggml_type_sizef(wtype); // lm_head
|
||||||
|
|
||||||
ctx_size += n_layer*(ggml_row_size(GGML_TYPE_F32, n_embd)); // ln_1_g
|
ctx_size += n_layer*(n_embd*ggml_type_sizef(GGML_TYPE_F32)); // ln_1_g
|
||||||
ctx_size += n_layer*(ggml_row_size(GGML_TYPE_F32, n_embd)); // ln_1_b
|
ctx_size += n_layer*(n_embd*ggml_type_sizef(GGML_TYPE_F32)); // ln_1_b
|
||||||
|
|
||||||
ctx_size += n_layer*(ggml_row_size(GGML_TYPE_F32, n_embd)); // ln_2_g
|
ctx_size += n_layer*(n_embd*ggml_type_sizef(GGML_TYPE_F32)); // ln_2_g
|
||||||
ctx_size += n_layer*(ggml_row_size(GGML_TYPE_F32, n_embd)); // ln_2_b
|
ctx_size += n_layer*(n_embd*ggml_type_sizef(GGML_TYPE_F32)); // ln_2_b
|
||||||
|
|
||||||
ctx_size += n_layer*(ggml_row_size(wtype, 3*n_embd*n_embd)); // c_attn_attn_w
|
ctx_size += n_layer*(3*n_embd*n_embd*ggml_type_sizef(wtype)); // c_attn_attn_w
|
||||||
ctx_size += n_layer*(ggml_row_size(GGML_TYPE_F32, 3*n_embd)); // c_attn_attn_b
|
ctx_size += n_layer*( 3*n_embd*ggml_type_sizef(GGML_TYPE_F32)); // c_attn_attn_b
|
||||||
|
|
||||||
ctx_size += n_layer*(ggml_row_size(wtype, n_embd*n_embd)); // c_attn_proj_w
|
ctx_size += n_layer*(n_embd*n_embd*ggml_type_sizef(wtype)); // c_attn_proj_w
|
||||||
ctx_size += n_layer*(ggml_row_size(GGML_TYPE_F32, n_embd)); // c_attn_proj_b
|
ctx_size += n_layer*( n_embd*ggml_type_sizef(GGML_TYPE_F32)); // c_attn_proj_b
|
||||||
|
|
||||||
ctx_size += n_layer*(ggml_row_size(wtype, 4*n_embd*n_embd)); // c_mlp_fc_w
|
ctx_size += n_layer*(4*n_embd*n_embd*ggml_type_sizef(wtype)); // c_mlp_fc_w
|
||||||
ctx_size += n_layer*(ggml_row_size(GGML_TYPE_F32, 4*n_embd)); // c_mlp_fc_b
|
ctx_size += n_layer*( 4*n_embd*ggml_type_sizef(GGML_TYPE_F32)); // c_mlp_fc_b
|
||||||
|
|
||||||
ctx_size += n_layer*(ggml_row_size(wtype, 4*n_embd*n_embd)); // c_mlp_proj_w
|
ctx_size += n_layer*(4*n_embd*n_embd*ggml_type_sizef(wtype)); // c_mlp_proj_w
|
||||||
ctx_size += n_layer*(ggml_row_size(GGML_TYPE_F32, n_embd)); // c_mlp_proj_b
|
ctx_size += n_layer*( n_embd*ggml_type_sizef(GGML_TYPE_F32)); // c_mlp_proj_b
|
||||||
|
|
||||||
ctx_size += n_ctx*n_layer*ggml_row_size(GGML_TYPE_F32, n_embd); // memory_k
|
ctx_size += n_ctx*n_layer*n_embd*ggml_type_sizef(GGML_TYPE_F32); // memory_k
|
||||||
ctx_size += n_ctx*n_layer*ggml_row_size(GGML_TYPE_F32, n_embd); // memory_v
|
ctx_size += n_ctx*n_layer*n_embd*ggml_type_sizef(GGML_TYPE_F32); // memory_v
|
||||||
|
|
||||||
ctx_size += (6 + 12*n_layer)*256; // object overhead
|
ctx_size += (6 + 12*n_layer)*256; // object overhead
|
||||||
|
|
||||||
@ -525,7 +525,8 @@ bool gpt2_eval(
|
|||||||
struct ggml_tensor * KQ_scaled =
|
struct ggml_tensor * KQ_scaled =
|
||||||
ggml_scale(ctx0,
|
ggml_scale(ctx0,
|
||||||
KQ,
|
KQ,
|
||||||
1.0f/sqrt(float(n_embd)/n_head));
|
ggml_new_f32(ctx0, 1.0f/sqrt(float(n_embd)/n_head))
|
||||||
|
);
|
||||||
|
|
||||||
// KQ_masked = mask_past(KQ_scaled)
|
// KQ_masked = mask_past(KQ_scaled)
|
||||||
// [n_past + N, N, 12]
|
// [n_past + N, N, 12]
|
||||||
|
@ -31,7 +31,6 @@ struct whisper_params {
|
|||||||
bool print_special = false;
|
bool print_special = false;
|
||||||
bool print_energy = false;
|
bool print_energy = false;
|
||||||
bool no_timestamps = true;
|
bool no_timestamps = true;
|
||||||
bool use_gpu = true;
|
|
||||||
|
|
||||||
std::string person = "Santa";
|
std::string person = "Santa";
|
||||||
std::string language = "en";
|
std::string language = "en";
|
||||||
@ -62,7 +61,6 @@ bool whisper_params_parse(int argc, char ** argv, whisper_params & params) {
|
|||||||
else if (arg == "-tr" || arg == "--translate") { params.translate = true; }
|
else if (arg == "-tr" || arg == "--translate") { params.translate = true; }
|
||||||
else if (arg == "-ps" || arg == "--print-special") { params.print_special = 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 == "-pe" || arg == "--print-energy") { params.print_energy = true; }
|
||||||
else if (arg == "-ng" || arg == "--no-gpu") { params.use_gpu = false; }
|
|
||||||
else if (arg == "-p" || arg == "--person") { params.person = argv[++i]; }
|
else if (arg == "-p" || arg == "--person") { params.person = argv[++i]; }
|
||||||
else if (arg == "-l" || arg == "--language") { params.language = argv[++i]; }
|
else if (arg == "-l" || arg == "--language") { params.language = argv[++i]; }
|
||||||
else if (arg == "-mw" || arg == "--model-whisper") { params.model_wsp = argv[++i]; }
|
else if (arg == "-mw" || arg == "--model-whisper") { params.model_wsp = argv[++i]; }
|
||||||
@ -96,7 +94,6 @@ void whisper_print_usage(int /*argc*/, char ** argv, const whisper_params & para
|
|||||||
fprintf(stderr, " -tr, --translate [%-7s] translate from source language to english\n", params.translate ? "true" : "false");
|
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, " -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, " -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, " -p NAME, --person NAME [%-7s] person name (for prompt selection)\n", params.person.c_str());
|
fprintf(stderr, " -p NAME, --person NAME [%-7s] person name (for prompt selection)\n", params.person.c_str());
|
||||||
fprintf(stderr, " -l LANG, --language LANG [%-7s] spoken language\n", params.language.c_str());
|
fprintf(stderr, " -l LANG, --language LANG [%-7s] spoken language\n", params.language.c_str());
|
||||||
fprintf(stderr, " -mw FILE, --model-whisper [%-7s] whisper model file\n", params.model_wsp.c_str());
|
fprintf(stderr, " -mw FILE, --model-whisper [%-7s] whisper model file\n", params.model_wsp.c_str());
|
||||||
@ -184,10 +181,8 @@ int main(int argc, char ** argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// whisper init
|
// whisper init
|
||||||
struct whisper_context_params cparams = whisper_context_default_params();
|
|
||||||
cparams.use_gpu = params.use_gpu;
|
|
||||||
|
|
||||||
struct whisper_context * ctx_wsp = whisper_init_from_file_with_params(params.model_wsp.c_str(), cparams);
|
struct whisper_context * ctx_wsp = whisper_init_from_file(params.model_wsp.c_str());
|
||||||
|
|
||||||
// gpt init
|
// gpt init
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ help()
|
|||||||
echo "Usage: ./twitch.sh -s [step] -m [model] -t [threads] [url]"
|
echo "Usage: ./twitch.sh -s [step] -m [model] -t [threads] [url]"
|
||||||
echo "options:"
|
echo "options:"
|
||||||
echo "-s Step in seconds (default is $step)."
|
echo "-s Step in seconds (default is $step)."
|
||||||
echo "-m Choose model, options are: 'tiny.en' 'tiny' 'base.en' 'base' 'small.en' 'small' 'medium.en' 'medium' 'large-v1' 'large-v2' 'large-v3' (default is '$model')."
|
echo "-m Choose model, options are: 'tiny.en' 'tiny' 'base.en' 'base' 'small.en' 'small' 'medium.en' 'medium' 'large-v1' 'large' (default is '$model')."
|
||||||
echo "-t Number of threads to use."
|
echo "-t Number of threads to use."
|
||||||
echo "-h Print this help page."
|
echo "-h Print this help page."
|
||||||
echo
|
echo
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
set(CMAKE_CXX_STANDARD 11)
|
|
||||||
|
|
||||||
add_subdirectory(libwchess)
|
|
||||||
|
|
||||||
if (EMSCRIPTEN)
|
|
||||||
add_subdirectory(wchess.wasm)
|
|
||||||
else()
|
|
||||||
add_subdirectory(wchess.cmd)
|
|
||||||
endif()
|
|
@ -1,45 +0,0 @@
|
|||||||
# wchess
|
|
||||||
|
|
||||||
Voice-controlled chess using Whisper
|
|
||||||
|
|
||||||
Online demo: https://whisper.ggerganov.com/wchess/
|
|
||||||
|
|
||||||
https://github.com/ggerganov/whisper.cpp/assets/1991296/c2b2f03c-9684-49f3-8106-357d2d4e67fa
|
|
||||||
|
|
||||||
## Command-line tool
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir build && cd build
|
|
||||||
cmake -DWHISPER_SDL2=1 ..
|
|
||||||
make -j
|
|
||||||
|
|
||||||
./bin/wchess -m ../models/ggml-base.en.bin
|
|
||||||
|
|
||||||
Move: start
|
|
||||||
|
|
||||||
a b c d e f g h
|
|
||||||
r n b q k b n r 8
|
|
||||||
p p p p p p p p 7
|
|
||||||
. * . * . * . * 6
|
|
||||||
* . * . * . * . 5
|
|
||||||
. * . * . * . * 4
|
|
||||||
* . * . * . * . 3
|
|
||||||
P P P P P P P P 2
|
|
||||||
R N B Q K B N R 1
|
|
||||||
|
|
||||||
White's turn
|
|
||||||
[(l)isten/(p)ause/(q)uit]:
|
|
||||||
```
|
|
||||||
|
|
||||||
## TODO
|
|
||||||
|
|
||||||
- Fix bugs in the chess moves logic
|
|
||||||
- Improve web-browser audio capture - sometimes it does not record the voice properly
|
|
||||||
- Add support for more languages by making the generated grammar string multilingual
|
|
||||||
- Explore ways to improve the dynamic grammar to be narrower
|
|
||||||
|
|
||||||
PRs welcome!
|
|
||||||
|
|
||||||
## Thanks
|
|
||||||
|
|
||||||
- [chessboardjs](https://chessboardjs.com) for the neat chessboard JS library used in this demo
|
|
@ -1,19 +0,0 @@
|
|||||||
add_library(wchess-core STATIC
|
|
||||||
WChess.cpp
|
|
||||||
WChess.h
|
|
||||||
Chessboard.cpp
|
|
||||||
Chessboard.h
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(wchess-core
|
|
||||||
PUBLIC
|
|
||||||
whisper
|
|
||||||
common
|
|
||||||
)
|
|
||||||
|
|
||||||
target_include_directories(wchess-core
|
|
||||||
PUBLIC
|
|
||||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
|
|
||||||
)
|
|
||||||
|
|
||||||
# add_executable(test-chessboard test-chessboard.cpp Chessboard.cpp)
|
|
@ -1,803 +0,0 @@
|
|||||||
#include "Chessboard.h"
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <vector>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cstring>
|
|
||||||
#include <set>
|
|
||||||
#include <list>
|
|
||||||
#include <chrono>
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
constexpr std::array<const char*, 64> positions = {
|
|
||||||
"a1", "b1", "c1", "d1", "e1", "f1", "g1", "h1",
|
|
||||||
"a2", "b2", "c2", "d2", "e2", "f2", "g2", "h2",
|
|
||||||
"a3", "b3", "c3", "d3", "e3", "f3", "g3", "h3",
|
|
||||||
"a4", "b4", "c4", "d4", "e4", "f4", "g4", "h4",
|
|
||||||
"a5", "b5", "c5", "d5", "e5", "f5", "g5", "h5",
|
|
||||||
"a6", "b6", "c6", "d6", "e6", "f6", "g6", "h6",
|
|
||||||
"a7", "b7", "c7", "d7", "e7", "f7", "g7", "h7",
|
|
||||||
"a8", "b8", "c8", "d8", "e8", "f8", "g8", "h8",
|
|
||||||
};
|
|
||||||
constexpr char INVALID_POS = positions.size();
|
|
||||||
constexpr int R = 0; // rank index
|
|
||||||
constexpr int F = 1; // file index
|
|
||||||
#define FILE (c[F] - '1')
|
|
||||||
#define RANK (c[R] - 'a')
|
|
||||||
constexpr char operator ""_P(const char * c, size_t size) {
|
|
||||||
return size < 2 || RANK < 0 || RANK > 7 ||
|
|
||||||
FILE < 0 || FILE > 7 ? INVALID_POS : FILE * 8 + RANK;
|
|
||||||
}
|
|
||||||
#undef FILE
|
|
||||||
#undef RANK
|
|
||||||
|
|
||||||
struct sview {
|
|
||||||
const char * ptr = nullptr;
|
|
||||||
size_t size = 0;
|
|
||||||
|
|
||||||
sview() = default;
|
|
||||||
sview(const char * p, size_t s) : ptr(p), size(s) {}
|
|
||||||
sview(const std::string& s) : ptr(s.data()), size(s.size()) {}
|
|
||||||
|
|
||||||
size_t find(char del, size_t pos) {
|
|
||||||
while (pos < size && ptr[pos] != del) ++pos;
|
|
||||||
return pos < size ? pos : std::string::npos;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<sview> split(sview str, char del) {
|
|
||||||
std::vector<sview> res;
|
|
||||||
size_t cur = 0;
|
|
||||||
size_t last = 0;
|
|
||||||
while (cur != std::string::npos) {
|
|
||||||
if (str.ptr[last] == ' ') {
|
|
||||||
++last;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
cur = str.find(del, last);
|
|
||||||
size_t len = cur == std::string::npos ? str.size - last : cur - last;
|
|
||||||
res.emplace_back(str.ptr + last, len);
|
|
||||||
last = cur + 1;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
char strToPos(sview str) {
|
|
||||||
return operator ""_P(str.ptr, str.size);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr std::array<const char*, 6> pieceNames = {
|
|
||||||
"pawn", "knight", "bishop", "rook", "queen", "king",
|
|
||||||
};
|
|
||||||
|
|
||||||
static constexpr std::array<char, 6> blackShort = {
|
|
||||||
'p', 'n', 'b', 'r', 'q', 'k',
|
|
||||||
};
|
|
||||||
static constexpr std::array<char, 6> whiteShort = {
|
|
||||||
'P', 'N', 'B', 'R', 'Q', 'K',
|
|
||||||
};
|
|
||||||
|
|
||||||
char strToType(sview str) {
|
|
||||||
auto it = std::find_if(pieceNames.begin(), pieceNames.end(), [str] (const char* name) { return strncmp(name, str.ptr, str.size) == 0; });
|
|
||||||
return it != pieceNames.end() ? it - pieceNames.begin() : pieceNames.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
// directions
|
|
||||||
using Direction = std::array<char, 2>;
|
|
||||||
|
|
||||||
constexpr Direction N = {(char) 0, (char) 1};
|
|
||||||
constexpr Direction NNE = {(char) 1, (char) 2};
|
|
||||||
constexpr Direction NE = {(char) 1, (char) 1};
|
|
||||||
constexpr Direction ENE = {(char) 2, (char) 1};
|
|
||||||
constexpr Direction E = {(char) 1, (char) 0};
|
|
||||||
constexpr Direction ESE = {(char) 2, (char) -1};
|
|
||||||
constexpr Direction SE = {(char) 1, (char) -1};
|
|
||||||
constexpr Direction SSE = {(char) 1, (char) -2};
|
|
||||||
constexpr Direction S = {(char) 0, (char) -1};
|
|
||||||
constexpr Direction SSW = {(char) -1, (char) -2};
|
|
||||||
constexpr Direction SW = {(char) -1, (char) -1};
|
|
||||||
constexpr Direction WSW = {(char) -2, (char) -1};
|
|
||||||
constexpr Direction W = {(char) -1, (char) 0};
|
|
||||||
constexpr Direction WNW = {(char) -2, (char) 1};
|
|
||||||
constexpr Direction NW = {(char) -1, (char) 1};
|
|
||||||
constexpr Direction NNW = {(char) -1, (char) 2};
|
|
||||||
|
|
||||||
char makeStep(char pos, const Direction& d) {
|
|
||||||
char next[2] = { char(positions[pos][R] + d[R]) , char(positions[pos][F] + d[F]) };
|
|
||||||
return strToPos(sview{next, sizeof(next)});
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Modifier>
|
|
||||||
char traverse(char pos, const Direction& d, const Modifier& m, int count = 8) {
|
|
||||||
while (--count >= 0) {
|
|
||||||
pos = makeStep(pos, d);
|
|
||||||
if (pos == INVALID_POS || m(pos)) break;
|
|
||||||
}
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
Direction normalize(const Direction& distance) {
|
|
||||||
//return {char((distance[R] > 0) - (distance[R] < 0)), char((distance[F] > 0) - (distance[F] < 0))};
|
|
||||||
const int drp = distance[R] > 0 ? 1 : 0;
|
|
||||||
const int drn = distance[R] < 0 ? 1 : 0;
|
|
||||||
const int dfp = distance[F] > 0 ? 1 : 0;
|
|
||||||
const int dfn = distance[F] < 0 ? 1 : 0;
|
|
||||||
return {char(drp - drn), char(dfp - dfn)};
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Pin {
|
|
||||||
Direction d;
|
|
||||||
Piece* pinner;
|
|
||||||
Piece* pinned;
|
|
||||||
};
|
|
||||||
using Pins = std::list<Pin>;
|
|
||||||
using Board = std::array<Piece*, 64>;
|
|
||||||
|
|
||||||
std::vector<Direction> filter(const Direction& pin, std::initializer_list<Direction> directions) {
|
|
||||||
if (pin[R] == 0 && pin[F] == 0) return directions;
|
|
||||||
std::vector<Direction> result;
|
|
||||||
for (auto& d : directions) {
|
|
||||||
if ((d[R] == pin[R] || d[R] == -pin[R]) && (d[F] == pin[F] || d[F] == -pin[F])) result.push_back(d);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Piece {
|
|
||||||
public:
|
|
||||||
enum Types : char {
|
|
||||||
Pawn,
|
|
||||||
Knight,
|
|
||||||
Bishop,
|
|
||||||
Rook,
|
|
||||||
Queen,
|
|
||||||
King,
|
|
||||||
//
|
|
||||||
NUM_PIECES
|
|
||||||
};
|
|
||||||
|
|
||||||
enum Colors : char {
|
|
||||||
White,
|
|
||||||
Black,
|
|
||||||
};
|
|
||||||
|
|
||||||
const char* name() const;
|
|
||||||
char initial() const;
|
|
||||||
Types type() const { return m_type; }
|
|
||||||
Colors color() const { return m_color; }
|
|
||||||
char pos() const { return m_pos; }
|
|
||||||
void setPos(char pos) {
|
|
||||||
m_pos = pos;
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
const char* coord() const;
|
|
||||||
const std::set<char>& allowed() const { return m_allowed; }
|
|
||||||
bool canReach(char pos) const;
|
|
||||||
virtual bool movePattern(char pos) const = 0;
|
|
||||||
void take();
|
|
||||||
virtual void reinit(const State& state) = 0;
|
|
||||||
void invalidate();
|
|
||||||
protected:
|
|
||||||
Piece(Types type, Colors color, char pos, std::set<char> allowed)
|
|
||||||
: m_type(type), m_color(color), m_pos(pos), m_allowed(std::move(allowed)) {}
|
|
||||||
Piece(const Piece&) = delete;
|
|
||||||
~Piece() = default;
|
|
||||||
|
|
||||||
const Types m_type;
|
|
||||||
const Colors m_color;
|
|
||||||
char m_pos;
|
|
||||||
std::set<char> m_allowed;
|
|
||||||
bool m_update = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Pawn : public Piece {
|
|
||||||
Pawn(Colors color, char pos, std::set<char> next) : Piece(Types::Pawn, color, pos, std::move(next)) {}
|
|
||||||
|
|
||||||
bool is_first_move() const {
|
|
||||||
return m_color ? coord()[F] == '7' : coord()[F] == '2';
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool movePattern(char pos) const override {
|
|
||||||
if (m_pos == INVALID_POS) return false;
|
|
||||||
auto cur = coord();
|
|
||||||
auto next = positions[pos];
|
|
||||||
Direction distance = {char(next[R] - cur[R]), char(next[F] - cur[F])};
|
|
||||||
char forward = m_color ? -1 : 1;
|
|
||||||
return (forward == distance[F] && distance[R] * distance[R] <= 1)
|
|
||||||
|| (is_first_move() && 2 * forward == distance[F] && distance[R] == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void reinit(const State& state) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Knight : public Piece {
|
|
||||||
Knight(Colors color, char pos, std::set<char> next) : Piece(Types::Knight, color, pos, std::move(next)) {}
|
|
||||||
|
|
||||||
virtual bool movePattern(char pos) const override {
|
|
||||||
if (m_pos == INVALID_POS) return false;
|
|
||||||
auto cur = coord();
|
|
||||||
auto next = positions[pos];
|
|
||||||
Direction diff = {char(next[R] - cur[R]), char(next[F] - cur[F])};
|
|
||||||
return diff[R]*diff[R] + diff[F]*diff[F] == 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void reinit(const State& state) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Bishop : public Piece {
|
|
||||||
Bishop(Colors color, char pos) : Piece(Types::Bishop, color, pos, {}) {}
|
|
||||||
|
|
||||||
virtual bool movePattern(char pos) const override {
|
|
||||||
if (m_pos == INVALID_POS) return false;
|
|
||||||
auto cur = coord();
|
|
||||||
auto next = positions[pos];
|
|
||||||
return cur[R] - cur[F] == next[R] - next[F] || cur[R] + cur[F] == next[R] + next[F];
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void reinit(const State& state) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Rook : public Piece {
|
|
||||||
Rook(Colors color, char pos) : Piece(Types::Rook, color, pos, {}) {}
|
|
||||||
|
|
||||||
virtual bool movePattern(char pos) const override {
|
|
||||||
if (m_pos == INVALID_POS) return false;
|
|
||||||
auto cur = coord();
|
|
||||||
auto next = positions[pos];
|
|
||||||
return cur[R] == next[R] || cur[F] == next[F];
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void reinit(const State& state) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Queen : public Piece {
|
|
||||||
Queen(Colors color, char pos) : Piece(Types::Queen, color, pos, {}) {}
|
|
||||||
|
|
||||||
virtual bool movePattern(char pos) const override {
|
|
||||||
if (m_pos == INVALID_POS) return false;
|
|
||||||
auto cur = coord();
|
|
||||||
auto next = positions[pos];
|
|
||||||
return cur[R] == next[R] || cur[F] == next[F] || cur[R] - cur[F] == next[R] - next[F] || cur[R] + cur[F] == next[R] + next[F];
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void reinit(const State& state) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct King : public Piece {
|
|
||||||
King(Colors color, char pos) : Piece(Types::King, color, pos, {}) {}
|
|
||||||
|
|
||||||
virtual bool movePattern(char pos) const override {
|
|
||||||
if (m_pos == INVALID_POS) return false;
|
|
||||||
auto cur = coord();
|
|
||||||
auto next = positions[pos];
|
|
||||||
Direction diff = {char(next[R] - cur[R]), char(next[F] - cur[F])};
|
|
||||||
return diff[R]*diff[R] + diff[F]*diff[F] <= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void reinit(const State& state) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PieceSet {
|
|
||||||
Piece* begin() { return &p1; }
|
|
||||||
Piece* end() { return &r2 + 1; }
|
|
||||||
const Piece* begin() const { return &p1; }
|
|
||||||
const Piece* end() const { return &r2 + 1; }
|
|
||||||
Piece& operator[](int i) { return *(begin() + i); }
|
|
||||||
const Piece& operator[](int i) const { return *(begin() + i); }
|
|
||||||
|
|
||||||
Pawn p1;
|
|
||||||
Pawn p2;
|
|
||||||
Pawn p3;
|
|
||||||
Pawn p4;
|
|
||||||
Pawn p5;
|
|
||||||
Pawn p6;
|
|
||||||
Pawn p7;
|
|
||||||
Pawn p8;
|
|
||||||
Rook r1;
|
|
||||||
Knight n1;
|
|
||||||
Bishop b1;
|
|
||||||
Queen q;
|
|
||||||
King k;
|
|
||||||
Bishop b2;
|
|
||||||
Knight n2;
|
|
||||||
Rook r2;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct State {
|
|
||||||
State();
|
|
||||||
PieceSet blacks;
|
|
||||||
PieceSet whites;
|
|
||||||
Board board;
|
|
||||||
Pins blackPins;
|
|
||||||
Pins whitePins;
|
|
||||||
};
|
|
||||||
|
|
||||||
Direction findPin(const Piece& piece, const State& state) {
|
|
||||||
auto& pins = piece.color() ? state.blackPins : state.whitePins;
|
|
||||||
auto it = std::find_if(pins.begin(), pins.end(), [&] (const Pin& pin) { return pin.pinned == &piece; });
|
|
||||||
if (it != pins.end()) return it->d;
|
|
||||||
return {0, 0};
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Find {
|
|
||||||
Find(const Board& board) : m_board(board) {}
|
|
||||||
bool operator() (char pos) const { return m_board[pos]; }
|
|
||||||
const Board& m_board;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Add {
|
|
||||||
Add(const Board& board, std::set<char>& moves, Piece::Colors color) : m_board(board), m_moves(moves), m_color(color) {}
|
|
||||||
bool operator() (char pos) const {
|
|
||||||
if (!m_board[pos] || m_board[pos]->color() != m_color) m_moves.insert(pos);
|
|
||||||
return m_board[pos];
|
|
||||||
}
|
|
||||||
const Board& m_board;
|
|
||||||
std::set<char>& m_moves;
|
|
||||||
Piece::Colors m_color;
|
|
||||||
};
|
|
||||||
|
|
||||||
void Pawn::reinit(const State& state) {
|
|
||||||
if (m_pos == INVALID_POS) return;
|
|
||||||
if (!m_update) return;
|
|
||||||
m_update = false;
|
|
||||||
m_allowed.clear();
|
|
||||||
|
|
||||||
auto pin = findPin(*this, state);
|
|
||||||
|
|
||||||
auto & left = m_color ? SW : NW;
|
|
||||||
auto & right = m_color ? SE : NE;
|
|
||||||
|
|
||||||
for (auto& direction : filter(pin, { left, right })) {
|
|
||||||
auto pos = makeStep(m_pos, direction);
|
|
||||||
if (pos != INVALID_POS && state.board[pos] && state.board[pos]->color() != m_color) m_allowed.insert(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto & forward = m_color ? S : N;
|
|
||||||
if (!filter(pin, {forward}).empty()) {
|
|
||||||
traverse(m_pos, forward, [&] (char pos) {
|
|
||||||
if (!state.board[pos]) m_allowed.insert(pos);
|
|
||||||
return state.board[pos] || !is_first_move();
|
|
||||||
}, 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Knight::reinit(const State& state) {
|
|
||||||
if (m_pos == INVALID_POS) return;
|
|
||||||
if (!m_update) return;
|
|
||||||
m_update = false;
|
|
||||||
m_allowed.clear();
|
|
||||||
auto pin = findPin(*this, state);
|
|
||||||
if (pin[R] != 0 || pin[F] != 0) return;
|
|
||||||
for (auto& direction : { NNE, ENE, ESE, SSE, SSW, WSW, WNW, NNW }) {
|
|
||||||
auto pos = makeStep(m_pos, direction);
|
|
||||||
if (pos != INVALID_POS && (!state.board[pos] || state.board[pos]->color() != m_color)) m_allowed.insert(pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bishop::reinit(const State& state) {
|
|
||||||
if (m_pos == INVALID_POS) return;
|
|
||||||
if (!m_update) return;
|
|
||||||
m_update = false;
|
|
||||||
m_allowed.clear();
|
|
||||||
auto pin = findPin(*this, state);
|
|
||||||
for (auto& direction : filter(pin, { NE, SE, SW, NW })) {
|
|
||||||
traverse(m_pos, direction, Add(state.board, m_allowed, m_color));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Rook::reinit(const State& state) {
|
|
||||||
if (m_pos == INVALID_POS) return;
|
|
||||||
if (!m_update) return;
|
|
||||||
m_update = false;
|
|
||||||
m_allowed.clear();
|
|
||||||
auto pin = findPin(*this, state);
|
|
||||||
for (auto& direction : filter(pin, { N, E, S, W })) {
|
|
||||||
traverse(m_pos, direction, Add(state.board, m_allowed, m_color));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Queen::reinit(const State& state) {
|
|
||||||
if (m_pos == INVALID_POS) return;
|
|
||||||
if (!m_update) return;
|
|
||||||
m_update = false;
|
|
||||||
m_allowed.clear();
|
|
||||||
auto pin = findPin(*this, state);
|
|
||||||
for (auto& direction : filter(pin, { N, NE, E, SE, S, SW, W, NW })) {
|
|
||||||
traverse(m_pos, direction, Add(state.board, m_allowed, m_color));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void King::reinit(const State& state) {
|
|
||||||
if (m_pos == INVALID_POS) return;
|
|
||||||
if (!m_update) return;
|
|
||||||
m_update = false;
|
|
||||||
m_allowed.clear();
|
|
||||||
auto& enemyPieces = m_color ? state.whites : state.blacks;
|
|
||||||
auto& pawnAttackLeft = m_color ? SW : NW;
|
|
||||||
auto& pawnAttackRight = m_color ? SE : NE;
|
|
||||||
for (auto& direction : { N, NE, E, SE, S, SW, W, NW }) {
|
|
||||||
auto pos = makeStep(m_pos, direction);
|
|
||||||
bool accept = pos != INVALID_POS && !(state.board[pos] && state.board[pos]->color() == m_color);
|
|
||||||
if (accept) {
|
|
||||||
for (auto& p : enemyPieces) {
|
|
||||||
if (!p.movePattern(pos)) continue;
|
|
||||||
if (p.type() == Piece::Knight || p.type() == Piece::King) {
|
|
||||||
accept = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (p.type() == Piece::Pawn) {
|
|
||||||
auto from = positions[pos];
|
|
||||||
auto to = p.coord();
|
|
||||||
Direction d {char(to[R] - from[R]), char(to[F] - from[F])};
|
|
||||||
if (d == pawnAttackLeft || d == pawnAttackRight) {
|
|
||||||
accept = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
auto from = positions[pos];
|
|
||||||
auto to = p.coord();
|
|
||||||
Direction d = normalize({char(to[R] - from[R]), char(to[F] - from[F])});
|
|
||||||
auto reached = traverse(pos, d, Find(state.board));
|
|
||||||
if (p.pos() == reached) {
|
|
||||||
accept = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (accept) m_allowed.insert(pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* Piece::name() const {
|
|
||||||
static_assert(pieceNames.size() == Piece::NUM_PIECES, "Mismatch between piece names and types");
|
|
||||||
return pieceNames[m_type];
|
|
||||||
}
|
|
||||||
|
|
||||||
char Piece::initial() const {
|
|
||||||
static_assert(blackShort.size() == Piece::NUM_PIECES, "Mismatch between piece names and types");
|
|
||||||
static_assert(whiteShort.size() == Piece::NUM_PIECES, "Mismatch between piece names and types");
|
|
||||||
return m_color ? blackShort[m_type] : whiteShort[m_type];
|
|
||||||
}
|
|
||||||
|
|
||||||
void Piece::invalidate() {
|
|
||||||
m_update = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const char* Piece::coord() const {
|
|
||||||
if (m_pos == INVALID_POS) return "";
|
|
||||||
return positions[m_pos];
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Piece::canReach(char pos) const {
|
|
||||||
return movePattern(pos) && m_allowed.count(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Piece::take() {
|
|
||||||
m_pos = INVALID_POS;
|
|
||||||
m_allowed = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
State::State()
|
|
||||||
: blacks {
|
|
||||||
{Piece::Black, "a7"_P, {"a5"_P, "a6"_P} },
|
|
||||||
{Piece::Black, "b7"_P, {"b5"_P, "b6"_P} },
|
|
||||||
{Piece::Black, "c7"_P, {"c5"_P, "c6"_P} },
|
|
||||||
{Piece::Black, "d7"_P, {"d5"_P, "d6"_P} },
|
|
||||||
{Piece::Black, "e7"_P, {"e5"_P, "e6"_P} },
|
|
||||||
{Piece::Black, "f7"_P, {"f5"_P, "f6"_P} },
|
|
||||||
{Piece::Black, "g7"_P, {"g5"_P, "g6"_P} },
|
|
||||||
{Piece::Black, "h7"_P, {"h5"_P, "h6"_P} },
|
|
||||||
{Piece::Black, "a8"_P},
|
|
||||||
{Piece::Black, "b8"_P, {"a6"_P, "c6"_P} },
|
|
||||||
{Piece::Black, "c8"_P},
|
|
||||||
{Piece::Black, "d8"_P},
|
|
||||||
{Piece::Black, "e8"_P},
|
|
||||||
{Piece::Black, "f8"_P},
|
|
||||||
{Piece::Black, "g8"_P, {"f6"_P, "h6"_P} },
|
|
||||||
{Piece::Black, "h8"_P},
|
|
||||||
}
|
|
||||||
, whites {
|
|
||||||
{Piece::White, "a2"_P, {"a3"_P, "a4"_P} },
|
|
||||||
{Piece::White, "b2"_P, {"b3"_P, "b4"_P} },
|
|
||||||
{Piece::White, "c2"_P, {"c3"_P, "c4"_P} },
|
|
||||||
{Piece::White, "d2"_P, {"d3"_P, "d4"_P} },
|
|
||||||
{Piece::White, "e2"_P, {"e3"_P, "e4"_P} },
|
|
||||||
{Piece::White, "f2"_P, {"f3"_P, "f4"_P} },
|
|
||||||
{Piece::White, "g2"_P, {"g3"_P, "g4"_P} },
|
|
||||||
{Piece::White, "h2"_P, {"h3"_P, "h4"_P} },
|
|
||||||
{Piece::White, "a1"_P},
|
|
||||||
{Piece::White, "b1"_P, {"a3"_P, "c3"_P} },
|
|
||||||
{Piece::White, "c1"_P},
|
|
||||||
{Piece::White, "d1"_P},
|
|
||||||
{Piece::White, "e1"_P},
|
|
||||||
{Piece::White, "f1"_P},
|
|
||||||
{Piece::White, "g1"_P, {"f3"_P, "h3"_P} },
|
|
||||||
{Piece::White, "h1"_P},
|
|
||||||
}
|
|
||||||
, board {{
|
|
||||||
&whites[ 8], &whites[ 9], &whites[10], &whites[11], &whites[12], &whites[13], &whites[14], &whites[15],
|
|
||||||
&whites[ 0], &whites[ 1], &whites[ 2], &whites[ 3], &whites[ 4], &whites[ 5], &whites[ 6], &whites[ 7],
|
|
||||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
|
||||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
|
||||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
|
||||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
|
||||||
&blacks[ 0], &blacks[ 1], &blacks[ 2], &blacks[ 3], &blacks[ 4], &blacks[ 5], &blacks[ 6], &blacks[ 7],
|
|
||||||
&blacks[ 8], &blacks[ 9], &blacks[10], &blacks[11], &blacks[12], &blacks[13], &blacks[14], &blacks[15],
|
|
||||||
}}
|
|
||||||
{}
|
|
||||||
|
|
||||||
Chessboard::Chessboard()
|
|
||||||
: m_state(new State())
|
|
||||||
{
|
|
||||||
setGrammar();
|
|
||||||
}
|
|
||||||
|
|
||||||
Chessboard::~Chessboard() = default;
|
|
||||||
|
|
||||||
void Chessboard::setPrompt(const std::string& prompt) {
|
|
||||||
m_prompt = prompt;
|
|
||||||
setGrammar();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Chessboard::setGrammar() {
|
|
||||||
m_grammar.clear();
|
|
||||||
|
|
||||||
std::string result;
|
|
||||||
if (m_prompt.empty()) {
|
|
||||||
result += "move ::= \" \" ((piece | frompos) \" \" \"to \"?)? topos\n";
|
|
||||||
//result += "move ::= \" \" frompos \" \" \"to \"? topos\n";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// result += "move ::= prompt \" \" ((piece | frompos) \" \" \"to \"?)? topos\n"
|
|
||||||
result += "move ::= prompt \" \" frompos \" \" \"to \"? topos\n"
|
|
||||||
"prompt ::= \" " + m_prompt + "\"\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::set<Piece::Types> pieceTypes;
|
|
||||||
std::set<char> from_pos;
|
|
||||||
std::set<char> to_pos;
|
|
||||||
auto& pieces = m_moveCounter % 2 ? m_state->blacks : m_state->whites;
|
|
||||||
std::set<size_t> flags;
|
|
||||||
for (auto& p : pieces) {
|
|
||||||
if (p.allowed().empty()) continue;
|
|
||||||
bool addPiece = false;
|
|
||||||
if (!m_inCheck || p.type() == Piece::King) {
|
|
||||||
to_pos.insert(p.allowed().begin(), p.allowed().end());
|
|
||||||
addPiece = !p.allowed().empty();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (auto move : p.allowed()) {
|
|
||||||
if (m_allowedInCheck.count(move)) {
|
|
||||||
to_pos.insert(move);
|
|
||||||
addPiece = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (addPiece) {
|
|
||||||
pieceTypes.insert(p.type());
|
|
||||||
from_pos.insert(p.pos());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (pieceTypes.empty()) return;
|
|
||||||
|
|
||||||
result += "piece ::= (";
|
|
||||||
for (auto& p : pieceTypes) result += " \"" + std::string(pieceNames[p]) + "\" |";
|
|
||||||
result.pop_back();
|
|
||||||
result += ")\n\n";
|
|
||||||
|
|
||||||
result += "frompos ::= (";
|
|
||||||
for (auto& p : from_pos) result += " \"" + std::string(positions[p]) + "\" |";
|
|
||||||
result.pop_back();
|
|
||||||
result += ")\n";
|
|
||||||
|
|
||||||
result += "topos ::= (";
|
|
||||||
for (auto& p : to_pos) result += " \"" + std::string(positions[p]) + "\" |";
|
|
||||||
result.pop_back();
|
|
||||||
result += ")\n";
|
|
||||||
|
|
||||||
m_grammar = std::move(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Chessboard::stringifyBoard() {
|
|
||||||
std::string result;
|
|
||||||
result.reserve(16 + 2 * 64 + 16);
|
|
||||||
for (char rank = 'a'; rank <= 'h'; ++rank) {
|
|
||||||
result.push_back(rank);
|
|
||||||
result.push_back(' ');
|
|
||||||
}
|
|
||||||
result.back() = '\n';
|
|
||||||
for (int i = 7; i >= 0; --i) {
|
|
||||||
for (int j = 0; j < 8; ++j) {
|
|
||||||
auto p = m_state->board[i * 8 + j];
|
|
||||||
if (p) result.push_back(p->initial());
|
|
||||||
else result.push_back((i + j) % 2 ? '.' : '*');
|
|
||||||
result.push_back(' ');
|
|
||||||
}
|
|
||||||
result.push_back('0' + i + 1);
|
|
||||||
result.push_back('\n');
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Chessboard::process(const std::string& command) {
|
|
||||||
const auto t_start = std::chrono::high_resolution_clock::now();
|
|
||||||
auto color = Piece::Colors(m_moveCounter % 2);
|
|
||||||
Piece* piece = nullptr;
|
|
||||||
auto pos_to = INVALID_POS;
|
|
||||||
if (!parseCommand(command, piece, pos_to)) return "";
|
|
||||||
|
|
||||||
auto pos_from = piece->pos();
|
|
||||||
|
|
||||||
if (!move(*piece, pos_to)) return "";
|
|
||||||
|
|
||||||
flagUpdates(pos_from, pos_to);
|
|
||||||
|
|
||||||
detectChecks();
|
|
||||||
|
|
||||||
auto& enemyPieces = color ? m_state->whites : m_state->blacks;
|
|
||||||
for (auto& p : enemyPieces) p.reinit(*m_state); // only enemy moves needed next
|
|
||||||
|
|
||||||
std::string result = {positions[pos_from][R], positions[pos_from][F], '-', positions[pos_to][R], positions[pos_to][F]};
|
|
||||||
++m_moveCounter;
|
|
||||||
setGrammar();
|
|
||||||
const auto t_end = std::chrono::high_resolution_clock::now();
|
|
||||||
auto t_ms = std::chrono::duration_cast<std::chrono::milliseconds>(t_end - t_start).count();
|
|
||||||
fprintf(stdout, "%s: Move '%s%s%s', (t = %d ms)\n", __func__, "\033[1m", result.data(), "\033[0m", (int) t_ms);
|
|
||||||
if (m_grammar.empty()) result.push_back('#');
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Chessboard::parseCommand(const std::string& command, Piece*& piece, char& pos_to) {
|
|
||||||
auto color = Piece::Colors(m_moveCounter % 2);
|
|
||||||
fprintf(stdout, "%s: Command to %s: '%s%.*s%s'\n", __func__, (color ? "Black" : "White"), "\033[1m", int(command.size()), command.data(), "\033[0m");
|
|
||||||
|
|
||||||
if (command.empty()) return false;
|
|
||||||
auto tokens = split(command, ' ');
|
|
||||||
auto pos_from = INVALID_POS;
|
|
||||||
auto type = Piece::Types::NUM_PIECES;
|
|
||||||
if (tokens.size() == 1) {
|
|
||||||
type = Piece::Types::Pawn;
|
|
||||||
pos_to = strToPos(tokens.front());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
pos_from = strToPos(tokens.front());
|
|
||||||
if (pos_from == INVALID_POS) type = Piece::Types(strToType(tokens.front()));
|
|
||||||
pos_to = strToPos(tokens.back());
|
|
||||||
}
|
|
||||||
if (pos_to == INVALID_POS) return false;
|
|
||||||
if (pos_from == INVALID_POS) {
|
|
||||||
if (type == Piece::Types::NUM_PIECES) return false;
|
|
||||||
auto& pieces = color ? m_state->blacks : m_state->whites;
|
|
||||||
for (auto& p : pieces) {
|
|
||||||
if (p.type() == type && p.canReach(pos_to)) {
|
|
||||||
pos_from = p.pos();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (pos_from == INVALID_POS) return false;
|
|
||||||
if (m_state->board[pos_from] == nullptr) return false;
|
|
||||||
piece = m_state->board[pos_from];
|
|
||||||
if (piece->color() != color) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Chessboard::flagUpdates(char pos_from, char pos_to) {
|
|
||||||
auto color = Piece::Colors(m_moveCounter % 2);
|
|
||||||
auto& enemyPieces = color ? m_state->whites : m_state->blacks;
|
|
||||||
auto& ownPieces = color ? m_state->blacks : m_state->whites;
|
|
||||||
for (auto& p : enemyPieces) {
|
|
||||||
if (p.movePattern(pos_to) || p.movePattern(pos_from)) {
|
|
||||||
updatePins(p);
|
|
||||||
p.invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& p : ownPieces) {
|
|
||||||
if (p.movePattern(pos_to) || p.movePattern(pos_from)) {
|
|
||||||
updatePins(p);
|
|
||||||
p.invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Chessboard::updatePins(Piece& piece) {
|
|
||||||
if (piece.type() == Piece::Pawn || piece.type() == Piece::Knight || piece.type() == Piece::King) return;
|
|
||||||
auto& enemyPieces = piece.color() ? m_state->whites : m_state->blacks;
|
|
||||||
auto& enemyPins = piece.color() ? m_state->whitePins : m_state->blackPins;
|
|
||||||
auto& king = enemyPieces.k;
|
|
||||||
auto it = std::find_if(enemyPins.begin(), enemyPins.end(), [&] (const Pin& pin) { return pin.pinner == &piece; });
|
|
||||||
if (it != enemyPins.end()) {
|
|
||||||
it->pinned->invalidate();
|
|
||||||
enemyPins.erase(it);
|
|
||||||
}
|
|
||||||
if (piece.movePattern(king.pos())) {
|
|
||||||
auto to = positions[king.pos()];
|
|
||||||
auto from = piece.coord();
|
|
||||||
Direction d = normalize({char(to[R] - from[R]), char(to[F] - from[F])});
|
|
||||||
|
|
||||||
auto reached = traverse(piece.pos(), d, Find(m_state->board));
|
|
||||||
auto foundPiece = m_state->board[reached];
|
|
||||||
if (&king == foundPiece) {
|
|
||||||
// check
|
|
||||||
king.invalidate();
|
|
||||||
}
|
|
||||||
else if (foundPiece && foundPiece->color() != piece.color()) {
|
|
||||||
reached = traverse(reached, d, Find(m_state->board));
|
|
||||||
if (&king == m_state->board[reached]) {
|
|
||||||
enemyPins.push_back({d, &piece, foundPiece});
|
|
||||||
foundPiece->invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Chessboard::detectChecks() {
|
|
||||||
auto color = Piece::Colors(m_moveCounter % 2);
|
|
||||||
auto& enemyPieces = color ? m_state->whites : m_state->blacks;
|
|
||||||
auto& ownPieces = color ? m_state->blacks : m_state->whites;
|
|
||||||
auto& king = enemyPieces.k;
|
|
||||||
auto& pawnAttackLeft = color ? SW : NW;
|
|
||||||
auto& pawnAttackRight = color ? SE : NE;
|
|
||||||
for (auto& p : ownPieces) {
|
|
||||||
if (!p.movePattern(king.pos())) continue;
|
|
||||||
auto to = positions[king.pos()];
|
|
||||||
auto from = p.coord();
|
|
||||||
|
|
||||||
if (p.type() == Piece::Knight) {
|
|
||||||
if (!m_inCheck) {
|
|
||||||
m_allowedInCheck = { p.pos() };
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
m_allowedInCheck.clear();
|
|
||||||
}
|
|
||||||
m_inCheck = true;
|
|
||||||
}
|
|
||||||
else if (p.type() == Piece::Pawn) {
|
|
||||||
Direction d {char(to[R] - from[R]), char(to[F] - from[F])};
|
|
||||||
if (d == pawnAttackLeft || d == pawnAttackRight) {
|
|
||||||
if (!m_inCheck) {
|
|
||||||
m_allowedInCheck = { p.pos() };
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
m_allowedInCheck.clear();
|
|
||||||
}
|
|
||||||
m_inCheck = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Direction d = normalize({char(to[R] - from[R]), char(to[F] - from[F])});
|
|
||||||
std::set<char> tmp;
|
|
||||||
auto pos = traverse(p.pos(), d, Add(m_state->board, tmp, king.color()));
|
|
||||||
if (pos == king.pos()) {
|
|
||||||
tmp.insert(p.pos());
|
|
||||||
if (!m_inCheck) {
|
|
||||||
m_allowedInCheck = std::move(tmp);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
m_allowedInCheck.clear();
|
|
||||||
}
|
|
||||||
m_inCheck = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Chessboard::move(Piece& piece, char pos_to) {
|
|
||||||
auto& allowed = piece.allowed();
|
|
||||||
|
|
||||||
if (allowed.count(pos_to) == 0 || (m_inCheck && piece.type() != Piece::King && m_allowedInCheck.count(pos_to) == 0)) return false;
|
|
||||||
if (m_state->board[pos_to] && m_state->board[pos_to]->color() == piece.color()) return false;
|
|
||||||
if (m_state->board[pos_to]) m_state->board[pos_to]->take();
|
|
||||||
m_state->board[piece.pos()] = nullptr;
|
|
||||||
m_state->board[pos_to] = &piece;
|
|
||||||
piece.setPos(pos_to);
|
|
||||||
|
|
||||||
m_inCheck = false;
|
|
||||||
m_allowedInCheck.clear();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <string>
|
|
||||||
#include <set>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
// just basic validation
|
|
||||||
// fixme: missing en passant, castling, promotion, etc.
|
|
||||||
struct State;
|
|
||||||
class Piece;
|
|
||||||
class Chessboard {
|
|
||||||
public:
|
|
||||||
Chessboard();
|
|
||||||
~Chessboard();
|
|
||||||
std::string process(const std::string& command);
|
|
||||||
std::string stringifyBoard();
|
|
||||||
const std::string& grammar() { return m_grammar; }
|
|
||||||
const std::string& prompt() { return m_prompt; }
|
|
||||||
void setPrompt(const std::string& prompt);
|
|
||||||
private:
|
|
||||||
bool parseCommand(const std::string& command, Piece*& piece, char& pos_to);
|
|
||||||
bool move(Piece& piece, char pos);
|
|
||||||
void flagUpdates(char pos_from, char pos_to);
|
|
||||||
void updatePins(Piece& piece);
|
|
||||||
void detectChecks();
|
|
||||||
void setGrammar();
|
|
||||||
|
|
||||||
std::unique_ptr<State> m_state;
|
|
||||||
std::set<char> m_allowedInCheck;
|
|
||||||
bool m_inCheck = false;
|
|
||||||
int m_moveCounter = 0;
|
|
||||||
std::string m_grammar;
|
|
||||||
std::string m_prompt;
|
|
||||||
};
|
|
@ -1,193 +0,0 @@
|
|||||||
#include "WChess.h"
|
|
||||||
#include "Chessboard.h"
|
|
||||||
#include "grammar-parser.h"
|
|
||||||
#include "common.h"
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
WChess::WChess(whisper_context * ctx,
|
|
||||||
const whisper_full_params & wparams,
|
|
||||||
callbacks cb,
|
|
||||||
settings s)
|
|
||||||
: m_ctx(ctx)
|
|
||||||
, m_wparams(wparams)
|
|
||||||
, m_cb(cb)
|
|
||||||
, m_settings(s)
|
|
||||||
, m_board(new Chessboard())
|
|
||||||
{}
|
|
||||||
|
|
||||||
WChess::~WChess() = default;
|
|
||||||
|
|
||||||
void WChess::set_move(const std::string& moves, float prob) const {
|
|
||||||
if (m_cb.set_move) (*m_cb.set_move)(moves, prob);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WChess::set_grammar(const std::string& grammar) const {
|
|
||||||
if (m_cb.set_grammar) (*m_cb.set_grammar)(grammar);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WChess::get_audio(std::vector<float>& pcmf32) const {
|
|
||||||
if (m_cb.get_audio) return (*m_cb.get_audio)(pcmf32);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string WChess::stringify_board() const {
|
|
||||||
return m_board->stringifyBoard();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string WChess::get_grammar() const {
|
|
||||||
return m_board->grammar();
|
|
||||||
}
|
|
||||||
|
|
||||||
void WChess::run() {
|
|
||||||
bool have_prompt = true;
|
|
||||||
bool ask_prompt = !have_prompt;
|
|
||||||
|
|
||||||
float logprob_min = 0.0f;
|
|
||||||
|
|
||||||
float logprob_sum = 0.0f;
|
|
||||||
|
|
||||||
int n_tokens = 0;
|
|
||||||
|
|
||||||
std::vector<float> pcmf32_cur;
|
|
||||||
std::vector<float> pcmf32_prompt;
|
|
||||||
|
|
||||||
const std::string k_prompt = have_prompt ? "" : "rook to d4, f3";
|
|
||||||
int64_t t_ms = 0;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (get_audio(pcmf32_cur)) {
|
|
||||||
if (!pcmf32_cur.empty()) {
|
|
||||||
// fprintf(stdout, "%s: Processing ...\n", __func__);
|
|
||||||
|
|
||||||
if (!have_prompt) {
|
|
||||||
const auto txt = ::trim(transcribe(pcmf32_cur, logprob_min, logprob_sum, n_tokens, 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");
|
|
||||||
|
|
||||||
// save the audio for the prompt
|
|
||||||
pcmf32_prompt = pcmf32_cur;
|
|
||||||
have_prompt = true;
|
|
||||||
m_board->setPrompt(k_prompt);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!pcmf32_prompt.empty()) pcmf32_cur.insert(pcmf32_cur.begin(), pcmf32_prompt.begin(), pcmf32_prompt.end());
|
|
||||||
constexpr size_t MIN_SIZE = 1.2 * WHISPER_SAMPLE_RATE;
|
|
||||||
if (MIN_SIZE > pcmf32_cur.size()) pcmf32_cur.insert(pcmf32_cur.begin(), MIN_SIZE - pcmf32_cur.size(), 0.0f);
|
|
||||||
|
|
||||||
// fprintf(stdout, "%s: grammar rules:\n'%s'\n", __func__, m_board->grammar().c_str());
|
|
||||||
|
|
||||||
auto grammar_parsed = grammar_parser::parse(m_board->grammar().c_str());
|
|
||||||
auto grammar_rules = grammar_parsed.c_rules();
|
|
||||||
|
|
||||||
m_wparams.grammar_rules = grammar_rules.data();
|
|
||||||
m_wparams.n_grammar_rules = grammar_rules.size();
|
|
||||||
|
|
||||||
m_wparams.i_start_rule = grammar_parsed.symbol_ids.at("move");
|
|
||||||
auto txt = ::trim(transcribe(pcmf32_cur, logprob_min, logprob_sum, n_tokens, t_ms));
|
|
||||||
|
|
||||||
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 (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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stdout, "%s: DEBUG: txt = '%s', prob = %.2f%%\n", __func__, txt.c_str(), p);
|
|
||||||
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");
|
|
||||||
|
|
||||||
if (!command.empty()) {
|
|
||||||
set_move(m_board->process(command), p);
|
|
||||||
set_grammar(m_board->grammar());
|
|
||||||
}
|
|
||||||
if (m_board->grammar().empty()) {
|
|
||||||
fprintf(stdout, "%s: No more moves possible\n", __func__);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string WChess::transcribe(
|
|
||||||
const std::vector<float> & pcmf32,
|
|
||||||
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;
|
|
||||||
|
|
||||||
if (whisper_full(m_ctx, m_wparams, pcmf32.data(), pcmf32.size()) != 0) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string result;
|
|
||||||
|
|
||||||
const int n_segments = whisper_full_n_segments(m_ctx);
|
|
||||||
for (int i = 0; i < n_segments; ++i) {
|
|
||||||
const char * text = whisper_full_get_segment_text(m_ctx, i);
|
|
||||||
|
|
||||||
result += text;
|
|
||||||
|
|
||||||
const int n = whisper_full_n_tokens(m_ctx, i);
|
|
||||||
for (int j = 0; j < n; ++j) {
|
|
||||||
const auto token = whisper_full_get_token_data(m_ctx, i, j);
|
|
||||||
|
|
||||||
if(token.plog > 0.0f) return {};
|
|
||||||
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<std::chrono::milliseconds>(t_end - t_start).count();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "whisper.h"
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
class Chessboard;
|
|
||||||
|
|
||||||
class WChess {
|
|
||||||
public:
|
|
||||||
using CheckRunningCb = bool (*)();
|
|
||||||
using GetAudioCb = bool (*)(std::vector<float> &);
|
|
||||||
using SetMovesCb = void (*)(const std::string &, float);
|
|
||||||
using SetGrammarCb = void (*)(const std::string &);
|
|
||||||
using ClearAudioCb = void (*)();
|
|
||||||
|
|
||||||
struct callbacks {
|
|
||||||
GetAudioCb get_audio = nullptr;
|
|
||||||
SetMovesCb set_move = nullptr;
|
|
||||||
SetGrammarCb set_grammar = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct settings {
|
|
||||||
int32_t vad_ms = 2000;
|
|
||||||
int32_t prompt_ms = 5000;
|
|
||||||
int32_t command_ms = 4000;
|
|
||||||
float vad_thold = 0.2f;
|
|
||||||
float freq_thold = 100.0f;
|
|
||||||
bool print_energy = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
WChess(
|
|
||||||
whisper_context * ctx,
|
|
||||||
const whisper_full_params & wparams,
|
|
||||||
callbacks cb,
|
|
||||||
settings s
|
|
||||||
);
|
|
||||||
~WChess();
|
|
||||||
|
|
||||||
void run();
|
|
||||||
|
|
||||||
std::string stringify_board() const;
|
|
||||||
|
|
||||||
std::string get_grammar() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool get_audio(std::vector<float>& pcmf32) const;
|
|
||||||
void set_move(const std::string& moves, float prob) const;
|
|
||||||
void set_grammar(const std::string& grammar) const;
|
|
||||||
|
|
||||||
std::string transcribe(
|
|
||||||
const std::vector<float> & pcmf32,
|
|
||||||
float & logprob_min,
|
|
||||||
float & logprob_sum,
|
|
||||||
int & n_tokens,
|
|
||||||
int64_t & t_ms);
|
|
||||||
|
|
||||||
whisper_context * m_ctx;
|
|
||||||
whisper_full_params m_wparams;
|
|
||||||
const callbacks m_cb;
|
|
||||||
const settings m_settings;
|
|
||||||
std::unique_ptr<Chessboard> m_board;
|
|
||||||
};
|
|
@ -1,117 +0,0 @@
|
|||||||
#include "Chessboard.h"
|
|
||||||
|
|
||||||
#define ASSERT(x) \
|
|
||||||
do { \
|
|
||||||
if (!(x)) { \
|
|
||||||
fprintf(stderr, "ASSERT: %s:%d: %s\n", __FILE__, __LINE__, #x); \
|
|
||||||
fflush(stderr); \
|
|
||||||
exit(1); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
{
|
|
||||||
Chessboard chess;
|
|
||||||
|
|
||||||
ASSERT(chess.process("pawn to d4") == "d2-d4");
|
|
||||||
ASSERT(chess.process("e5") == "e7-e5");
|
|
||||||
ASSERT(chess.process("c1 h6") == "c1-h6");
|
|
||||||
ASSERT(chess.process("queen h4") == "d8-h4");
|
|
||||||
ASSERT(chess.process("bishop to g5") == "h6-g5");
|
|
||||||
ASSERT(chess.process("bishop to b4") == "f8-b4");
|
|
||||||
ASSERT(chess.process("c4") == "");
|
|
||||||
ASSERT(chess.process("knight c3") == "b1-c3");
|
|
||||||
ASSERT(chess.process("knight c6") == "b8-c6");
|
|
||||||
ASSERT(chess.process("f3") == "");
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Chessboard chess;
|
|
||||||
|
|
||||||
ASSERT(chess.process("d4") == "d2-d4");
|
|
||||||
ASSERT(chess.process("e5") == "e7-e5");
|
|
||||||
ASSERT(chess.process("e4") == "e2-e4");
|
|
||||||
ASSERT(chess.process("queen h4") == "d8-h4");
|
|
||||||
ASSERT(chess.process("queen h5") == "d1-h5");
|
|
||||||
ASSERT(chess.process("f5") == "");
|
|
||||||
ASSERT(chess.process("g6") == "g7-g6");
|
|
||||||
ASSERT(chess.process("knight e2") == "g1-e2");
|
|
||||||
ASSERT(chess.process("f5") == "f7-f5");
|
|
||||||
ASSERT(chess.process("knight g3") == "e2-g3");
|
|
||||||
ASSERT(chess.process("g5") == "");
|
|
||||||
ASSERT(chess.process("king e7") == "e8-e7");
|
|
||||||
ASSERT(chess.process("f4") == "f2-f4");
|
|
||||||
ASSERT(chess.process("g5") == "g6-g5");
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Chessboard chess;
|
|
||||||
|
|
||||||
ASSERT(chess.process("e4") == "e2-e4");
|
|
||||||
ASSERT(chess.process("c5") == "c7-c5");
|
|
||||||
ASSERT(chess.process("e5") == "e4-e5");
|
|
||||||
ASSERT(chess.process("c4") == "c5-c4");
|
|
||||||
ASSERT(chess.process("e6") == "e5-e6");
|
|
||||||
ASSERT(chess.process("c3") == "c4-c3");
|
|
||||||
ASSERT(chess.process("e7") == "");
|
|
||||||
ASSERT(chess.process("f7") == "e6-f7");
|
|
||||||
ASSERT(chess.process("d2") == "");
|
|
||||||
ASSERT(chess.process("king to f7") == "e8-f7");
|
|
||||||
ASSERT(chess.process("f4") == "f2-f4");
|
|
||||||
ASSERT(chess.process("d2") == "c3-d2");
|
|
||||||
ASSERT(chess.process("f5") == "");
|
|
||||||
ASSERT(chess.process("king to e2") == "e1-e2");
|
|
||||||
ASSERT(chess.process("king to g6") == "f7-g6");
|
|
||||||
ASSERT(chess.process("f5") == "f4-f5");
|
|
||||||
ASSERT(chess.process("e6") == "");
|
|
||||||
ASSERT(chess.process("king to h5") == "g6-h5");
|
|
||||||
ASSERT(chess.process("g4") == "g2-g4");
|
|
||||||
ASSERT(chess.process("king to g5") == "h5-g5");
|
|
||||||
ASSERT(chess.process("h4") == "h2-h4");
|
|
||||||
ASSERT(chess.process("king to h5") == "");
|
|
||||||
ASSERT(chess.process("king to g6") == "");
|
|
||||||
ASSERT(chess.process("king to h6") == "g5-h6");
|
|
||||||
ASSERT(chess.process("bishop to d2") == "c1-d2");
|
|
||||||
ASSERT(chess.process("king to g5") == "");
|
|
||||||
ASSERT(chess.process("g5") == "g7-g5");
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Chessboard chess;
|
|
||||||
ASSERT(chess.process("f4") == "f2-f4");
|
|
||||||
ASSERT(chess.process("e5") == "e7-e5");
|
|
||||||
ASSERT(chess.process("g4") == "g2-g4");
|
|
||||||
ASSERT(chess.process("queen to h4") == "d8-h4#");
|
|
||||||
ASSERT(chess.process("knight f3") == "");
|
|
||||||
ASSERT(chess.grammar().empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Chessboard chess;
|
|
||||||
ASSERT(chess.process("f4") == "f2-f4");
|
|
||||||
ASSERT(chess.process("e5") == "e7-e5");
|
|
||||||
ASSERT(chess.process("g4") == "g2-g4");
|
|
||||||
ASSERT(chess.process("d5") == "d7-d5");
|
|
||||||
ASSERT(chess.process("g1 f3") == "g1-f3");
|
|
||||||
ASSERT(chess.process("queen to h4") == "d8-h4");
|
|
||||||
ASSERT(!chess.grammar().empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Chessboard chess;
|
|
||||||
ASSERT(chess.process("knight c3") == "b1-c3");
|
|
||||||
ASSERT(chess.process("knight c6") == "b8-c6");
|
|
||||||
ASSERT(chess.process("knight b5") == "c3-b5");
|
|
||||||
ASSERT(chess.process("knight f6") == "g8-f6");
|
|
||||||
ASSERT(chess.process("knight d6") == "b5-d6");
|
|
||||||
ASSERT(chess.process("knight d4") == "");
|
|
||||||
ASSERT(chess.process("d6") == "c7-d6");
|
|
||||||
ASSERT(chess.process("e4") == "e2-e4");
|
|
||||||
ASSERT(chess.process("knight d4") == "c6-d4");
|
|
||||||
ASSERT(chess.process("d3") == "d2-d3");
|
|
||||||
ASSERT(chess.process("knight e4") == "f6-e4");
|
|
||||||
ASSERT(chess.process("king to e2") == "");
|
|
||||||
ASSERT(chess.process("king to d2") == "");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
if (WHISPER_SDL2)
|
|
||||||
set(TARGET wchess)
|
|
||||||
add_executable(${TARGET} wchess.cmd.cpp)
|
|
||||||
|
|
||||||
include(DefaultTargetOptions)
|
|
||||||
|
|
||||||
target_link_libraries(${TARGET} PRIVATE wchess-core common-sdl ${CMAKE_THREAD_LIBS_INIT})
|
|
||||||
endif ()
|
|
@ -1,247 +0,0 @@
|
|||||||
// Command line voice assisted chess
|
|
||||||
//
|
|
||||||
// Speak chess move commands to the microphone.
|
|
||||||
// The moves will translated to chessboard positions.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "WChess.h"
|
|
||||||
#include "common-sdl.h"
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
bool speed_up = false;
|
|
||||||
bool translate = false;
|
|
||||||
bool print_special = false;
|
|
||||||
bool print_energy = false;
|
|
||||||
bool no_timestamps = true;
|
|
||||||
bool use_gpu = 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;
|
|
||||||
};
|
|
||||||
|
|
||||||
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, " -su, --speed-up [%-7s] speed up audio by x2 (reduced accuracy)\n", params.speed_up ? "true" : "false");
|
|
||||||
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, " -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-penalty N [%-7.1f] scales down logits of nongrammar tokens\n", params.grammar_penalty);
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
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 == "-su" || arg == "--speed-up") { params.speed_up = true; }
|
|
||||||
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 == "-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-penalty") { params.grammar_penalty = std::stof(argv[++i]); }
|
|
||||||
else {
|
|
||||||
fprintf(stderr, "error: unknown argument: %s\n", arg.c_str());
|
|
||||||
whisper_print_usage(argc, argv, params);
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<WChess> g_wchess;
|
|
||||||
int g_moveCount = 0;
|
|
||||||
void set_move(const std::string & move, float) {
|
|
||||||
if (!move.empty()) {
|
|
||||||
g_moveCount++;
|
|
||||||
fprintf(stdout, "Move: %s\n\n", move.c_str());
|
|
||||||
}
|
|
||||||
else fprintf(stdout, "Move rejected\n\n");
|
|
||||||
fprintf(stdout, "%s\n", g_wchess->stringify_board().c_str());
|
|
||||||
fprintf(stdout, "%s\n", g_moveCount ? "White's turn" : "Black's turn");
|
|
||||||
}
|
|
||||||
|
|
||||||
audio_async g_audio(30*1000);
|
|
||||||
bool g_listening = false;
|
|
||||||
std::vector<float> g_pcmf32;
|
|
||||||
|
|
||||||
bool read_input() {
|
|
||||||
std::string input;
|
|
||||||
while (true) {
|
|
||||||
fprintf(stdout, "[(l)isten/(p)ause/(q)uit]: ");
|
|
||||||
std::cin >> input;
|
|
||||||
fprintf(stdout, "\n");
|
|
||||||
if (input[0] == 'q') {
|
|
||||||
fprintf(stdout, "Quitting\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (input[0] == 'l') {
|
|
||||||
if (!g_listening) {
|
|
||||||
fprintf(stdout, "Listening\n");
|
|
||||||
g_listening = true;
|
|
||||||
g_pcmf32.clear();
|
|
||||||
g_audio.resume();
|
|
||||||
g_audio.clear();
|
|
||||||
}
|
|
||||||
else fprintf(stdout, "Still listening\n");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (g_listening) {
|
|
||||||
g_listening = false;
|
|
||||||
g_audio.get(0, g_pcmf32);
|
|
||||||
g_audio.pause();
|
|
||||||
fprintf(stdout, "Processing\n");
|
|
||||||
}
|
|
||||||
else fprintf(stdout, "Not listening\n");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get_audio(std::vector<float> & pcmf32_cur) {
|
|
||||||
if (!read_input()) return false;
|
|
||||||
if (!g_pcmf32.empty()) pcmf32_cur = std::move(g_pcmf32);
|
|
||||||
else pcmf32_cur.clear();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char ** argv) {
|
|
||||||
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;
|
|
||||||
|
|
||||||
struct whisper_context * ctx = whisper_init_from_file_with_params(params.model.c_str(), cparams);
|
|
||||||
if (!ctx) {
|
|
||||||
fprintf(stderr, "%s: whisper_init_from_file_with_params() failed!\n", __func__);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// init audio
|
|
||||||
|
|
||||||
if (!g_audio.init(params.capture_id, WHISPER_SAMPLE_RATE)) {
|
|
||||||
fprintf(stderr, "%s: audio.init() failed!\n", __func__);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct whisper_full_params wparams = whisper_full_default_params(whisper_sampling_strategy::WHISPER_SAMPLING_GREEDY);
|
|
||||||
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.no_timestamps = true;
|
|
||||||
|
|
||||||
wparams.max_tokens = 32;
|
|
||||||
wparams.audio_ctx = 768; // partial encoder context for better performance
|
|
||||||
|
|
||||||
wparams.temperature = 0.0f;
|
|
||||||
wparams.temperature_inc = 2.0f;
|
|
||||||
wparams.greedy.best_of = 1;
|
|
||||||
|
|
||||||
wparams.beam_search.beam_size = 1;
|
|
||||||
|
|
||||||
wparams.language = "en";
|
|
||||||
|
|
||||||
wparams.grammar_penalty = 100.0;
|
|
||||||
|
|
||||||
wparams.initial_prompt = params.context.data();
|
|
||||||
|
|
||||||
WChess::callbacks cb;
|
|
||||||
cb.get_audio = get_audio;
|
|
||||||
cb.set_move = set_move;
|
|
||||||
|
|
||||||
WChess::settings s;
|
|
||||||
s.vad_ms = 2000;
|
|
||||||
s.prompt_ms = params.prompt_ms;
|
|
||||||
s.command_ms = params.command_ms;
|
|
||||||
s.vad_thold = params.vad_thold;
|
|
||||||
s.freq_thold = params.freq_thold;
|
|
||||||
s.print_energy = params.print_energy;
|
|
||||||
|
|
||||||
g_wchess.reset(new WChess(ctx, wparams, cb, s));
|
|
||||||
set_move("start", 0);
|
|
||||||
g_wchess->run();
|
|
||||||
|
|
||||||
whisper_print_timings(ctx);
|
|
||||||
whisper_free(ctx);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
set(TARGET wchess.wasm)
|
|
||||||
|
|
||||||
add_executable(${TARGET}
|
|
||||||
wchess.wasm.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
include(DefaultTargetOptions)
|
|
||||||
|
|
||||||
target_link_libraries(${TARGET} PRIVATE
|
|
||||||
common
|
|
||||||
wchess-core
|
|
||||||
)
|
|
||||||
|
|
||||||
unset(EXTRA_FLAGS)
|
|
||||||
|
|
||||||
if (WHISPER_WASM_SINGLE_FILE)
|
|
||||||
set(EXTRA_FLAGS "-s SINGLE_FILE=1")
|
|
||||||
message(STATUS "Embedding WASM inside chess.js")
|
|
||||||
|
|
||||||
add_custom_command(
|
|
||||||
TARGET ${TARGET} POST_BUILD
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy
|
|
||||||
${CMAKE_BINARY_DIR}/bin/${TARGET}.js
|
|
||||||
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}/js/chess.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']\" \
|
|
||||||
${EXTRA_FLAGS} \
|
|
||||||
")
|
|
||||||
|
|
||||||
|
|
||||||
add_custom_command(
|
|
||||||
TARGET ${TARGET} POST_BUILD
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/chessboardjs-1.0.0
|
|
||||||
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}/
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/jquery-3.7.1.min.js
|
|
||||||
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}/js/
|
|
||||||
)
|
|
||||||
|
|
||||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/index-tmpl.html ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}/index.html @ONLY)
|
|
||||||
configure_file(${CMAKE_SOURCE_DIR}/examples/helpers.js ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}/js/helpers.js @ONLY)
|
|
@ -1,54 +0,0 @@
|
|||||||
/*! chessboard.js v1.0.0 | (c) 2019 Chris Oakman | MIT License chessboardjs.com/license */
|
|
||||||
|
|
||||||
.clearfix-7da63 {
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-b72b1 {
|
|
||||||
border: 2px solid #404040;
|
|
||||||
box-sizing: content-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.square-55d63 {
|
|
||||||
float: left;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
/* disable any native browser highlighting */
|
|
||||||
-webkit-touch-callout: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-khtml-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.white-1e1d7 {
|
|
||||||
background-color: #f0d9b5;
|
|
||||||
color: #b58863;
|
|
||||||
}
|
|
||||||
|
|
||||||
.black-3c85d {
|
|
||||||
background-color: #b58863;
|
|
||||||
color: #f0d9b5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.highlight1-32417, .highlight2-9c5d2 {
|
|
||||||
box-shadow: inset 0 0 3px 3px yellow;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notation-322f9 {
|
|
||||||
cursor: default;
|
|
||||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alpha-d2270 {
|
|
||||||
bottom: 1px;
|
|
||||||
right: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.numeric-fc462 {
|
|
||||||
top: 2px;
|
|
||||||
left: 2px;
|
|
||||||
}
|
|
@ -1,2 +0,0 @@
|
|||||||
/*! chessboard.js v1.0.0 | (c) 2019 Chris Oakman | MIT License chessboardjs.com/license */
|
|
||||||
.clearfix-7da63{clear:both}.board-b72b1{border:2px solid #404040;box-sizing:content-box}.square-55d63{float:left;position:relative;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.white-1e1d7{background-color:#f0d9b5;color:#b58863}.black-3c85d{background-color:#b58863;color:#f0d9b5}.highlight1-32417,.highlight2-9c5d2{box-shadow:inset 0 0 3px 3px #ff0}.notation-322f9{cursor:default;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;position:absolute}.alpha-d2270{bottom:1px;right:3px}.numeric-fc462{top:2px;left:2px}
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
Before Width: | Height: | Size: 2.9 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user