revid: added codec conversion after recording

This commit is contained in:
Trek H 2019-06-04 02:31:35 +09:30
parent 90c34c4108
commit 409dcabe0a
3 changed files with 53 additions and 25 deletions

31
codec/codecutil/lex.go Normal file
View File

@ -0,0 +1,31 @@
package codecutil
import (
"io"
"time"
)
// LexBytes reads n bytes from src and writes them to dst every t seconds.
func LexBytes(dst io.Writer, src io.Reader, t time.Duration, n int) error {
var tick <-chan time.Time
if t == 0 {
tick = make(chan time.Time)
} else {
ticker := time.NewTicker(t)
defer ticker.Stop()
tick = ticker.C
}
for {
<-tick
buf := make([]byte, n)
_, err := src.Read(buf)
if err != nil {
return err
}
_, err = dst.Write(buf)
if err != nil {
return err
}
}
}

View File

@ -25,6 +25,7 @@ LICENSE
package revid
import (
"bytes"
"errors"
"fmt"
"io"
@ -33,6 +34,7 @@ import (
"github.com/yobert/alsa"
"bitbucket.org/ausocean/av/codec/adpcm"
"bitbucket.org/ausocean/av/codec/pcm"
"bitbucket.org/ausocean/iot/pi/smartlogger"
"bitbucket.org/ausocean/utils/logger"
@ -118,7 +120,11 @@ func NewAudioDevice(cfg *AudioConfig) (*AudioDevice, error) {
a.l.Log(logger.Error, "given AudioConfig parameters are too small", "error", err.Error())
return nil, errors.New("given AudioConfig parameters are too small")
}
a.chunkSize = int(cs)
if a.Codec == ADPCM {
a.chunkSize = adpcm.EncBytes(int(cs))
} else {
a.chunkSize = int(cs)
}
a.rb = ring.NewBuffer(rbLen, a.chunkSize, rbTimeout)
a.mode = paused
@ -349,8 +355,7 @@ func (a *AudioDevice) Read(p []byte) (n int, err error) {
return n, nil
}
// formatBuffer returns an ALSA buffer that has the recording data from the ac's original ALSA buffer but stored
// in the desired format specified by the ac's parameters.
// formatBuffer returns audio that has been converted to the desired format.
func (a *AudioDevice) formatBuffer() alsa.Buffer {
var err error
@ -359,42 +364,36 @@ func (a *AudioDevice) formatBuffer() alsa.Buffer {
return a.ab
}
formatted := alsa.Buffer{Format: a.ab.Format}
bufCopied := false
formatted := alsa.Buffer{Format: a.ab.Format, Data: a.ab.Data}
if a.ab.Format.Channels != a.Channels {
// Convert channels.
// TODO(Trek): Make this work for conversions other than stereo to mono.
if a.ab.Format.Channels == 2 && a.Channels == 1 {
formatted.Data, err = pcm.StereoToMono(a.ab)
if err != nil {
a.l.Log(logger.Warning, "channel conversion failed, audio has remained stereo", "error", err.Error())
} else {
formatted.Format.Channels = 1
a.l.Log(logger.Fatal, "channel conversion failed", "error", err.Error())
}
bufCopied = true
}
}
if a.ab.Format.Rate != a.SampleRate {
// Convert rate.
if bufCopied {
formatted.Data, err = pcm.Resample(formatted, a.SampleRate)
} else {
formatted.Data, err = pcm.Resample(a.ab, a.SampleRate)
}
formatted.Data, err = pcm.Resample(formatted, a.SampleRate)
if err != nil {
a.l.Log(logger.Warning, "rate conversion failed, audio has remained original rate", "error", err.Error())
} else {
formatted.Format.Rate = a.SampleRate
a.l.Log(logger.Fatal, "rate conversion failed", "error", err.Error())
}
}
switch a.Codec {
case PCM:
case ADPCM:
// TODO(Trek):Add ADPCM conversion.
b := bytes.NewBuffer(make([]byte, 0, adpcm.EncBytes(len(formatted.Data))))
enc := adpcm.NewEncoder(b)
_, err = enc.Write(formatted.Data)
if err != nil {
a.l.Log(logger.Fatal, "unable to encode", "error", err.Error())
}
formatted.Data = b.Bytes()
default:
a.l.Log(logger.Error, "codec conversion failed, audio has remained original codec", "error", err.Error())
}

View File

@ -3,7 +3,6 @@ package revid
import (
"bytes"
"errors"
"io/ioutil"
"testing"
"time"
@ -92,10 +91,11 @@ func TestAudio(t *testing.T) {
ac := &AudioConfig{
SampleRate: 8000,
Channels: 1,
RecPeriod: 1,
RecPeriod: 0.5,
BitDepth: 16,
Codec: ADPCM,
}
n := 2 // Number of periods to wait while recording.
// Skip if there are no suitable devices to test with.
err := checkDevice(ac)
@ -113,9 +113,7 @@ func TestAudio(t *testing.T) {
if err != nil {
t.Error(err)
}
num := 3 // How many 'ac.RecPeriod's to record.
go codecutil.LexBytes(dst, ai, time.Duration(ac.RecPeriod*float64(time.Second)), ai.ChunkSize())
time.Sleep(time.Millisecond * 1000 * time.Duration(num))
time.Sleep(time.Millisecond * 1000 * time.Duration(n))
ai.Stop()
err = ioutil.WriteFile("./testout", dst.Bytes(), 0644)
}