2019-05-08 10:51:21 +03:00
|
|
|
package revid
|
|
|
|
|
|
|
|
import (
|
2019-05-22 08:26:58 +03:00
|
|
|
"bytes"
|
|
|
|
"errors"
|
2019-06-04 05:58:40 +03:00
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"runtime"
|
2019-05-08 10:51:21 +03:00
|
|
|
"testing"
|
2019-05-22 08:26:58 +03:00
|
|
|
"time"
|
2019-05-08 10:51:21 +03:00
|
|
|
|
2019-06-03 12:05:28 +03:00
|
|
|
"bitbucket.org/ausocean/av/codec/codecutil"
|
2019-05-21 06:09:10 +03:00
|
|
|
"github.com/yobert/alsa"
|
2019-05-09 05:41:02 +03:00
|
|
|
)
|
2019-05-08 10:51:21 +03:00
|
|
|
|
2019-05-22 08:26:58 +03:00
|
|
|
// Check that a device exists with the given config parameters.
|
|
|
|
func checkDevice(ac *AudioConfig) error {
|
2019-05-21 06:09:10 +03:00
|
|
|
cards, err := alsa.OpenCards()
|
|
|
|
if err != nil {
|
2019-05-22 08:26:58 +03:00
|
|
|
return errors.New("no audio cards found")
|
2019-05-21 06:09:10 +03:00
|
|
|
}
|
|
|
|
defer alsa.CloseCards(cards)
|
|
|
|
var testDev *alsa.Device
|
|
|
|
for _, card := range cards {
|
|
|
|
devices, err := card.Devices()
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
for _, dev := range devices {
|
|
|
|
if dev.Type != alsa.PCM || !dev.Record {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
testDev = dev
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if testDev == nil {
|
2019-05-22 08:26:58 +03:00
|
|
|
return errors.New("no suitable device found")
|
|
|
|
}
|
|
|
|
err = testDev.Open()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2019-05-21 06:09:10 +03:00
|
|
|
}
|
|
|
|
_, err = testDev.NegotiateChannels(2)
|
2019-05-09 05:41:02 +03:00
|
|
|
if err != nil {
|
2019-05-22 08:26:58 +03:00
|
|
|
return err
|
2019-05-21 06:09:10 +03:00
|
|
|
}
|
|
|
|
foundRate := false
|
2019-05-22 17:49:44 +03:00
|
|
|
for i := 0; i < len(Rates) && !foundRate; i++ {
|
|
|
|
if Rates[i] < ac.SampleRate {
|
2019-05-21 06:09:10 +03:00
|
|
|
continue
|
|
|
|
}
|
2019-05-22 17:49:44 +03:00
|
|
|
if Rates[i]%ac.SampleRate == 0 {
|
|
|
|
_, err = testDev.NegotiateRate(Rates[i])
|
2019-05-21 06:09:10 +03:00
|
|
|
if err == nil {
|
|
|
|
foundRate = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !foundRate {
|
|
|
|
_, err = testDev.NegotiateRate(defaultSampleRate)
|
|
|
|
if err != nil {
|
2019-05-22 08:26:58 +03:00
|
|
|
return err
|
2019-05-21 06:09:10 +03:00
|
|
|
}
|
2019-05-09 05:41:02 +03:00
|
|
|
}
|
2019-05-22 08:26:58 +03:00
|
|
|
var aFmt alsa.FormatType
|
|
|
|
switch ac.BitDepth {
|
|
|
|
case 16:
|
|
|
|
aFmt = alsa.S16_LE
|
|
|
|
case 32:
|
|
|
|
aFmt = alsa.S32_LE
|
|
|
|
default:
|
|
|
|
return errors.New("unsupported bitdepth")
|
|
|
|
}
|
|
|
|
_, err = testDev.NegotiateFormat(aFmt)
|
2019-05-21 06:09:10 +03:00
|
|
|
if err != nil {
|
2019-05-22 08:26:58 +03:00
|
|
|
return err
|
2019-05-21 06:09:10 +03:00
|
|
|
}
|
|
|
|
_, err = testDev.NegotiateBufferSize(8192, 16384)
|
|
|
|
if err != nil {
|
2019-05-22 08:26:58 +03:00
|
|
|
return err
|
2019-05-21 06:09:10 +03:00
|
|
|
}
|
|
|
|
if err = testDev.Prepare(); err != nil {
|
2019-05-22 08:26:58 +03:00
|
|
|
return err
|
2019-05-21 06:09:10 +03:00
|
|
|
}
|
2019-05-22 08:26:58 +03:00
|
|
|
if testDev != nil {
|
|
|
|
testDev.Close()
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2019-05-21 06:09:10 +03:00
|
|
|
|
2019-06-04 05:58:40 +03:00
|
|
|
// rTestLogger implements a revid.Logger.
|
|
|
|
type rTestLogger struct{}
|
|
|
|
|
|
|
|
func (tl rTestLogger) SetLevel(level int8) {}
|
|
|
|
|
|
|
|
func (tl rTestLogger) Log(level int8, msg string, params ...interface{}) {
|
|
|
|
logLevels := [...]string{"Debug", "Info", "Warn", "Error", "", "", "Fatal"}
|
|
|
|
if level < -1 || level > 5 {
|
|
|
|
panic("Invalid log level")
|
|
|
|
}
|
|
|
|
if !silent {
|
|
|
|
fmt.Printf("%s: %s\n", logLevels[level+1], msg)
|
|
|
|
}
|
|
|
|
if level == 5 {
|
|
|
|
buf := make([]byte, 1<<16)
|
|
|
|
size := runtime.Stack(buf, true)
|
|
|
|
fmt.Printf("%s\n", string(buf[:size]))
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestAudioDevice(t *testing.T) {
|
2019-05-22 08:26:58 +03:00
|
|
|
// We want to open a device with a standard configuration.
|
|
|
|
ac := &AudioConfig{
|
|
|
|
SampleRate: 8000,
|
|
|
|
Channels: 1,
|
2019-06-03 20:01:35 +03:00
|
|
|
RecPeriod: 0.5,
|
2019-05-22 08:26:58 +03:00
|
|
|
BitDepth: 16,
|
|
|
|
Codec: ADPCM,
|
|
|
|
}
|
2019-06-03 20:01:35 +03:00
|
|
|
n := 2 // Number of periods to wait while recording.
|
2019-05-21 06:09:10 +03:00
|
|
|
|
2019-05-22 08:26:58 +03:00
|
|
|
// Skip if there are no suitable devices to test with.
|
|
|
|
err := checkDevice(ac)
|
|
|
|
if err != nil {
|
2019-06-04 05:58:40 +03:00
|
|
|
t.Skip(err)
|
2019-05-22 08:26:58 +03:00
|
|
|
}
|
2019-05-08 11:37:33 +03:00
|
|
|
|
2019-05-22 08:26:58 +03:00
|
|
|
// Create a new audioDevice, start, read/lex, and then stop it.
|
2019-06-04 05:58:40 +03:00
|
|
|
var l rTestLogger
|
|
|
|
ai, err := NewAudioDevice(ac, l)
|
2019-05-28 19:36:58 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
2019-05-22 17:49:44 +03:00
|
|
|
dst := bytes.NewBuffer(make([]byte, 0))
|
2019-05-28 19:36:58 +03:00
|
|
|
err = ai.Start()
|
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
2019-06-03 12:05:28 +03:00
|
|
|
go codecutil.LexBytes(dst, ai, time.Duration(ac.RecPeriod*float64(time.Second)), ai.ChunkSize())
|
2019-06-04 05:58:40 +03:00
|
|
|
time.Sleep(time.Second * time.Duration(ac.RecPeriod) * time.Duration(n))
|
2019-05-21 06:09:10 +03:00
|
|
|
ai.Stop()
|
2019-05-08 10:51:21 +03:00
|
|
|
}
|