mirror of https://bitbucket.org/ausocean/av.git
pcm: changed term clip to buffer
This commit is contained in:
parent
3a7c2c5c5e
commit
796a3b9a97
|
@ -78,11 +78,11 @@ type audioClient struct {
|
|||
parameters
|
||||
|
||||
// internals
|
||||
dev *yalsa.Device // audio input device
|
||||
clip pcm.Clip // Clip to contain the recording.
|
||||
rb *ring.Buffer // our buffer
|
||||
ns *netsender.Sender // our NetSender
|
||||
vs int // our "var sum" to track var changes
|
||||
dev *yalsa.Device // audio input device
|
||||
buf pcm.Buffer // Buffer to contain the recording.
|
||||
rb *ring.Buffer // our buffer
|
||||
ns *netsender.Sender // our NetSender
|
||||
vs int // our "var sum" to track var changes
|
||||
}
|
||||
|
||||
type parameters struct {
|
||||
|
@ -141,17 +141,17 @@ func main() {
|
|||
if err != nil {
|
||||
log.Log(logger.Error, err.Error())
|
||||
}
|
||||
cf := pcm.ClipFormat{
|
||||
cf := pcm.BufferFormat{
|
||||
SFormat: sf,
|
||||
Channels: ab.Format.Channels,
|
||||
Rate: ab.Format.Rate,
|
||||
}
|
||||
ac.clip = pcm.Clip{
|
||||
ac.buf = pcm.Buffer{
|
||||
Format: cf,
|
||||
Data: ab.Data,
|
||||
}
|
||||
|
||||
recSize := (((len(ac.clip.Data) / ac.dev.BufferFormat().Channels) * ac.channels) / ac.dev.BufferFormat().Rate) * ac.rate
|
||||
recSize := (((len(ac.buf.Data) / ac.dev.BufferFormat().Channels) * ac.channels) / ac.dev.BufferFormat().Rate) * ac.rate
|
||||
rbLen := rbDuration / ac.period
|
||||
ac.rb = ring.NewBuffer(rbLen, recSize, rbTimeout)
|
||||
|
||||
|
@ -332,7 +332,7 @@ func (ac *audioClient) open() error {
|
|||
// Re-opens the device and tries again if ASLA returns an error.
|
||||
// Spends a lot of time sleeping in Paused mode.
|
||||
// ToDo: Currently, reading audio and writing to the ringbuffer are synchronous.
|
||||
// Need a way to asynchronously read from the clip, i.e., _while_ it is recording to avoid any gaps.
|
||||
// Need a way to asynchronously read from the buf, i.e., _while_ it is recording to avoid any gaps.
|
||||
func (ac *audioClient) input() {
|
||||
for {
|
||||
ac.mu.Lock()
|
||||
|
@ -344,7 +344,7 @@ func (ac *audioClient) input() {
|
|||
}
|
||||
log.Log(logger.Debug, "recording audio for period", "seconds", ac.period)
|
||||
ac.mu.Lock()
|
||||
err := ac.dev.Read(ac.clip.Data)
|
||||
err := ac.dev.Read(ac.buf.Data)
|
||||
ac.mu.Unlock()
|
||||
if err != nil {
|
||||
log.Log(logger.Debug, "device.Read failed", "error", err.Error())
|
||||
|
@ -386,7 +386,7 @@ func (ac *audioClient) input() {
|
|||
// This function also handles NetReceiver configuration requests and updating of NetReceiver vars.
|
||||
func (ac *audioClient) output() {
|
||||
// Calculate the size of the output data based on wanted channels and rate.
|
||||
outLen := (((len(ac.clip.Data) / ac.clip.Format.Channels) * ac.channels) / ac.clip.Format.Rate) * ac.rate
|
||||
outLen := (((len(ac.buf.Data) / ac.buf.Format.Channels) * ac.channels) / ac.buf.Format.Rate) * ac.rate
|
||||
buf := make([]byte, outLen)
|
||||
|
||||
mime := "audio/x-wav;codec=pcm;rate=" + strconv.Itoa(ac.rate) + ";channels=" + strconv.Itoa(ac.channels) + ";bits=" + strconv.Itoa(ac.bits)
|
||||
|
@ -523,9 +523,9 @@ func read(rb *ring.Buffer, buf []byte) (int, error) {
|
|||
return n, nil
|
||||
}
|
||||
|
||||
// formatBuffer returns a Clip that has the recording data from the ac's original Clip but stored
|
||||
// formatBuffer returns a Buffer that has the recording data from the ac's original Buffer but stored
|
||||
// in the desired format specified by the ac's parameters.
|
||||
func (ac *audioClient) formatBuffer() pcm.Clip {
|
||||
func (ac *audioClient) formatBuffer() pcm.Buffer {
|
||||
var err error
|
||||
ac.mu.Lock()
|
||||
wantChannels := ac.channels
|
||||
|
@ -533,17 +533,17 @@ func (ac *audioClient) formatBuffer() pcm.Clip {
|
|||
ac.mu.Unlock()
|
||||
|
||||
// If nothing needs to be changed, return the original.
|
||||
if ac.clip.Format.Channels == wantChannels && ac.clip.Format.Rate == wantRate {
|
||||
return ac.clip
|
||||
if ac.buf.Format.Channels == wantChannels && ac.buf.Format.Rate == wantRate {
|
||||
return ac.buf
|
||||
}
|
||||
|
||||
formatted := pcm.Clip{Format: ac.clip.Format}
|
||||
formatted := pcm.Buffer{Format: ac.buf.Format}
|
||||
bufCopied := false
|
||||
if ac.clip.Format.Channels != wantChannels {
|
||||
if ac.buf.Format.Channels != wantChannels {
|
||||
|
||||
// Convert channels.
|
||||
if ac.clip.Format.Channels == 2 && wantChannels == 1 {
|
||||
if formatted, err = pcm.StereoToMono(ac.clip); err != nil {
|
||||
if ac.buf.Format.Channels == 2 && wantChannels == 1 {
|
||||
if formatted, err = pcm.StereoToMono(ac.buf); err != nil {
|
||||
log.Log(logger.Warning, "channel conversion failed, audio has remained stereo", "error", err.Error())
|
||||
} else {
|
||||
formatted.Format.Channels = 1
|
||||
|
@ -552,13 +552,13 @@ func (ac *audioClient) formatBuffer() pcm.Clip {
|
|||
}
|
||||
}
|
||||
|
||||
if ac.clip.Format.Rate != wantRate {
|
||||
if ac.buf.Format.Rate != wantRate {
|
||||
|
||||
// Convert rate.
|
||||
if bufCopied {
|
||||
formatted, err = pcm.Resample(formatted, wantRate)
|
||||
} else {
|
||||
formatted, err = pcm.Resample(ac.clip, wantRate)
|
||||
formatted, err = pcm.Resample(ac.buf, wantRate)
|
||||
}
|
||||
if err != nil {
|
||||
log.Log(logger.Warning, "rate conversion failed, audio has remained original rate", "error", err.Error())
|
||||
|
|
|
@ -35,7 +35,7 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// SampleFormat is the format that a PCM Clip's samples can be in.
|
||||
// SampleFormat is the format that a PCM Buffer's samples can be in.
|
||||
type SampleFormat int
|
||||
|
||||
// Used to represent an unknown format.
|
||||
|
@ -52,33 +52,33 @@ const (
|
|||
// https://trac.ffmpeg.org/wiki/audio%20types
|
||||
)
|
||||
|
||||
// ClipFormat contains the format for a PCM Clip.
|
||||
type ClipFormat struct {
|
||||
// BufferFormat contains the format for a PCM Buffer.
|
||||
type BufferFormat struct {
|
||||
SFormat SampleFormat
|
||||
Rate int
|
||||
Channels int
|
||||
}
|
||||
|
||||
// Clip contains a clip of PCM data and the format that it is in.
|
||||
type Clip struct {
|
||||
Format ClipFormat
|
||||
// Buffer contains a buffer of PCM data and the format that it is in.
|
||||
type Buffer struct {
|
||||
Format BufferFormat
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// Resample takes Clip c and resamples the pcm audio data to 'rate' Hz and returns a Clip with the resampled data.
|
||||
// Resample takes Buffer c and resamples the pcm audio data to 'rate' Hz and returns a Buffer with the resampled data.
|
||||
// Notes:
|
||||
// - Currently only downsampling is implemented and c's rate must be divisible by 'rate' or an error will occur.
|
||||
// - If the number of bytes in c.Data is not divisible by the decimation factor (ratioFrom), the remaining bytes will
|
||||
// not be included in the result. Eg. input of length 480002 downsampling 6:1 will result in output length 80000.
|
||||
func Resample(c Clip, rate int) (Clip, error) {
|
||||
func Resample(c Buffer, rate int) (Buffer, error) {
|
||||
if c.Format.Rate == rate {
|
||||
return c, nil
|
||||
}
|
||||
if c.Format.Rate < 0 {
|
||||
return Clip{}, fmt.Errorf("Unable to convert from: %v Hz", c.Format.Rate)
|
||||
return Buffer{}, fmt.Errorf("Unable to convert from: %v Hz", c.Format.Rate)
|
||||
}
|
||||
if rate < 0 {
|
||||
return Clip{}, fmt.Errorf("Unable to convert to: %v Hz", rate)
|
||||
return Buffer{}, fmt.Errorf("Unable to convert to: %v Hz", rate)
|
||||
}
|
||||
|
||||
// The number of bytes in a sample.
|
||||
|
@ -89,7 +89,7 @@ func Resample(c Clip, rate int) (Clip, error) {
|
|||
case S16_LE:
|
||||
sampleLen = 2 * c.Format.Channels
|
||||
default:
|
||||
return Clip{}, fmt.Errorf("Unhandled ALSA format: %v", c.Format.SFormat)
|
||||
return Buffer{}, fmt.Errorf("Unhandled ALSA format: %v", c.Format.SFormat)
|
||||
}
|
||||
inPcmLen := len(c.Data)
|
||||
|
||||
|
@ -100,7 +100,7 @@ func Resample(c Clip, rate int) (Clip, error) {
|
|||
|
||||
// ratioTo = 1 is the only number that will result in an even sampling.
|
||||
if ratioTo != 1 {
|
||||
return Clip{}, fmt.Errorf("unhandled from:to rate ratio %v:%v: 'to' must be 1", ratioFrom, ratioTo)
|
||||
return Buffer{}, fmt.Errorf("unhandled from:to rate ratio %v:%v: 'to' must be 1", ratioFrom, ratioTo)
|
||||
}
|
||||
|
||||
newLen := inPcmLen / ratioFrom
|
||||
|
@ -129,9 +129,9 @@ func Resample(c Clip, rate int) (Clip, error) {
|
|||
resampled = append(resampled, bAvg...)
|
||||
}
|
||||
|
||||
// Return a new Clip with resampled data.
|
||||
return Clip{
|
||||
Format: ClipFormat{
|
||||
// Return a new Buffer with resampled data.
|
||||
return Buffer{
|
||||
Format: BufferFormat{
|
||||
Channels: c.Format.Channels,
|
||||
SFormat: c.Format.SFormat,
|
||||
Rate: rate,
|
||||
|
@ -141,13 +141,13 @@ func Resample(c Clip, rate int) (Clip, error) {
|
|||
}
|
||||
|
||||
// StereoToMono returns raw mono audio data generated from only the left channel from
|
||||
// the given stereo Clip
|
||||
func StereoToMono(c Clip) (Clip, error) {
|
||||
// the given stereo Buffer
|
||||
func StereoToMono(c Buffer) (Buffer, error) {
|
||||
if c.Format.Channels == 1 {
|
||||
return c, nil
|
||||
}
|
||||
if c.Format.Channels != 2 {
|
||||
return Clip{}, fmt.Errorf("Audio is not stereo or mono, it has %v channels", c.Format.Channels)
|
||||
return Buffer{}, fmt.Errorf("Audio is not stereo or mono, it has %v channels", c.Format.Channels)
|
||||
}
|
||||
|
||||
var stereoSampleBytes int
|
||||
|
@ -157,7 +157,7 @@ func StereoToMono(c Clip) (Clip, error) {
|
|||
case S16_LE:
|
||||
stereoSampleBytes = 4
|
||||
default:
|
||||
return Clip{}, fmt.Errorf("Unhandled sample format %v", c.Format.SFormat)
|
||||
return Buffer{}, fmt.Errorf("Unhandled sample format %v", c.Format.SFormat)
|
||||
}
|
||||
|
||||
recLength := len(c.Data)
|
||||
|
@ -173,9 +173,9 @@ func StereoToMono(c Clip) (Clip, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// Return a new Clip with resampled data.
|
||||
return Clip{
|
||||
Format: ClipFormat{
|
||||
// Return a new Buffer with resampled data.
|
||||
return Buffer{
|
||||
Format: BufferFormat{
|
||||
Channels: 1,
|
||||
SFormat: c.Format.SFormat,
|
||||
Rate: c.Format.Rate,
|
||||
|
|
|
@ -45,19 +45,19 @@ func TestResample(t *testing.T) {
|
|||
log.Fatal(err)
|
||||
}
|
||||
|
||||
format := ClipFormat{
|
||||
format := BufferFormat{
|
||||
Channels: 1,
|
||||
Rate: 48000,
|
||||
SFormat: S16_LE,
|
||||
}
|
||||
|
||||
clip := Clip{
|
||||
buf := Buffer{
|
||||
Format: format,
|
||||
Data: inPcm,
|
||||
}
|
||||
|
||||
// Resample pcm.
|
||||
resampled, err := Resample(clip, 8000)
|
||||
resampled, err := Resample(buf, 8000)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@ -86,19 +86,19 @@ func TestStereoToMono(t *testing.T) {
|
|||
log.Fatal(err)
|
||||
}
|
||||
|
||||
format := ClipFormat{
|
||||
format := BufferFormat{
|
||||
Channels: 2,
|
||||
Rate: 44100,
|
||||
SFormat: S16_LE,
|
||||
}
|
||||
|
||||
clip := Clip{
|
||||
buf := Buffer{
|
||||
Format: format,
|
||||
Data: inPcm,
|
||||
}
|
||||
|
||||
// Convert audio.
|
||||
mono, err := StereoToMono(clip)
|
||||
mono, err := StereoToMono(buf)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ type ALSA struct {
|
|||
mu sync.Mutex // Provides synchronisation when changing modes concurrently.
|
||||
title string // Name of audio title, or empty for the default title.
|
||||
dev *yalsa.Device // ALSA device's Audio input device.
|
||||
clip pcm.Clip // Clip to contain the recording.
|
||||
buf pcm.Buffer // Buffer to contain the recording.
|
||||
rb *ring.Buffer // Our buffer.
|
||||
chunkSize int // This is the number of bytes that will be stored in rb at a time.
|
||||
Config // Configuration parameters for this device.
|
||||
|
@ -139,18 +139,18 @@ func (d *ALSA) Set(c config.Config) error {
|
|||
d.l.Log(logger.Error, pkg+err.Error())
|
||||
return err
|
||||
}
|
||||
cf := pcm.ClipFormat{
|
||||
cf := pcm.BufferFormat{
|
||||
SFormat: sf,
|
||||
Channels: ab.Format.Channels,
|
||||
Rate: ab.Format.Rate,
|
||||
}
|
||||
d.clip = pcm.Clip{
|
||||
d.buf = pcm.Buffer{
|
||||
Format: cf,
|
||||
Data: ab.Data,
|
||||
}
|
||||
|
||||
// Account for channel conversion.
|
||||
chunkSize := float64(len(d.clip.Data) / d.dev.BufferFormat().Channels * d.Channels)
|
||||
chunkSize := float64(len(d.buf.Data) / d.dev.BufferFormat().Channels * d.Channels)
|
||||
|
||||
// Account for resampling.
|
||||
chunkSize = (chunkSize / float64(d.dev.BufferFormat().Rate)) * float64(d.SampleRate)
|
||||
|
@ -386,7 +386,7 @@ func (d *ALSA) input() {
|
|||
|
||||
// Read from audio device.
|
||||
d.l.Log(logger.Debug, pkg+"recording audio for period", "seconds", d.RecPeriod)
|
||||
err := d.dev.Read(d.clip.Data)
|
||||
err := d.dev.Read(d.buf.Data)
|
||||
if err != nil {
|
||||
d.l.Log(logger.Debug, pkg+"read failed", "error", err.Error())
|
||||
err = d.open() // re-open
|
||||
|
@ -428,26 +428,26 @@ func (d *ALSA) Read(p []byte) (int, error) {
|
|||
}
|
||||
|
||||
// formatBuffer returns audio that has been converted to the desired format.
|
||||
func (d *ALSA) formatBuffer() pcm.Clip {
|
||||
func (d *ALSA) formatBuffer() pcm.Buffer {
|
||||
var err error
|
||||
|
||||
// If nothing needs to be changed, return the original.
|
||||
if d.clip.Format.Channels == d.Channels && d.clip.Format.Rate == d.SampleRate {
|
||||
return d.clip
|
||||
if d.buf.Format.Channels == d.Channels && d.buf.Format.Rate == d.SampleRate {
|
||||
return d.buf
|
||||
}
|
||||
var formatted pcm.Clip
|
||||
if d.clip.Format.Channels != d.Channels {
|
||||
var formatted pcm.Buffer
|
||||
if d.buf.Format.Channels != d.Channels {
|
||||
// Convert channels.
|
||||
// TODO(Trek): Make this work for conversions other than stereo to mono.
|
||||
if d.clip.Format.Channels == 2 && d.Channels == 1 {
|
||||
formatted, err = pcm.StereoToMono(d.clip)
|
||||
if d.buf.Format.Channels == 2 && d.Channels == 1 {
|
||||
formatted, err = pcm.StereoToMono(d.buf)
|
||||
if err != nil {
|
||||
d.l.Log(logger.Fatal, pkg+"channel conversion failed", "error", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if d.clip.Format.Rate != d.SampleRate {
|
||||
if d.buf.Format.Rate != d.SampleRate {
|
||||
// Convert rate.
|
||||
formatted, err = pcm.Resample(formatted, d.SampleRate)
|
||||
if err != nil {
|
||||
|
|
|
@ -62,19 +62,19 @@ func main() {
|
|||
log.Fatalf("Unhandled ALSA format: %v", SFString)
|
||||
}
|
||||
|
||||
format := pcm.ClipFormat{
|
||||
format := pcm.BufferFormat{
|
||||
Channels: channels,
|
||||
Rate: from,
|
||||
SFormat: sf,
|
||||
}
|
||||
|
||||
clip := pcm.Clip{
|
||||
buf := pcm.Buffer{
|
||||
Format: format,
|
||||
Data: inPcm,
|
||||
}
|
||||
|
||||
// Resample audio.
|
||||
resampled, err := pcm.Resample(clip, to)
|
||||
resampled, err := pcm.Resample(buf, to)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -59,18 +59,18 @@ func main() {
|
|||
log.Fatalf("Unhandled sample format: %v", SFString)
|
||||
}
|
||||
|
||||
format := pcm.ClipFormat{
|
||||
format := pcm.BufferFormat{
|
||||
Channels: 2,
|
||||
SFormat: sf,
|
||||
}
|
||||
|
||||
clip := pcm.Clip{
|
||||
buf := pcm.Buffer{
|
||||
Format: format,
|
||||
Data: inPcm,
|
||||
}
|
||||
|
||||
// Convert audio.
|
||||
mono, err := pcm.StereoToMono(clip)
|
||||
mono, err := pcm.StereoToMono(buf)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue