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