Merge branch 'master' into treatment-newmods

This commit is contained in:
Trek H 2020-08-22 10:49:45 +09:30
commit 44801e6f71
4 changed files with 55 additions and 39 deletions

View File

@ -142,6 +142,9 @@ func (d *ALSA) Setup(c config.Config) error {
errs = append(errs, errInvalidCodec) errs = append(errs, errInvalidCodec)
c.InputCodec = defaultCodec c.InputCodec = defaultCodec
} }
if errs != nil {
return errs
}
d.Config = Config{ d.Config = Config{
SampleRate: c.SampleRate, SampleRate: c.SampleRate,
Channels: c.Channels, Channels: c.Channels,
@ -153,16 +156,14 @@ func (d *ALSA) Setup(c config.Config) error {
// Open the requested audio device. // Open the requested audio device.
err := d.open() err := d.open()
if err != nil { if err != nil {
d.l.Log(logger.Error, pkg+"failed to open device") return fmt.Errorf("failed to open device: %w", err)
return err
} }
// Setup the device to record with desired period. // Setup the device to record with desired period.
ab := d.dev.NewBufferDuration(time.Duration(d.RecPeriod * float64(time.Second))) ab := d.dev.NewBufferDuration(time.Duration(d.RecPeriod * float64(time.Second)))
sf, err := pcm.SFFromString(ab.Format.SampleFormat.String()) sf, err := pcm.SFFromString(ab.Format.SampleFormat.String())
if err != nil { if err != nil {
d.l.Log(logger.Error, pkg+err.Error()) return fmt.Errorf("unable to get sample format from string: %w", err)
return err
} }
cf := pcm.BufferFormat{ cf := pcm.BufferFormat{
SFormat: sf, SFormat: sf,
@ -229,20 +230,20 @@ func (d *ALSA) Stop() error {
func (d *ALSA) open() error { func (d *ALSA) open() error {
// Close any existing device. // Close any existing device.
if d.dev != nil { if d.dev != nil {
d.l.Log(logger.Debug, pkg+"closing device", "title", d.title) d.l.Log(logger.Debug, "closing device", "title", d.title)
d.dev.Close() d.dev.Close()
d.dev = nil d.dev = nil
} }
// Open sound card and open recording device. // Open sound card and open recording device.
d.l.Log(logger.Debug, pkg+"opening sound card") d.l.Log(logger.Debug, "opening sound card")
cards, err := yalsa.OpenCards() cards, err := yalsa.OpenCards()
if err != nil { if err != nil {
return OpenError(err) return OpenError(err)
} }
defer yalsa.CloseCards(cards) defer yalsa.CloseCards(cards)
d.l.Log(logger.Debug, pkg+"finding audio device") d.l.Log(logger.Debug, "finding audio device")
for _, card := range cards { for _, card := range cards {
devices, err := card.Devices() devices, err := card.Devices()
if err != nil { if err != nil {
@ -262,19 +263,22 @@ func (d *ALSA) open() error {
return OpenError(errors.New("no ALSA device found")) return OpenError(errors.New("no ALSA device found"))
} }
d.l.Log(logger.Debug, pkg+"opening ALSA device", "title", d.dev.Title) d.l.Log(logger.Debug, "opening ALSA device", "title", d.dev.Title)
err = d.dev.Open() err = d.dev.Open()
if err != nil { if err != nil {
return OpenError(err) return OpenError(err)
} }
// 2 channels is what most devices need to record in. If mono is requested, // Try to configure device with chosen channels.
// the recording will be converted in formatBuffer(). channels, err := d.dev.NegotiateChannels(int(d.Channels))
channels, err := d.dev.NegotiateChannels(2) if err != nil && d.Channels == 1 {
if err != nil { d.l.Log(logger.Info, "device is unable to record in mono, trying stereo", "error", err)
return OpenError(err) channels, err = d.dev.NegotiateChannels(2)
} }
d.l.Log(logger.Debug, pkg+"alsa device channels set", "channels", channels) if err != nil {
return OpenError(fmt.Errorf("device is unable to record with requested number of channels: %w", err))
}
d.l.Log(logger.Debug, "alsa device channels set", "channels", channels)
// Try to negotiate a rate to record in that is divisible by the wanted rate // Try to negotiate a rate to record in that is divisible by the wanted rate
// so that it can be easily downsampled to the wanted rate. // so that it can be easily downsampled to the wanted rate.
@ -294,7 +298,7 @@ func (d *ALSA) open() error {
rate, err = d.dev.NegotiateRate(r) rate, err = d.dev.NegotiateRate(r)
if err == nil { if err == nil {
foundRate = true foundRate = true
d.l.Log(logger.Debug, pkg+"alsa device sample rate set", "rate", rate) d.l.Log(logger.Debug, "alsa device sample rate set", "rate", rate)
break break
} }
} }
@ -302,12 +306,12 @@ func (d *ALSA) open() error {
// If no easily divisible rate is found, then use the default rate. // If no easily divisible rate is found, then use the default rate.
if !foundRate { if !foundRate {
d.l.Log(logger.Warning, pkg+"Unable to sample at requested rate, default used.", "rateRequested", d.SampleRate) d.l.Log(logger.Warning, "unable to sample at requested rate, default used.", "rateRequested", d.SampleRate)
rate, err = d.dev.NegotiateRate(defaultSampleRate) rate, err = d.dev.NegotiateRate(defaultSampleRate)
if err != nil { if err != nil {
return OpenError(err) return OpenError(err)
} }
d.l.Log(logger.Debug, pkg+"alsa device sample rate set", "rate", rate) d.l.Log(logger.Debug, "alsa device sample rate set", "rate", rate)
} }
var aFmt yalsa.FormatType var aFmt yalsa.FormatType
@ -332,7 +336,7 @@ func (d *ALSA) open() error {
default: default:
return OpenError(fmt.Errorf("unsupported sample bits %v", d.BitDepth)) return OpenError(fmt.Errorf("unsupported sample bits %v", d.BitDepth))
} }
d.l.Log(logger.Debug, pkg+"alsa device bit depth set", "bitdepth", bitdepth) d.l.Log(logger.Debug, "alsa device bit depth set", "bitdepth", bitdepth)
// A 50ms period is a sensible value for low-ish latency. (this could be made configurable if needed) // A 50ms period is a sensible value for low-ish latency. (this could be made configurable if needed)
// Some devices only accept even period sizes while others want powers of 2. // Some devices only accept even period sizes while others want powers of 2.
@ -347,13 +351,13 @@ func (d *ALSA) open() error {
if err != nil { if err != nil {
return OpenError(err) return OpenError(err)
} }
d.l.Log(logger.Debug, pkg+"alsa device buffer size set", "buffersize", bufSize) d.l.Log(logger.Debug, "alsa device buffer size set", "buffersize", bufSize)
if err = d.dev.Prepare(); err != nil { if err = d.dev.Prepare(); err != nil {
return OpenError(err) return OpenError(err)
} }
d.l.Log(logger.Debug, pkg+"successfully negotiated device params") d.l.Log(logger.Debug, "successfully negotiated device params")
return nil return nil
} }
@ -371,7 +375,7 @@ func (d *ALSA) input() {
continue continue
case stopped: case stopped:
if d.dev != nil { if d.dev != nil {
d.l.Log(logger.Debug, pkg+"closing ALSA device", "title", d.title) d.l.Log(logger.Debug, "closing ALSA device", "title", d.title)
d.dev.Close() d.dev.Close()
d.dev = nil d.dev = nil
} }
@ -379,31 +383,31 @@ func (d *ALSA) input() {
} }
// Read from audio device. // Read from audio device.
d.l.Log(logger.Debug, pkg+"recording audio for period", "seconds", d.RecPeriod) d.l.Log(logger.Debug, "recording audio for period", "seconds", d.RecPeriod)
err := d.dev.Read(d.pb.Data) err := d.dev.Read(d.pb.Data)
if err != nil { if err != nil {
d.l.Log(logger.Debug, pkg+"read failed", "error", err.Error()) d.l.Log(logger.Debug, "read failed", "error", err.Error())
err = d.open() // re-open err = d.open() // re-open
if err != nil { if err != nil {
d.l.Log(logger.Fatal, pkg+"reopening device failed", "error", err.Error()) d.l.Log(logger.Fatal, "reopening device failed", "error", err.Error())
return return
} }
continue continue
} }
// Process audio. // Process audio.
d.l.Log(logger.Debug, pkg+"processing audio") d.l.Log(logger.Debug, "processing audio")
toWrite := d.formatBuffer() toWrite := d.formatBuffer()
// Write audio to ringbuffer. // Write audio to ringbuffer.
n, err := d.rb.Write(toWrite.Data) n, err := d.rb.Write(toWrite.Data)
switch err { switch err {
case nil: case nil:
d.l.Log(logger.Debug, pkg+"wrote audio to ringbuffer", "length", n) d.l.Log(logger.Debug, "wrote audio to ringbuffer", "length", n)
case ring.ErrDropped: case ring.ErrDropped:
d.l.Log(logger.Warning, pkg+"old audio data overwritten") d.l.Log(logger.Warning, "old audio data overwritten")
default: default:
d.l.Log(logger.Error, pkg+"unexpected ringbuffer error", "error", err.Error()) d.l.Log(logger.Error, "unexpected ringbuffer error", "error", err.Error())
return return
} }
} }
@ -436,7 +440,7 @@ func (d *ALSA) formatBuffer() pcm.Buffer {
if d.pb.Format.Channels == 2 && d.Channels == 1 { if d.pb.Format.Channels == 2 && d.Channels == 1 {
formatted, err = pcm.StereoToMono(d.pb) formatted, err = pcm.StereoToMono(d.pb)
if err != nil { if err != nil {
d.l.Log(logger.Fatal, pkg+"channel conversion failed", "error", err.Error()) d.l.Log(logger.Fatal, "channel conversion failed", "error", err.Error())
} }
} }
} }
@ -445,7 +449,7 @@ func (d *ALSA) formatBuffer() pcm.Buffer {
// Convert rate. // Convert rate.
formatted, err = pcm.Resample(formatted, d.SampleRate) formatted, err = pcm.Resample(formatted, d.SampleRate)
if err != nil { if err != nil {
d.l.Log(logger.Fatal, pkg+"rate conversion failed", "error", err.Error()) d.l.Log(logger.Fatal, "rate conversion failed", "error", err.Error())
} }
} }
@ -456,11 +460,11 @@ func (d *ALSA) formatBuffer() pcm.Buffer {
enc := adpcm.NewEncoder(b) enc := adpcm.NewEncoder(b)
_, err = enc.Write(formatted.Data) _, err = enc.Write(formatted.Data)
if err != nil { if err != nil {
d.l.Log(logger.Fatal, pkg+"unable to encode", "error", err.Error()) d.l.Log(logger.Fatal, "unable to encode", "error", err.Error())
} }
formatted.Data = b.Bytes() formatted.Data = b.Bytes()
default: default:
d.l.Log(logger.Error, pkg+"unhandled audio codec") d.l.Log(logger.Error, "unhandled audio codec")
} }
return formatted return formatted

View File

@ -27,6 +27,7 @@ package alsa
import ( import (
"bytes" "bytes"
"errors"
"io/ioutil" "io/ioutil"
"os" "os"
"strconv" "strconv"
@ -35,6 +36,7 @@ import (
"bitbucket.org/ausocean/av/codec/codecutil" "bitbucket.org/ausocean/av/codec/codecutil"
"bitbucket.org/ausocean/av/codec/pcm" "bitbucket.org/ausocean/av/codec/pcm"
"bitbucket.org/ausocean/av/device"
"bitbucket.org/ausocean/av/revid/config" "bitbucket.org/ausocean/av/revid/config"
"bitbucket.org/ausocean/utils/logger" "bitbucket.org/ausocean/utils/logger"
) )
@ -54,14 +56,14 @@ func TestDevice(t *testing.T) {
l := logger.New(logger.Debug, os.Stderr, true) l := logger.New(logger.Debug, os.Stderr, true)
ai := New(l) ai := New(l)
err := ai.Setup(c) err := ai.Setup(c)
// If there was an error opening the device, skip this test. // Log any config errors, otherwise if there was an error opening a device, skip
if _, ok := err.(OpenError); ok { // this test since not all testing environments will have recording devices.
var e *device.MultiError
if err != nil && errors.As(err, &e) {
t.Logf("errors from configuring device: %s", err.Error())
} else if err != nil {
t.Skip(err) t.Skip(err)
} }
// For any other error, report it.
if err != nil {
t.Error(err)
}
err = ai.Start() err = ai.Start()
if err != nil { if err != nil {
t.Error(err) t.Error(err)

5
go.sum
View File

@ -1,3 +1,7 @@
bitbucket.org/ausocean/iot v1.2.13 h1:E9LcW3HYqRgJqxNhPJUCfVRvoV2IAU4B7JSDNxB/x2k=
bitbucket.org/ausocean/iot v1.2.13/go.mod h1:Q5FwaOKnCty3dVeVtki6DLwYa5vhNpOaeu1lwLyPCg8=
bitbucket.org/ausocean/iot v1.2.14 h1:+2up1G1g28RnXFw+1tpobdpZxzZDYEXgsXwmpNZDCWA=
bitbucket.org/ausocean/iot v1.2.14/go.mod h1:Q5FwaOKnCty3dVeVtki6DLwYa5vhNpOaeu1lwLyPCg8=
bitbucket.org/ausocean/iot v1.2.16 h1:dZvENB2cLmeHvEfBULhGxnM9K10IuWFLqjcFKBJHpgw= bitbucket.org/ausocean/iot v1.2.16 h1:dZvENB2cLmeHvEfBULhGxnM9K10IuWFLqjcFKBJHpgw=
bitbucket.org/ausocean/iot v1.2.16/go.mod h1:rRcWt6SoM/jgIZpP1zrpnKb5BhxIMulAJ+q1xTvLh94= bitbucket.org/ausocean/iot v1.2.16/go.mod h1:rRcWt6SoM/jgIZpP1zrpnKb5BhxIMulAJ+q1xTvLh94=
bitbucket.org/ausocean/utils v1.2.11 h1:zA0FOaPjN960ryp8PKCkV5y50uWBYrIxCVnXjwbvPqg= bitbucket.org/ausocean/utils v1.2.11 h1:zA0FOaPjN960ryp8PKCkV5y50uWBYrIxCVnXjwbvPqg=
@ -22,6 +26,7 @@ github.com/go-audio/audio v0.0.0-20181013203223-7b2a6ca21480 h1:4sGU+UABMMsRJyD+
github.com/go-audio/audio v0.0.0-20181013203223-7b2a6ca21480/go.mod h1:6uAu0+H2lHkwdGsAY+j2wHPNPpPoeg5AaEFh9FlA+Zs= github.com/go-audio/audio v0.0.0-20181013203223-7b2a6ca21480/go.mod h1:6uAu0+H2lHkwdGsAY+j2wHPNPpPoeg5AaEFh9FlA+Zs=
github.com/go-audio/wav v0.0.0-20181013172942-de841e69b884 h1:2TaXIaVA4ff/MHHezOj83tCypALTFAcXOImcFWNa3jw= github.com/go-audio/wav v0.0.0-20181013172942-de841e69b884 h1:2TaXIaVA4ff/MHHezOj83tCypALTFAcXOImcFWNa3jw=
github.com/go-audio/wav v0.0.0-20181013172942-de841e69b884/go.mod h1:UiqzUyfX0zs3pJ/DPyvS5v8sN6s5bXPUDDIVA5v8dks= github.com/go-audio/wav v0.0.0-20181013172942-de841e69b884/go.mod h1:UiqzUyfX0zs3pJ/DPyvS5v8sN6s5bXPUDDIVA5v8dks=
github.com/go-delve/delve v1.5.0/go.mod h1:c6b3a1Gry6x8a4LGCe/CWzrocrfaHvkUxCj3k4bvSUQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/google/go-cmp v0.4.1 h1:/exdXoGamhu5ONeUJH0deniYLWYvQwW66yvlfiiKTu0= github.com/google/go-cmp v0.4.1 h1:/exdXoGamhu5ONeUJH0deniYLWYvQwW66yvlfiiKTu0=

View File

@ -25,11 +25,13 @@ LICENSE
package revid package revid
import ( import (
"errors"
"fmt" "fmt"
"strconv" "strconv"
"bitbucket.org/ausocean/av/codec/codecutil" "bitbucket.org/ausocean/av/codec/codecutil"
"bitbucket.org/ausocean/av/container/mts" "bitbucket.org/ausocean/av/container/mts"
"bitbucket.org/ausocean/av/device"
"bitbucket.org/ausocean/av/device/alsa" "bitbucket.org/ausocean/av/device/alsa"
"bitbucket.org/ausocean/utils/logger" "bitbucket.org/ausocean/utils/logger"
) )
@ -42,8 +44,11 @@ func (r *Revid) setupAudio() error {
// Configure ALSA device. // Configure ALSA device.
r.cfg.Logger.Log(logger.Debug, "configuring input device") r.cfg.Logger.Log(logger.Debug, "configuring input device")
err := d.Setup(r.cfg) err := d.Setup(r.cfg)
if err != nil { var e *device.MultiError
if err != nil && errors.As(err, &e) {
r.cfg.Logger.Log(logger.Warning, "errors from configuring input device", "errors", err) r.cfg.Logger.Log(logger.Warning, "errors from configuring input device", "errors", err)
} else if err != nil {
return err
} }
r.cfg.Logger.Log(logger.Info, "input device configured") r.cfg.Logger.Log(logger.Info, "input device configured")