Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
14 changes: 14 additions & 0 deletions .github/workflows/test-go.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ jobs:
cp -v ../scripts/go/_internal/lib/x86_64-pc-windows-gnu/*.dll ../scripts/go/_internal/non-streaming-canary-decode-files/
cp -v ../scripts/go/_internal/lib/x86_64-pc-windows-gnu/*.dll ../scripts/go/_internal/non-streaming-decode-files/
cp -v ../scripts/go/_internal/lib/x86_64-pc-windows-gnu/*.dll ../scripts/go/_internal/non-streaming-omnilingual-asr-ctc-decode-files
cp -v ../scripts/go/_internal/lib/x86_64-pc-windows-gnu/*.dll ../scripts/go/_internal/non-streaming-medasr-ctc-decode-files
cp -v ../scripts/go/_internal/lib/x86_64-pc-windows-gnu/*.dll ../scripts/go/_internal/non-streaming-speaker-diarization/
cp -v ../scripts/go/_internal/lib/x86_64-pc-windows-gnu/*.dll ../scripts/go/_internal/non-streaming-tts/
cp -v ../scripts/go/_internal/lib/x86_64-pc-windows-gnu/*.dll ../scripts/go/_internal/speaker-identification/
Expand Down Expand Up @@ -141,6 +142,19 @@ jobs:
name: ${{ matrix.os }}-libs
path: to-upload/

- name: Test non-streaming decoding files with MedASR
shell: bash
run: |
cd scripts/go/_internal/non-streaming-medasr-ctc-decode-files
ls -lh
go mod tidy
cat go.mod
go build
ls -lh

./run.sh
rm -rf sherpa-onnx-medasr-*

- name: Test non-streaming decoding files with Omnilingual ASR
shell: bash
run: |
Expand Down
3 changes: 3 additions & 0 deletions go-api-examples/non-streaming-medasr-ctc-decode-files/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module non-streaming-medasr-ctc-decode-files

go 1.17
Comment on lines +1 to +3

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add missing dependencies to the module manifest.

The main.go file imports github.com/k2-fsa/sherpa-onnx-go/sherpa_onnx and github.com/youpy/go-wav, but this go.mod does not declare them. Users attempting to build this example directly will encounter unresolved dependency errors.

🔎 Add the missing dependencies
 module non-streaming-medasr-ctc-decode-files
 
 go 1.17
+
+replace github.com/k2-fsa/sherpa-onnx-go/sherpa_onnx => ../../scripts/go
+
+require (
+	github.com/k2-fsa/sherpa-onnx-go/sherpa_onnx v0.0.0-00010101000000-000000000000
+	github.com/youpy/go-wav v0.3.2
+)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
module non-streaming-medasr-ctc-decode-files
go 1.17
module non-streaming-medasr-ctc-decode-files
go 1.17
replace github.com/k2-fsa/sherpa-onnx-go/sherpa_onnx => ../../scripts/go
require (
github.com/k2-fsa/sherpa-onnx-go/sherpa_onnx v0.0.0-00010101000000-000000000000
github.com/youpy/go-wav v0.3.2
)
🤖 Prompt for AI Agents
In go-api-examples/non-streaming-medasr-ctc-decode-files/go.mod lines 1-3, the
module manifest is missing required dependencies used in main.go; add require
directives for github.com/k2-fsa/sherpa-onnx-go/sherpa_onnx and
github.com/youpy/go-wav with appropriate semantic versions (or run the module
add command to resolve and populate go.mod and go.sum) so the example builds
without unresolved dependency errors.

97 changes: 97 additions & 0 deletions go-api-examples/non-streaming-medasr-ctc-decode-files/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package main

import (
"bytes"
"encoding/binary"
"log"
"os"
"strings"

sherpa "github.com/k2-fsa/sherpa-onnx-go/sherpa_onnx"
"github.com/youpy/go-wav"
)

func main() {
log.SetFlags(log.LstdFlags | log.Lmicroseconds)

config := sherpa.OfflineRecognizerConfig{}

config.ModelConfig.MedAsr.Model = "./sherpa-onnx-medasr-ctc-en-int8-2025-12-25/model.int8.onnx"
config.ModelConfig.Tokens = "./sherpa-onnx-medasr-ctc-en-int8-2025-12-25/tokens.txt"

waveFilename := "./sherpa-onnx-medasr-ctc-en-int8-2025-12-25/test_wavs/0.wav"
Comment on lines +19 to +22

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The file paths for the model, tokens, and wave file are hardcoded. While this is an example, making them configurable via command-line flags would make the example more flexible and easier to use with different files. Consider using the flag package for this.


samples, sampleRate := readWave(waveFilename)

log.Println("Initializing recognizer (may take several seconds)")
recognizer := sherpa.NewOfflineRecognizer(&config)
log.Println("Recognizer created!")
defer sherpa.DeleteOfflineRecognizer(recognizer)

log.Println("Start decoding!")
stream := sherpa.NewOfflineStream(recognizer)
defer sherpa.DeleteOfflineStream(stream)

stream.AcceptWaveform(sampleRate, samples)

recognizer.Decode(stream)
log.Println("Decoding done!")
result := stream.GetResult()

log.Println("Text: " + strings.ToLower(result.Text))
}

func readWave(filename string) (samples []float32, sampleRate int) {
file, _ := os.Open(filename)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

critical

The error returned by os.Open() is ignored. If the file does not exist or cannot be opened, this will lead to a panic later in the code. You should handle this error to make the program more robust.

Suggested change
file, _ := os.Open(filename)
file, err := os.Open(filename)
if err != nil {
log.Fatalf("Failed to open %s: %v", filename, err)
}

defer file.Close()
Comment on lines +44 to +46

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix unhandled file open error.

The error from os.Open is ignored, which will cause a nil pointer dereference panic when defer file.Close() is executed if the file cannot be opened.

🔎 Handle the error properly
 func readWave(filename string) (samples []float32, sampleRate int) {
-	file, _ := os.Open(filename)
+	file, err := os.Open(filename)
+	if err != nil {
+		log.Fatalf("Failed to open file: %v", err)
+	}
 	defer file.Close()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func readWave(filename string) (samples []float32, sampleRate int) {
file, _ := os.Open(filename)
defer file.Close()
func readWave(filename string) (samples []float32, sampleRate int) {
file, err := os.Open(filename)
if err != nil {
log.Fatalf("Failed to open file: %v", err)
}
defer file.Close()
🤖 Prompt for AI Agents
In go-api-examples/non-streaming-medasr-ctc-decode-files/main.go around lines
44-46, the call to os.Open ignores the error and defers file.Close()
immediately, which can panic if the open fails; change the code to capture the
error (file, err := os.Open(filename)), check if err != nil and handle it
(return nil, 0 or propagate/log the error as appropriate for this package), and
only call defer file.Close() after confirming file is non-nil; ensure callers
can distinguish the failure by returning zero values or returning the error up
the call chain.


reader := wav.NewReader(file)
format, err := reader.Format()
if err != nil {
log.Fatalf("Failed to read wave format")
}
Comment on lines +49 to +52

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

When logging a fatal error, it's helpful to include the underlying error message for better debugging.

Suggested change
format, err := reader.Format()
if err != nil {
log.Fatalf("Failed to read wave format")
}
format, err := reader.Format()
if err != nil {
log.Fatalf("Failed to read wave format: %v", err)
}


if format.AudioFormat != 1 {
log.Fatalf("Support only PCM format. Given: %v\n", format.AudioFormat)
}

if format.NumChannels != 1 {
log.Fatalf("Support only 1 channel wave file. Given: %v\n", format.NumChannels)
}

if format.BitsPerSample != 16 {
log.Fatalf("Support only 16-bit per sample. Given: %v\n", format.BitsPerSample)
}

reader.Duration() // so that it initializes reader.Size

buf := make([]byte, reader.Size)
n, err := reader.Read(buf)
if n != int(reader.Size) {
log.Fatalf("Failed to read %v bytes. Returned %v bytes\n", reader.Size, n)
}
Comment on lines +69 to +72

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The error returned by reader.Read() is not checked. An error during reading could lead to processing incomplete or corrupt data. It's important to handle this error. Also, log.Fatalf automatically adds a newline, so the \n at the end of the format string is redundant.

Suggested change
n, err := reader.Read(buf)
if n != int(reader.Size) {
log.Fatalf("Failed to read %v bytes. Returned %v bytes\n", reader.Size, n)
}
n, err := reader.Read(buf)
if err != nil {
log.Fatalf("Failed to read wave data: %v", err)
}
if n != int(reader.Size) {
log.Fatalf("Failed to read %v bytes. Got %v bytes", reader.Size, n)
}


samples = samplesInt16ToFloat(buf)
sampleRate = int(format.SampleRate)

return
}

func samplesInt16ToFloat(inSamples []byte) []float32 {
numSamples := len(inSamples) / 2
outSamples := make([]float32, numSamples)

for i := 0; i != numSamples; i++ {
s := inSamples[i*2 : (i+1)*2]

var s16 int16
buf := bytes.NewReader(s)
err := binary.Read(buf, binary.LittleEndian, &s16)
if err != nil {
log.Fatal("Failed to parse 16-bit sample")
}
outSamples[i] = float32(s16) / 32768
}
Comment on lines +84 to +94

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The current implementation of samplesInt16ToFloat creates a new bytes.Reader for every sample inside the loop, which is inefficient. You can achieve better performance by directly reading from the byte slice using binary.LittleEndian.Uint16.

for i := 0; i < numSamples; i++ {
		s16 := int16(binary.LittleEndian.Uint16(inSamples[i*2:]))
		outSamples[i] = float32(s16) / 32768.0
	}


return outSamples
}
15 changes: 15 additions & 0 deletions go-api-examples/non-streaming-medasr-ctc-decode-files/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env bash

set -ex

export CGO_ENABLED=1

if [ ! -f ./sherpa-onnx-medasr-ctc-en-int8-2025-12-25/tokens.txt ]; then
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-medasr-ctc-en-int8-2025-12-25.tar.bz2
tar xvf sherpa-onnx-medasr-ctc-en-int8-2025-12-25.tar.bz2
rm sherpa-onnx-medasr-ctc-en-int8-2025-12-25.tar.bz2
fi

go mod tidy
go build
./non-streaming-medasr-ctc-decode-files
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
non-streaming-medasr-ctc-decode-files
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module non-streaming-medasr-ctc-decode-files

go 1.17

replace github.com/k2-fsa/sherpa-onnx-go/sherpa_onnx => ../
11 changes: 11 additions & 0 deletions scripts/go/sherpa_onnx.go
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,10 @@ type OfflineOmnilingualAsrCtcModelConfig struct {
Model string // Path to the model, e.g., model.onnx or model.int8.onnx
}

type OfflineMedAsrCtcModelConfig struct {
Model string // Path to the model, e.g., model.onnx or model.int8.onnx
}

type OfflineDolphinModelConfig struct {
Model string // Path to the model, e.g., model.onnx or model.int8.onnx
}
Expand Down Expand Up @@ -485,6 +489,7 @@ type OfflineModelConfig struct {
Canary OfflineCanaryModelConfig
WenetCtc OfflineWenetCtcModelConfig
Omnilingual OfflineOmnilingualAsrCtcModelConfig
MedAsr OfflineMedAsrCtcModelConfig
Tokens string // Path to tokens.txt

// Number of threads to use for neural network computation
Expand Down Expand Up @@ -589,6 +594,7 @@ func newCOfflineRecognizerConfig(config *OfflineRecognizerConfig) *C.struct_Sher
c.model_config.wenet_ctc.model = C.CString(config.ModelConfig.WenetCtc.Model)

c.model_config.omnilingual.model = C.CString(config.ModelConfig.Omnilingual.Model)
c.model_config.medasr.model = C.CString(config.ModelConfig.MedAsr.Model)

c.model_config.tokens = C.CString(config.ModelConfig.Tokens)

Expand Down Expand Up @@ -742,6 +748,11 @@ func freeCOfflineRecognizerConfig(c *C.struct_SherpaOnnxOfflineRecognizerConfig)
c.model_config.wenet_ctc.model = nil
}

if c.model_config.medasr.model != nil {
C.free(unsafe.Pointer(c.model_config.medasr.model))
c.model_config.medasr.model = nil
}

if c.model_config.omnilingual.model != nil {
C.free(unsafe.Pointer(c.model_config.omnilingual.model))
c.model_config.omnilingual.model = nil
Expand Down