mirror of https://bitbucket.org/ausocean/av.git
revid: added codec conversion after recording
This commit is contained in:
parent
90c34c4108
commit
409dcabe0a
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue