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

Using PortAudio with Snowboy with Golang #311

Closed
deadprogram opened this issue Nov 19, 2017 · 4 comments
Closed

Using PortAudio with Snowboy with Golang #311

deadprogram opened this issue Nov 19, 2017 · 4 comments

Comments

@deadprogram
Copy link
Contributor

Hello, everyone. Thanks for working on this interesting lib.

I am trying to use Snowboy from the Golang wrappers https://github.com/brentnd/go-snowboy created by @brentnd that wraps the https://github.com/Kitt-AI/snowboy/tree/master/swig/Go package in this project, along with the PortAudio Golang wrapper https://github.com/gordonklaus/portaudio from @gordonklaus

I am able to grab data from the mic, and pump it into Snowboy, but it seems like must be in the wrong format or something?

This is my code:

package main

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"time"

	"github.com/brentnd/go-snowboy"
	"github.com/gordonklaus/portaudio"
)

func chkErr(err error) error {
	if err != nil {
		fmt.Errorf("%s", err)
		return err
	}
	return nil
}

type Sound struct {
	stream *portaudio.Stream
	data   []int16
}

func (s *Sound) Read(p []byte) (int, error) {
	s.stream.Read()

	buf := &bytes.Buffer{}
	for _, v := range s.data {
		binary.Write(buf, binary.LittleEndian, v)
	}

	p = buf.Bytes()
	fmt.Println("buffer:", p[:10], "...", p[len(p)-10:])
	return len(p), nil
}

func main() {
	inputChannels := 1
	outputChannels := 0
	sampleRate := 16000
	framesPerBuffer := make([]int16, 1024)

	err := portaudio.Initialize()
	chkErr(err)
	defer portaudio.Terminate()

	stream, err := portaudio.OpenDefaultStream(inputChannels, outputChannels, float64(sampleRate), len(framesPerBuffer), framesPerBuffer)
	chkErr(err)
	defer stream.Close()

	d := snowboy.NewDetector("/home/ron/Development/snowboy/resources/common.res")
	defer d.Close()

	d.HandleFunc(snowboy.NewHotword("/home/ron/Development/snowboy/resources/snowboy.umdl", 0.5), func(string) {
		fmt.Println("Handle func in snowboy.NewDefaultHotword")
	})

	d.HandleSilenceFunc(500*time.Millisecond, func(string) {
		fmt.Println("silence detected")
	})

	sr, nc, bd := d.AudioFormat()
	fmt.Printf("sample rate=%d, num channels=%d, bit depth=%d\n", sr, nc, bd)

	err = stream.Start()
	chkErr(err)

	sounder := &Sound{stream, framesPerBuffer}

	err = d.ReadAndDetect(sounder)
	chkErr(err)
}

I know I am getting data, here is my output:

$ go run main.go 
sample rate=16000, num channels=1, bit depth=16
buffer: [0 0 0 0 2 0 253 255 4 0] ... [25 255 143 255 177 254 135 255 53 0]
buffer: [67 255 86 255 126 255 145 255 242 0] ... [113 1 150 1 157 254 233 0 7 2]
buffer: [3 1 250 1 8 2 224 1 42 1] ... [191 255 205 0 162 1 120 1 96 2]
buffer: [134 2 108 1 86 2 32 1 160 254] ... [204 255 129 255 249 255 25 0 15 0]
buffer: [137 255 86 255 116 255 104 255 175 255] ... [202 254 108 254 146 255 109 0 243 254]
buffer: [117 255 45 255 199 253 19 0 198 255] ... [98 254 241 253 135 254 24 255 102 254]
buffer: [21 254 11 255 12 255 28 253 66 253] ... [211 255 77 2 33 0 155 0 86 255]
buffer: [230 255 110 2 28 1 71 1 173 1] ... [15 4 37 3 167 1 212 255 136 255]
silence detected
buffer: [186 0 109 255 68 254 219 254 207 255] ... [55 8 132 2 108 254 195 252 47 1]
buffer: [150 5 135 0 126 252 49 247 59 243] ... [214 0 246 3 65 238 147 218 247 11]
buffer: [169 44 77 10 224 23 204 39 106 41] ... [138 87 176 88 5 87 139 87 28 90]
...

Note that the buffer does contain some kind of data, but for whatever reason Snowboy is not recognizing it.

Anyone have any ideas on how to debug this? Seems like it would be a useful demo, I'd love to contribute. Thanks!

@chenguoguo
Copy link
Collaborator

I haven't really played with the Go example. @brentnd could you please help?

@deadprogram
Copy link
Contributor Author

I literally just figured it out moments after I posted this! I was not copying the slice correctly.

This is the code that works:

package main

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"time"

	"github.com/brentnd/go-snowboy"
	"github.com/gordonklaus/portaudio"
)

func chkErr(err error) error {
	if err != nil {
		fmt.Errorf("%s", err)
		return err
	}
	return nil
}

type Sound struct {
	stream *portaudio.Stream
	data   []int16
}

func (s *Sound) Read(p []byte) (int, error) {
	s.stream.Read()

	buf := &bytes.Buffer{}
	for _, v := range s.data {
		binary.Write(buf, binary.LittleEndian, v)
	}

	copy(p, buf.Bytes())
	return len(p), nil
}

func main() {
	inputChannels := 1
	outputChannels := 0
	sampleRate := 16000
	framesPerBuffer := make([]int16, 1024)

	err := portaudio.Initialize()
	chkErr(err)
	defer portaudio.Terminate()

	stream, err := portaudio.OpenDefaultStream(inputChannels, outputChannels, float64(sampleRate), len(framesPerBuffer), framesPerBuffer)
	chkErr(err)
	defer stream.Close()

	d := snowboy.NewDetector("/home/ron/Development/snowboy/resources/common.res")
	defer d.Close()

	d.HandleFunc(snowboy.NewHotword("/home/ron/Development/snowboy/resources/snowboy.umdl", 0.5), func(string) {
		fmt.Println("Handle func in snowboy.NewDefaultHotword")
	})

	d.HandleSilenceFunc(500*time.Millisecond, func(string) {
		fmt.Println("silence detected")
	})

	sr, nc, bd := d.AudioFormat()
	fmt.Printf("sample rate=%d, num channels=%d, bit depth=%d\n", sr, nc, bd)

	err = stream.Start()
	chkErr(err)

	sounder := &Sound{stream, framesPerBuffer}

	err = d.ReadAndDetect(sounder)
	chkErr(err)
}
$ go run main.go 
sample rate=16000, num channels=1, bit depth=16
silence detected
silence detected
silence detected
Handle func in snowboy.NewDefaultHotword
silence detected
silence detected
silence detected
silence detected
silence detected
silence detected
Handle func in snowboy.NewDefaultHotword

Thanks again for the cool lib.

@xuchen
Copy link
Collaborator

xuchen commented Nov 20, 2017

@deadprogram Ron thanks for figuring this out. Could you please submit a PR to examples/Go so others can see your great work too?

@deadprogram
Copy link
Contributor Author

Hi @xuchen I've done so, thanks again for this cool project!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants