Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v2.0 go #296

Merged
merged 10 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/go-demos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ jobs:

- name: Test filedemo
run: ./leopard_file_demo -access_key ${{secrets.PV_VALID_ACCESS_KEY}} -input_audio_path ../../resources/audio_samples/test.wav

build-grpc-demo:
runs-on: ${{ matrix.os }}
defaults:
Expand Down
Binary file modified binding/go/embedded/lib/common/leopard_params.pv
Binary file not shown.
Binary file not shown.
Binary file modified binding/go/embedded/lib/linux/x86_64/libpv_leopard.so
Binary file not shown.
Binary file modified binding/go/embedded/lib/mac/arm64/libpv_leopard.dylib
Binary file not shown.
Binary file modified binding/go/embedded/lib/mac/x86_64/libpv_leopard.dylib
Binary file not shown.
Binary file not shown.
Binary file modified binding/go/embedded/lib/raspberry-pi/cortex-a53/libpv_leopard.so
Binary file not shown.
Binary file not shown.
Binary file modified binding/go/embedded/lib/raspberry-pi/cortex-a72/libpv_leopard.so
Binary file not shown.
Binary file modified binding/go/embedded/lib/windows/amd64/libpv_leopard.dll
Binary file not shown.
2 changes: 1 addition & 1 deletion binding/go/go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module github.com/Picovoice/leopard/binding/go
module github.com/Picovoice/leopard/binding/go/v2

go 1.16
2 changes: 1 addition & 1 deletion binding/go/go_test.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module github.com/Picovoice/leopard/binding/go
module github.com/Picovoice/leopard/binding/go/v2

go 1.16

Expand Down
112 changes: 77 additions & 35 deletions binding/go/leopard.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2022 Picovoice Inc.
// Copyright 2022-2023 Picovoice Inc.
//
// You may not use this file except in compliance with the license. A copy of the license is
// located in the "LICENSE" file accompanying this source.
Expand Down Expand Up @@ -52,16 +52,27 @@ const (
)

type LeopardError struct {
StatusCode PvStatus
Message string
StatusCode PvStatus
Message string
MessageStack []string
}

type leopardExts struct {
values map[string]struct{}
}

func (e *LeopardError) Error() string {
return fmt.Sprintf("%s: %s", pvStatusToString(e.StatusCode), e.Message)
var message strings.Builder
message.WriteString(fmt.Sprintf("%s: %s", pvStatusToString(e.StatusCode), e.Message))

if len(e.MessageStack) > 0 {
message.WriteString(":")
}

for i, value := range e.MessageStack {
message.WriteString(fmt.Sprintf("\n [%d] %s", i, value))
}
return message.String()
}

// Leopard struct
Expand All @@ -80,10 +91,14 @@ type Leopard struct {

// Flag to enable automatic punctuation insertion.
EnableAutomaticPunctuation bool

// Flag to enable speaker diarization, which allows Leopard to differentiate speakers as part of the transcription process.
// Word metadata will include a `SpeakerTag` to identify unique speakers.
EnableDiarization bool
}

type LeopardWord struct {
// Transcribed word
// Transcribed word.
Word string

// Start of word in seconds.
Expand All @@ -94,6 +109,10 @@ type LeopardWord struct {

// Transcription confidence. It is a number within [0, 1].
Confidence float32

// Unique speaker identifier. It is `-1` if diarization is not enabled during initialization; otherwise,
// it's a non-negative integer identifying unique speakers, with `0` reserved for unknown speakers.
SpeakerTag int32
}

// private vars
Expand Down Expand Up @@ -123,15 +142,16 @@ func NewLeopard(accessKey string) Leopard {
ModelPath: defaultModelFile,
LibraryPath: defaultLibPath,
EnableAutomaticPunctuation: false,
EnableDiarization: false,
}
}

