Init
This commit is contained in:
commit
5eedba33b9
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/model
|
||||||
|
voskcli
|
14
go.mod
Normal file
14
go.mod
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
module git.helcel.net/helcel/voskcli
|
||||||
|
|
||||||
|
go 1.20
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/alphacep/vosk-api/go v0.3.46-0.20230429023501-12f29a3415e4
|
||||||
|
github.com/m7shapan/njson v1.0.8
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/tidwall/gjson v1.12.1 // indirect
|
||||||
|
github.com/tidwall/match v1.1.1 // indirect
|
||||||
|
github.com/tidwall/pretty v1.2.0 // indirect
|
||||||
|
)
|
13
go.sum
Normal file
13
go.sum
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
github.com/alphacep/vosk-api/go v0.3.46-0.20230429023501-12f29a3415e4 h1:MGwc3N+btl1ayMYPbe5VyK0vifwwmgsUbDxZd9auO2Y=
|
||||||
|
github.com/alphacep/vosk-api/go v0.3.46-0.20230429023501-12f29a3415e4/go.mod h1:9X8IJsHnFk/b1xyvjlZifo+ZL5VTAx3LW+JQce/eRcA=
|
||||||
|
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||||
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/m7shapan/njson v1.0.8 h1:5FNWjy8WhZ53Ev4ricYPtZfAv7o3/7wAKL+zhXxknvg=
|
||||||
|
github.com/m7shapan/njson v1.0.8/go.mod h1:h3P0qmwsv8+s+cE07cXuTgtyKCrBIAEUger0hA71Fy0=
|
||||||
|
github.com/tidwall/gjson v1.12.1 h1:ikuZsLdhr8Ws0IdROXUS1Gi4v9Z4pGqpX/CvJkxvfpo=
|
||||||
|
github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
|
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||||
|
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||||
|
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||||
|
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
177
voskcli.go
Normal file
177
voskcli.go
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
njson "github.com/m7shapan/njson"
|
||||||
|
vosk "github.com/alphacep/vosk-api/go"
|
||||||
|
)
|
||||||
|
|
||||||
|
func usage() {
|
||||||
|
fmt.Println(`Usage: voskcli [FILE...]
|
||||||
|
|
||||||
|
--mic=NAME Specify the audio device (use 'arecord -L' to find mic name).
|
||||||
|
--model=PATH Path to the model to use`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fatal(a ...any) {
|
||||||
|
fmt.Fprintln(os.Stderr, "voskcli:", fmt.Sprint(a...))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
func warn(a ...any) {
|
||||||
|
fmt.Fprintln(os.Stderr, "voskcli: WARNING:", fmt.Sprint(a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func print(a ...any) {
|
||||||
|
fmt.Fprintln(os.Stdout, fmt.Sprint(a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func parseResults(json string) string {
|
||||||
|
type ResultJson struct {
|
||||||
|
Text string `njson:"text"`
|
||||||
|
Words []string `njson:"result.#.word"`
|
||||||
|
Confs []float64 `njson:"result.#.conf"`
|
||||||
|
Starts []float64 `njson:"result.#.start"`
|
||||||
|
Ends []float64 `njson:"result.#.end"`
|
||||||
|
Confidence float64 `njson:"confidence"` // only with alternatives
|
||||||
|
}
|
||||||
|
var s struct {
|
||||||
|
Alternatives []ResultJson `njson:"alternatives"`
|
||||||
|
|
||||||
|
// copy paste of ResultJson
|
||||||
|
Text string `njson:"text"`
|
||||||
|
Words []string `njson:"result.#.word"`
|
||||||
|
Confs []float64 `njson:"result.#.conf"`
|
||||||
|
Starts []float64 `njson:"result.#.start"`
|
||||||
|
Ends []float64 `njson:"result.#.end"`
|
||||||
|
Confidence float64 `njson:"confidence"` // only with alternatives
|
||||||
|
|
||||||
|
ParText string `njson:"partial"`
|
||||||
|
ParWords []string `njson:"partial_result.#.word"`
|
||||||
|
ParConfs []float64 `njson:"partial_result.#.conf"`
|
||||||
|
ParStarts []float64 `njson:"partial_result.#.start"`
|
||||||
|
ParEnds []float64 `njson:"partial_result.#.end"`
|
||||||
|
}
|
||||||
|
err := njson.Unmarshal([]byte(json), &s)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
print(json);
|
||||||
|
return s.Text
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func getMic(mic string) string {
|
||||||
|
if mic == "" {
|
||||||
|
mic = "default"
|
||||||
|
}
|
||||||
|
return mic
|
||||||
|
}
|
||||||
|
|
||||||
|
func record(mic string) (io.Reader, error) {
|
||||||
|
cmd := exec.Command("arecord", "-q", "-fS16_LE", "-c1", "-r16000", "-D", mic)
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
stdout, err := cmd.StdoutPipe()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = cmd.Start()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return stdout, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Action struct {
|
||||||
|
Tags []string
|
||||||
|
Text string
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var opts struct {
|
||||||
|
Mic string
|
||||||
|
Model string
|
||||||
|
Verbose bool
|
||||||
|
}
|
||||||
|
var model *vosk.VoskModel
|
||||||
|
{
|
||||||
|
m := os.Getenv("VOSK_MODEL")
|
||||||
|
if m == "" {
|
||||||
|
fatal("you need to install the a vosk model and set $VOSK_MODEL")
|
||||||
|
}
|
||||||
|
if opts.Verbose {
|
||||||
|
fmt.Fprintln(os.Stderr, "Model: " + m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
model, err = vosk.NewModel(m)
|
||||||
|
if err != nil {
|
||||||
|
fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defer model.Free()
|
||||||
|
|
||||||
|
var r *vosk.VoskRecognizer
|
||||||
|
{
|
||||||
|
var err error
|
||||||
|
r, err = vosk.NewRecognizer(model, float64(16000))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
r.SetWords(1)
|
||||||
|
r.SetMaxAlternatives(3)//3 ? 10 ?
|
||||||
|
}
|
||||||
|
defer r.Free()
|
||||||
|
|
||||||
|
var mic string
|
||||||
|
var audio io.Reader
|
||||||
|
|
||||||
|
mic = getMic(opts.Mic)
|
||||||
|
if opts.Verbose {
|
||||||
|
fmt.Fprintln(os.Stderr, "Microphone: " + mic)
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
audio, err = record(mic)
|
||||||
|
if err != nil {
|
||||||
|
fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
terminate := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(terminate, os.Interrupt, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-terminate: return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
chunk := make([]byte, 4096)
|
||||||
|
_, err := io.ReadFull(audio, chunk)
|
||||||
|
if err != nil {
|
||||||
|
if err != io.EOF && err != io.ErrUnexpectedEOF {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
r, err := record(mic)
|
||||||
|
if err != nil {
|
||||||
|
warn(err)
|
||||||
|
}
|
||||||
|
audio = r
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var res string
|
||||||
|
if (r.AcceptWaveform(chunk) == 1) {
|
||||||
|
res = r.FinalResult()
|
||||||
|
parseResults(res);
|
||||||
|
} else {
|
||||||
|
res = r.PartialResult() // r.Result()
|
||||||
|
parseResults(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user