From 322f4e6c4ea8f8b3526bb6cebce0113cd157fe62 Mon Sep 17 00:00:00 2001 From: David Thorpe Date: Fri, 6 Jan 2023 17:32:28 +0000 Subject: [PATCH] go : bindings updated so they can be used in third party packages. (#379) * Updated bindings so they can be used in third pary packages. * Updated makefiles to set FMA flag on optionally, for xeon E5 on Darwin --- CMakeLists.txt | 6 ++++- Makefile | 5 +++- bindings/go/.gitignore | 1 - bindings/go/Makefile | 19 ++++++++------- bindings/go/README.md | 23 +++++++++++++++++++ .../go/examples/go-model-download/main.go | 14 ++++++----- bindings/go/go.sum | 23 +++++++++++++++++++ bindings/go/params.go | 3 --- bindings/go/whisper.go | 11 +++++++-- bindings/go/whisper_test.go | 5 +++- 10 files changed, 85 insertions(+), 25 deletions(-) create mode 100644 bindings/go/go.sum diff --git a/CMakeLists.txt b/CMakeLists.txt index ee437399..5233459e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,6 +53,7 @@ if (APPLE) option(WHISPER_NO_ACCELERATE "whisper: disable Accelerate framework" OFF) option(WHISPER_NO_AVX "whisper: disable AVX" OFF) option(WHISPER_NO_AVX2 "whisper: disable AVX2" OFF) + option(WHISPER_NO_FMA "whisper: disable FMA" OFF) else() option(WHISPER_SUPPORT_OPENBLAS "whisper: support for OpenBLAS" OFF) endif() @@ -166,7 +167,10 @@ else() if(NOT WHISPER_NO_AVX2) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mavx2") endif() - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfma -mf16c") + if(NOT WHISPER_NO_FMA) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfma") + endif() + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mf16c") endif() endif() endif() diff --git a/Makefile b/Makefile index a9d205a7..b7edea8f 100644 --- a/Makefile +++ b/Makefile @@ -58,8 +58,11 @@ endif # feel free to update the Makefile for your architecture and send a pull request or issue ifeq ($(UNAME_M),$(filter $(UNAME_M),x86_64 i686)) ifeq ($(UNAME_S),Darwin) - CFLAGS += -mfma -mf16c + CFLAGS += -mf16c AVX1_M := $(shell sysctl machdep.cpu.features) + ifneq (,$(findstring FMA,$(AVX1_M))) + CFLAGS += -mfma + endif ifneq (,$(findstring AVX1.0,$(AVX1_M))) CFLAGS += -mavx endif diff --git a/bindings/go/.gitignore b/bindings/go/.gitignore index b4e10840..036df1d3 100644 --- a/bindings/go/.gitignore +++ b/bindings/go/.gitignore @@ -1,3 +1,2 @@ build models -go.sum diff --git a/bindings/go/Makefile b/bindings/go/Makefile index 420981ac..6be29799 100644 --- a/bindings/go/Makefile +++ b/bindings/go/Makefile @@ -1,28 +1,27 @@ -CMAKE := $(shell which cmake) -BUILD_DIR := "build" -MODELS_DIR := "models" +BUILD_DIR := build +MODELS_DIR := models EXAMPLES_DIR := $(wildcard examples/*) -C_INCLUDE_PATH := "../.." +INCLUDE_PATH := $(abspath ../..) +LIBRARY_PATH := $(abspath ../..) all: clean whisper examples whisper: mkdir @echo Build whisper - @${CMAKE} -S ../.. -B ${BUILD_DIR} -D BUILD_SHARED_LIBS=off -D WHISPER_NO_AVX2=on - @${CMAKE} --build ${BUILD_DIR} --target whisper + @${MAKE} -C ../.. libwhisper.a test: model-small whisper modtidy - @go test -v . - @go test -v ./pkg/whisper/... + @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/... examples: $(EXAMPLES_DIR) model-small: mkdir examples/go-model-download - @${BUILD_DIR}/go-model-download -out models small.en + @${BUILD_DIR}/go-model-download -out models ggml-small.en.bin $(EXAMPLES_DIR): mkdir whisper modtidy @echo Build example $(notdir $@) - @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 $@) ./$@ mkdir: @echo Mkdir ${BUILD_DIR} diff --git a/bindings/go/README.md b/bindings/go/README.md index 8ae89c77..2b14c309 100644 --- a/bindings/go/README.md +++ b/bindings/go/README.md @@ -74,4 +74,27 @@ And you can then test a model against samples with the following command: ./build/go-whisper -model models/ggml-tiny.en.bin samples/jfk.wav ``` +## Using the bindings + +To use the bindings in your own software, + + 1. Import `github.com/ggerganov/whisper.cpp/bindings/go/pkg/whisper` (or `github.com/ggerganov/whisper.cpp/bindings/go` into your package; + 2. Compile `libwhisper.a` (you can use `make whisper` in the `bindings/go` directory); + 3. Link your go binary against whisper by setting the environment variables `C_INCLUDE_PATH` and `LIBRARY_PATH` + to point to the `whisper.h` file directory and `libwhisper.a` file directory respectively. + +Look at the `Makefile` in the `bindings/go` directory for an example. + +The API Documentation: + + * https://pkg.go.dev/github.com/ggerganov/whisper.cpp/bindings/go + * https://pkg.go.dev/github.com/ggerganov/whisper.cpp/bindings/go/pkg/whisper + +Getting help: + + * Follow the discussion for the go bindings [here](https://github.com/ggerganov/whisper.cpp/discussions/312) + +## License + +The license for the Go bindings is the same as the license for the rest of the whisper.cpp project, which is the MIT License. See the `LICENSE` file for more details. diff --git a/bindings/go/examples/go-model-download/main.go b/bindings/go/examples/go-model-download/main.go index 841a2c65..91d016ac 100644 --- a/bindings/go/examples/go-model-download/main.go +++ b/bindings/go/examples/go-model-download/main.go @@ -17,15 +17,14 @@ import ( // CONSTANTS const ( - srcUrl = "https://huggingface.co/" // The location of the models - srcPathPrefix = "/datasets/ggerganov/whisper.cpp/resolve/main/ggml" // Filename prefix - srcExt = ".bin" // Filename extension - bufSize = 1024 * 64 // Size of the buffer used for downloading the model + srcUrl = "https://huggingface.co/datasets/ggerganov/whisper.cpp/resolve/main" // The location of the models + srcExt = ".bin" // Filename extension + bufSize = 1024 * 64 // Size of the buffer used for downloading the model ) var ( // The models which will be downloaded, if no model is specified as an argument - modelNames = []string{"tiny.en", "tiny", "base.en", "base", "small.en", "small", "medium.en", "medium", "large-v1", "large"} + 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 ( @@ -123,11 +122,14 @@ func GetModels() []string { // URLForModel returns the URL for the given model on huggingface.co func URLForModel(model string) (string, error) { + if filepath.Ext(model) != srcExt { + model += srcExt + } url, err := url.Parse(srcUrl) if err != nil { return "", err } else { - url.Path = srcPathPrefix + "-" + model + srcExt + url.Path = filepath.Join(url.Path, model) } return url.String(), nil } diff --git a/bindings/go/go.sum b/bindings/go/go.sum new file mode 100644 index 00000000..870ebdc3 --- /dev/null +++ b/bindings/go/go.sum @@ -0,0 +1,23 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-audio/audio v1.0.0 h1:zS9vebldgbQqktK4H0lUqWrG8P0NxCJVqcj7ZpNnwd4= +github.com/go-audio/audio v1.0.0/go.mod h1:6uAu0+H2lHkwdGsAY+j2wHPNPpPoeg5AaEFh9FlA+Zs= +github.com/go-audio/riff v1.0.0 h1:d8iCGbDvox9BfLagY94fBynxSPHO80LmZCaOsmKxokA= +github.com/go-audio/riff v1.0.0/go.mod h1:l3cQwc85y79NQFCRB7TiPoNiaijp6q8Z0Uv38rVG498= +github.com/go-audio/wav v1.1.0 h1:jQgLtbqBzY7G+BM8fXF7AHUk1uHUviWS4X39d5rsL2g= +github.com/go-audio/wav v1.1.0/go.mod h1:mpe9qfwbScEbkd8uybLuIpTgHyrISw/OTuvjUW2iGtE= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/bindings/go/params.go b/bindings/go/params.go index 7f4c509c..c67a7299 100644 --- a/bindings/go/params.go +++ b/bindings/go/params.go @@ -1,8 +1,5 @@ package whisper -// This file defines the whisper_token, whisper_token_data and whisper_full_params -// structures, which are used by the whisper_full() function. - import ( "fmt" ) diff --git a/bindings/go/whisper.go b/bindings/go/whisper.go index 2584f7bb..9381879c 100644 --- a/bindings/go/whisper.go +++ b/bindings/go/whisper.go @@ -9,8 +9,7 @@ import ( // CGO /* -#cgo CFLAGS: -I${SRCDIR}/../.. -#cgo LDFLAGS: -L${SRCDIR}/build -lwhisper -lm -lstdc++ +#cgo LDFLAGS: -lwhisper -lm -lstdc++ #cgo darwin LDFLAGS: -framework Accelerate #include #include @@ -171,6 +170,10 @@ func (ctx *Context) Whisper_tokenize(text string, tokens []Token) (int, error) { } // Return the id of the specified language, returns -1 if not found +// Examples: +// +// "de" -> 2 +// "german" -> 2 func (ctx *Context) Whisper_lang_id(lang string) int { return int(C.whisper_lang_id(C.CString(lang))) } @@ -211,6 +214,10 @@ func (ctx *Context) Whisper_n_text_ctx() int { return int(C.whisper_n_text_ctx((*C.struct_whisper_context)(ctx))) } +func (ctx *Context) Whisper_n_audio_ctx() int { + return int(C.whisper_n_audio_ctx((*C.struct_whisper_context)(ctx))) +} + func (ctx *Context) Whisper_is_multilingual() int { return int(C.whisper_is_multilingual((*C.struct_whisper_context)(ctx))) } diff --git a/bindings/go/whisper_test.go b/bindings/go/whisper_test.go index d7b8caef..2c95c81f 100644 --- a/bindings/go/whisper_test.go +++ b/bindings/go/whisper_test.go @@ -50,7 +50,10 @@ func Test_Whisper_001(t *testing.T) { ctx := whisper.Whisper_init(ModelPath) assert.NotNil(ctx) defer ctx.Whisper_free() - assert.NoError(ctx.Whisper_full(ctx.Whisper_full_default_params(whisper.SAMPLING_GREEDY), buf.AsFloat32Buffer().Data, nil, nil)) + params := ctx.Whisper_full_default_params(whisper.SAMPLING_GREEDY) + data := buf.AsFloat32Buffer().Data + err = ctx.Whisper_full(params, data, nil, nil) + assert.NoError(err) // Print out tokens num_segments := ctx.Whisper_full_n_segments()