kappanhang/audio-linux.go

164 lines
2.8 KiB
Go
Raw Normal View History

2020-10-20 15:23:12 +02:00
package main
import (
"bytes"
2020-10-20 16:04:35 +02:00
"errors"
"os"
2020-10-20 17:20:52 +02:00
"sync"
2020-10-20 15:23:12 +02:00
"github.com/akosmarton/papipes"
"github.com/nonoo/kappanhang/log"
)
type audioStruct struct {
source papipes.Source
sink papipes.Sink
// Send to this channel to play audio.
play chan []byte
// Read from this channel for audio.
rec chan []byte
2020-10-20 15:23:12 +02:00
2020-10-20 17:20:52 +02:00
mutex sync.Mutex
2020-10-20 15:23:12 +02:00
playBuf *bytes.Buffer
canPlay chan bool
}
var audio audioStruct
func (a *audioStruct) playLoop() {
for {
<-a.canPlay
for {
2020-10-20 17:20:52 +02:00
a.mutex.Lock()
if a.playBuf.Len() < 1920 {
a.mutex.Unlock()
break
}
d := make([]byte, 1920)
bytesToWrite, err := a.playBuf.Read(d)
a.mutex.Unlock()
if err != nil {
2020-10-20 17:20:52 +02:00
log.Error(err)
break
}
2020-10-20 17:20:52 +02:00
if bytesToWrite != len(d) {
log.Error("buffer underread")
break
2020-10-20 16:04:35 +02:00
}
2020-10-20 17:20:52 +02:00
for {
written, err := a.source.Write(d)
if err != nil {
if _, ok := err.(*os.PathError); !ok {
exit(err)
2020-10-20 17:20:52 +02:00
}
return
}
bytesToWrite -= written
if bytesToWrite == 0 {
break
}
d = d[written:]
}
2020-10-20 16:04:35 +02:00
}
}
}
func (a *audioStruct) recLoop() {
frameBuf := make([]byte, 1920)
buf := bytes.NewBuffer([]byte{})
for {
n, err := a.sink.Read(frameBuf)
if err != nil {
if _, ok := err.(*os.PathError); !ok {
exit(err)
2020-10-20 16:04:35 +02:00
}
return
}
buf.Write(frameBuf[:n])
for buf.Len() >= len(frameBuf) {
n, err = buf.Read(frameBuf)
if err != nil {
exit(err)
}
if n != len(frameBuf) {
2020-10-20 17:50:50 +02:00
exit(errors.New("audio buffer read error"))
2020-10-20 16:04:35 +02:00
}
a.rec <- frameBuf
2020-10-20 15:23:12 +02:00
}
}
}
func (a *audioStruct) loop() {
go a.playLoop()
2020-10-20 16:04:35 +02:00
go a.recLoop()
2020-10-20 15:23:12 +02:00
for {
2020-10-20 16:04:35 +02:00
d := <-a.play
2020-10-20 17:20:52 +02:00
a.mutex.Lock()
2020-10-20 16:04:35 +02:00
a.playBuf.Write(d)
2020-10-20 17:20:52 +02:00
a.mutex.Unlock()
2020-10-20 15:23:12 +02:00
2020-10-20 16:04:35 +02:00
select {
case a.canPlay <- true:
default:
2020-10-20 15:23:12 +02:00
}
}
}
func (a *audioStruct) init(devName string) {
2020-10-20 15:23:12 +02:00
a.source.Name = "kappanhang"
a.source.Filename = "/tmp/kappanhang.source"
a.source.Rate = 48000
a.source.Format = "s16le"
a.source.Channels = 1
2020-10-20 17:10:00 +02:00
a.source.SetProperty("device.buffering.buffer_size", (48000*16)/10) // 100 ms
a.source.SetProperty("device.description", "kappanhang: "+devName)
2020-10-20 15:23:12 +02:00
a.sink.Name = "kappanhang"
a.sink.Filename = "/tmp/kappanhang.sink"
a.sink.Rate = 48000
a.sink.Format = "s16le"
a.sink.Channels = 1
2020-10-20 17:10:00 +02:00
a.sink.SetProperty("device.buffering.buffer_size", (48000*16)/10)
a.sink.SetProperty("device.description", "kappanhang: "+devName)
2020-10-20 15:23:12 +02:00
if err := a.source.Open(); err != nil {
exit(err)
}
if err := a.sink.Open(); err != nil {
exit(err)
}
a.playBuf = bytes.NewBuffer([]byte{})
a.play = make(chan []byte)
a.canPlay = make(chan bool)
a.rec = make(chan []byte)
go a.loop()
}
func (a *audioStruct) deinit() {
if a.source.IsOpen() {
if err := a.source.Close(); err != nil {
2020-10-20 16:04:35 +02:00
if _, ok := err.(*os.PathError); !ok {
log.Error(err)
}
2020-10-20 15:23:12 +02:00
}
}
if a.sink.IsOpen() {
if err := a.sink.Close(); err != nil {
2020-10-20 16:04:35 +02:00
if _, ok := err.(*os.PathError); !ok {
log.Error(err)
}
2020-10-20 15:23:12 +02:00
}
}
}