// Init function for Leopard. Must be called before attempting process
func (leopard *Leopard) Init() error {
if leopard.AccessKey == "" {
return &LeopardError{
INVALID_ARGUMENT,
"No AccessKey provided to Leopard"}
StatusCode: INVALID_ARGUMENT,
Message: "No AccessKey provided to Leopard"}
}

if leopard.ModelPath == "" {
Expand All @@ -144,21 +164,31 @@ func (leopard *Leopard) Init() error {

if _, err := os.Stat(leopard.LibraryPath); os.IsNotExist(err) {
return &LeopardError{
INVALID_ARGUMENT,
fmt.Sprintf("Specified library file could not be found at %s", leopard.LibraryPath)}
StatusCode: INVALID_ARGUMENT,
Message: fmt.Sprintf("Specified library file could not be found at %s", leopard.LibraryPath)}
}

if _, err := os.Stat(leopard.ModelPath); os.IsNotExist(err) {
return &LeopardError{
INVALID_ARGUMENT,
fmt.Sprintf("Specified model file could not be found at %s", leopard.ModelPath)}
StatusCode: INVALID_ARGUMENT,
Message: fmt.Sprintf("Specified model file could not be found at %s", leopard.ModelPath)}
}

ret := nativeLeopard.nativeInit(leopard)
if PvStatus(ret) != SUCCESS {
if ret != SUCCESS {
errorStatus, messageStack := nativeLeopard.nativeGetErrorStack()
if errorStatus != SUCCESS {
return &LeopardError{
StatusCode: errorStatus,
Message: "Unable to get Leopard error state",
}
}

return &LeopardError{
PvStatus(ret),
"Leopard init failed."}
StatusCode: ret,
Message: "Leopard init failed",
MessageStack: messageStack,
}
}

SampleRate = nativeLeopard.nativeSampleRate()
Expand All @@ -171,8 +201,8 @@ func (leopard *Leopard) Init() error {
func (leopard *Leopard) Delete() error {
if leopard.handle == nil {
return &LeopardError{
INVALID_STATE,
"Leopard has not been initialized or has already been deleted"}
StatusCode: INVALID_STATE,
Message: "Leopard has not been initialized or has already been deleted"}
}

nativeLeopard.nativeDelete(leopard)
Expand All @@ -187,21 +217,31 @@ func (leopard *Leopard) Delete() error {
func (leopard *Leopard) Process(pcm []int16) (string, []LeopardWord, error) {
if leopard.handle == nil {
return "", nil, &LeopardError{
INVALID_STATE,
"Leopard has not been initialized or has already been deleted"}
StatusCode: INVALID_STATE,
Message: "Leopard has not been initialized or has already been deleted"}
}

if len(pcm) == 0 {
return "", nil, &LeopardError{
INVALID_ARGUMENT,
"Audio data must not be empty"}
StatusCode: INVALID_ARGUMENT,
Message: "Audio data must not be empty"}
}

ret, transcript, words := nativeLeopard.nativeProcess(leopard, pcm)
if PvStatus(ret) != SUCCESS {
if ret != SUCCESS {
errorStatus, messageStack := nativeLeopard.nativeGetErrorStack()
if errorStatus != SUCCESS {
return "", nil, &LeopardError{
StatusCode: errorStatus,
Message: "Unable to get Leopard error state",
}
}

return "", nil, &LeopardError{
PvStatus(ret),
"Leopard process failed."}
StatusCode: ret,
Message: "Leopard process failed",
MessageStack: messageStack,
}
}

return transcript, words, nil
Expand All @@ -213,29 +253,31 @@ func (leopard *Leopard) Process(pcm []int16) (string, []LeopardWord, error) {
func (leopard *Leopard) ProcessFile(audioPath string) (string, []LeopardWord, error) {
if leopard.handle == nil {
return "", nil, &LeopardError{
INVALID_STATE,
"Leopard has not been initialized or has already been deleted"}
StatusCode: INVALID_STATE,
Message: "Leopard has not been initialized or has already been deleted"}
}

if _, err := os.Stat(audioPath); os.IsNotExist(err) {
return "", nil, &LeopardError{
INVALID_ARGUMENT,
fmt.Sprintf("Specified file could not be found at '%s'", audioPath)}
StatusCode: INVALID_ARGUMENT,
Message: fmt.Sprintf("Specified file could not be found at '%s'", audioPath)}
}

ret, transcript, words := nativeLeopard.nativeProcessFile(leopard, audioPath)
if ret != SUCCESS {
if ret == INVALID_ARGUMENT {
fileExtension := filepath.Ext(audioPath)
if !validExtensions.includes(fileExtension) {
return "", nil, &LeopardError{
INVALID_ARGUMENT,
fmt.Sprintf("Specified file with extension '%s' is not supported", fileExtension)}
errorStatus, messageStack := nativeLeopard.nativeGetErrorStack()
if errorStatus != SUCCESS {
return "", nil, &LeopardError{
StatusCode: errorStatus,
Message: "Unable to get Leopard error state",
}
}

return "", nil, &LeopardError{
PvStatus(ret),
"Leopard process failed."}
StatusCode: ret,
Message: "Leopard process failed",
MessageStack: messageStack,
}
}

return transcript, words, nil
Expand Down
71 changes: 71 additions & 0 deletions binding/go/leopard_native.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ typedef struct {
float start_sec;
float end_sec;
float confidence;
int32_t speaker_tag;
} pv_word_t;

typedef int32_t (*pv_leopard_sample_rate_func)();
Expand All @@ -81,18 +82,21 @@ typedef int32_t (*pv_leopard_init_func)(
const char *access_key,
const char *model_path,
bool enable_punctuation_detection,
bool enable_diarization,
void **object);

int32_t pv_leopard_init_wrapper(
void *f,
const char *access_key,
const char *model_path,
bool enable_punctuation_detection,
bool enable_diarization,
void **object) {
return ((pv_leopard_init_func) f)(
access_key,
model_path,
enable_punctuation_detection,
enable_diarization,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

go format is weird, space vs tabs

object);
}

Expand Down Expand Up @@ -161,6 +165,29 @@ void pv_leopard_words_delete_wrapper(void *f, pv_word_t *words) {
return ((pv_leopard_words_delete_func) f)(words);
}

typedef void (*pv_set_sdk_func)(const char *);

void pv_set_sdk_wrapper(void *f, const char *sdk) {
return ((pv_set_sdk_func) f)(sdk);
}

typedef int32_t (*pv_get_error_stack_func)(char ***, int32_t *);

int32_t pv_get_error_stack_wrapper(
void *f,
char ***message_stack,
int32_t *message_stack_depth) {
return ((pv_get_error_stack_func) f)(message_stack, message_stack_depth);
}

typedef void (*pv_free_error_stack_func)(char **);

void pv_free_error_stack_wrapper(
void *f,
char **message_stack) {
return ((pv_free_error_stack_func) f)(message_stack);
}

*/
import "C"

Expand All @@ -175,6 +202,7 @@ type nativeLeopardInterface interface {
nativeDelete(*Leopard)
nativeSampleRate()
nativeVersion()
nativeGetErrorStack()
}
type nativeLeopardType struct {
libraryHandle unsafe.Pointer
Expand All @@ -186,6 +214,9 @@ type nativeLeopardType struct {
pv_leopard_words_delete_ptr unsafe.Pointer
pv_leopard_version_ptr unsafe.Pointer
pv_sample_rate_ptr unsafe.Pointer
pv_set_sdk_ptr unsafe.Pointer
pv_get_error_stack_ptr unsafe.Pointer
pv_free_error_stack_ptr unsafe.Pointer
}

func (nl *nativeLeopardType) nativeInit(leopard *Leopard) (status PvStatus) {
Expand All @@ -194,6 +225,7 @@ func (nl *nativeLeopardType) nativeInit(leopard *Leopard) (status PvStatus) {
modelPathC = C.CString(leopard.ModelPath)
libraryPathC = C.CString(leopard.LibraryPath)
enableAutomaticPunctuationC = C.bool(leopard.EnableAutomaticPunctuation)
enableDiarizationC = C.bool(leopard.EnableDiarization)
)
defer C.free(unsafe.Pointer(accessKeyC))
defer C.free(unsafe.Pointer(modelPathC))
Expand All @@ -208,12 +240,20 @@ func (nl *nativeLeopardType) nativeInit(leopard *Leopard) (status PvStatus) {
nl.pv_leopard_words_delete_ptr = C.load_symbol(nl.libraryHandle, C.CString("pv_leopard_words_delete"))
nl.pv_leopard_version_ptr = C.load_symbol(nl.libraryHandle, C.CString("pv_leopard_version"))
nl.pv_sample_rate_ptr = C.load_symbol(nl.libraryHandle, C.CString("pv_sample_rate"))
nl.pv_set_sdk_ptr = C.load_symbol(nl.libraryHandle, C.CString("pv_set_sdk"))
nl.pv_get_error_stack_ptr = C.load_symbol(nl.libraryHandle, C.CString("pv_get_error_stack"))
nl.pv_free_error_stack_ptr = C.load_symbol(nl.libraryHandle, C.CString("pv_free_error_stack"))

C.pv_set_sdk_wrapper(
nl.pv_set_sdk_ptr,
C.CString("go"))

var ret = C.pv_leopard_init_wrapper(
nl.pv_leopard_init_ptr,
accessKeyC,
modelPathC,
enableAutomaticPunctuationC,
enableDiarizationC,
&leopard.handle)

return PvStatus(ret)
Expand Down Expand Up @@ -251,6 +291,7 @@ func (nl *nativeLeopardType) nativeProcess(leopard *Leopard, pcm []int16) (statu
StartSec: float32(cWords[i].start_sec),
EndSec: float32(cWords[i].end_sec),
Confidence: float32(cWords[i].confidence),
SpeakerTag: int32(cWords[i].speaker_tag),
}
words = append(words, n)
}
Expand Down Expand Up @@ -290,6 +331,7 @@ func (nl *nativeLeopardType) nativeProcessFile(leopard *Leopard, audioPath strin
StartSec: float32(cWords[i].start_sec),
EndSec: float32(cWords[i].end_sec),
Confidence: float32(cWords[i].confidence),
SpeakerTag: int32(cWords[i].speaker_tag),
}
words = append(words, n)
}
Expand All @@ -308,3 +350,32 @@ func (nl *nativeLeopardType) nativeSampleRate() (sampleRate int) {
func (nl *nativeLeopardType) nativeVersion() (version string) {
return C.GoString(C.pv_leopard_version_wrapper(nl.pv_leopard_version_ptr))
}

func (nl *nativeLeopardType) nativeGetErrorStack() (status PvStatus, messageStack []string) {
var messageStackDepthRef C.int32_t
var messageStackRef **C.char

var ret = C.pv_get_error_stack_wrapper(
nl.pv_get_error_stack_ptr,
&messageStackRef,
&messageStackDepthRef)

if PvStatus(ret) != SUCCESS {
return PvStatus(ret), []string{}
}

defer C.pv_free_error_stack_wrapper(
nl.pv_free_error_stack_ptr,
messageStackRef)

messageStackDepth := int(messageStackDepthRef)
messageStackSlice := (*[1 << 28]*C.char)(unsafe.Pointer(messageStackRef))[:messageStackDepth:messageStackDepth]

messageStack = make([]string, messageStackDepth)

for i := 0; i < messageStackDepth; i++ {
messageStack[i] = C.GoString(messageStackSlice[i])
}

return PvStatus(ret), messageStack
}
Loading
Loading