pcm: changed term clip to buffer

This commit is contained in:
Trek H 2019-11-13 16:54:41 +10:30
parent 3a7c2c5c5e
commit 796a3b9a97
6 changed files with 68 additions and 68 deletions

View File

@ -78,11 +78,11 @@ type audioClient struct {
parameters parameters
// internals // internals
dev *yalsa.Device // audio input device dev *yalsa.Device // audio input device
clip pcm.Clip // Clip to contain the recording. buf pcm.Buffer // Buffer to contain the recording.
rb *ring.Buffer // our buffer rb *ring.Buffer // our buffer
ns *netsender.Sender // our NetSender ns *netsender.Sender // our NetSender
vs int // our "var sum" to track var changes vs int // our "var sum" to track var changes
} }
type parameters struct { type parameters struct {
@ -141,17 +141,17 @@ func main() {
if err != nil { if err != nil {
log.Log(logger.Error, err.Error()) log.Log(logger.Error, err.Error())
} }
cf := pcm.ClipFormat{ cf := pcm.BufferFormat{
SFormat: sf, SFormat: sf,
Channels: ab.Format.Channels, Channels: ab.Format.Channels,
Rate: ab.Format.Rate, Rate: ab.Format.Rate,
} }
ac.clip = pcm.Clip{ ac.buf = pcm.Buffer{
Format: cf, Format: cf,
Data: ab.Data, 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 rbLen := rbDuration / ac.period
ac.rb = ring.NewBuffer(rbLen, recSize, rbTimeout) 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. // Re-opens the device and tries again if ASLA returns an error.
// Spends a lot of time sleeping in Paused mode. // Spends a lot of time sleeping in Paused mode.
// ToDo: Currently, reading audio and writing to the ringbuffer are synchronous. // 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() { func (ac *audioClient) input() {
for { for {
ac.mu.Lock() ac.mu.Lock()
@ -344,7 +344,7 @@ func (ac *audioClient) input() {
} }
log.Log(logger.Debug, "recording audio for period", "seconds", ac.period) log.Log(logger.Debug, "recording audio for period", "seconds", ac.period)
ac.mu.Lock() ac.mu.Lock()
err := ac.dev.Read(ac.clip.Data) err := ac.dev.Read(ac.buf.Data)
ac.mu.Unlock() ac.mu.Unlock()
if err != nil { if err != nil {
log.Log(logger.Debug, "device.Read failed", "error", err.Error()) 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. // This function also handles NetReceiver configuration requests and updating of NetReceiver vars.
func (ac *audioClient) output() { func (ac *audioClient) output() {
// Calculate the size of the output data based on wanted channels and rate. // 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) 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) 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 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. // 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 var err error
ac.mu.Lock() ac.mu.Lock()
wantChannels := ac.channels wantChannels := ac.channels
@ -533,17 +533,17 @@ func (ac *audioClient) formatBuffer() pcm.Clip {
ac.mu.Unlock() ac.mu.Unlock()
// If nothing needs to be changed, return the original. // If nothing needs to be changed, return the original.
if ac.clip.Format.Channels == wantChannels && ac.clip.Format.Rate == wantRate { if ac.buf.Format.Channels == wantChannels && ac.buf.Format.Rate == wantRate {
return ac.clip return ac.buf
} }
formatted := pcm.Clip{Format: ac.clip.Format} formatted := pcm.Buffer{Format: ac.buf.Format}
bufCopied := false bufCopied := false
if ac.clip.Format.Channels != wantChannels { if ac.buf.Format.Channels != wantChannels {
// Convert channels. // Convert channels.
if ac.clip.Format.Channels == 2 && wantChannels == 1 { if ac.buf.Format.Channels == 2 && wantChannels == 1 {
if formatted, err = pcm.StereoToMono(ac.clip); err != nil { if formatted, err = pcm.StereoToMono(ac.buf); err != nil {
log.Log(logger.Warning, "channel conversion failed, audio has remained stereo", "error", err.Error()) log.Log(logger.Warning, "channel conversion failed, audio has remained stereo", "error", err.Error())
} else { } else {
formatted.Format.Channels = 1 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. // Convert rate.
if bufCopied { if bufCopied {
formatted, err = pcm.Resample(formatted, wantRate) formatted, err = pcm.Resample(formatted, wantRate)
} else { } else {
formatted, err = pcm.Resample(ac.clip, wantRate) formatted, err = pcm.Resample(ac.buf, wantRate)
} }
if err != nil { if err != nil {
log.Log(logger.Warning, "rate conversion failed, audio has remained original rate", "error", err.Error()) log.Log(logger.Warning, "rate conversion failed, audio has remained original rate", "error", err.Error())

View File

@ -35,7 +35,7 @@ import (
"github.com/pkg/errors" "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 type SampleFormat int
// Used to represent an unknown format. // Used to represent an unknown format.
@ -52,33 +52,33 @@ const (
// https://trac.ffmpeg.org/wiki/audio%20types // https://trac.ffmpeg.org/wiki/audio%20types
) )
// ClipFormat contains the format for a PCM Clip. // BufferFormat contains the format for a PCM Buffer.
type ClipFormat struct { type BufferFormat struct {
SFormat SampleFormat SFormat SampleFormat
Rate int Rate int
Channels int Channels int
} }
// Clip contains a clip of PCM data and the format that it is in. // Buffer contains a buffer of PCM data and the format that it is in.
type Clip struct { type Buffer struct {
Format ClipFormat Format BufferFormat
Data []byte 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: // Notes:
// - Currently only downsampling is implemented and c's rate must be divisible by 'rate' or an error will occur. // - 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 // - 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. // 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 { if c.Format.Rate == rate {
return c, nil return c, nil
} }
if c.Format.Rate < 0 { 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 { 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. // The number of bytes in a sample.
@ -89,7 +89,7 @@ func Resample(c Clip, rate int) (Clip, error) {
case S16_LE: case S16_LE:
sampleLen = 2 * c.Format.Channels sampleLen = 2 * c.Format.Channels
default: 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) 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. // ratioTo = 1 is the only number that will result in an even sampling.
if ratioTo != 1 { 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 newLen := inPcmLen / ratioFrom
@ -129,9 +129,9 @@ func Resample(c Clip, rate int) (Clip, error) {
resampled = append(resampled, bAvg...) resampled = append(resampled, bAvg...)
} }
// Return a new Clip with resampled data. // Return a new Buffer with resampled data.
return Clip{ return Buffer{
Format: ClipFormat{ Format: BufferFormat{
Channels: c.Format.Channels, Channels: c.Format.Channels,
SFormat: c.Format.SFormat, SFormat: c.Format.SFormat,
Rate: rate, 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 // StereoToMono returns raw mono audio data generated from only the left channel from
// the given stereo Clip // the given stereo Buffer
func StereoToMono(c Clip) (Clip, error) { func StereoToMono(c Buffer) (Buffer, error) {
if c.Format.Channels == 1 { if c.Format.Channels == 1 {
return c, nil return c, nil
} }
if c.Format.Channels != 2 { 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 var stereoSampleBytes int
@ -157,7 +157,7 @@ func StereoToMono(c Clip) (Clip, error) {
case S16_LE: case S16_LE:
stereoSampleBytes = 4 stereoSampleBytes = 4
default: 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) recLength := len(c.Data)
@ -173,9 +173,9 @@ func StereoToMono(c Clip) (Clip, error) {
} }
} }
// Return a new Clip with resampled data. // Return a new Buffer with resampled data.
return Clip{ return Buffer{
Format: ClipFormat{ Format: BufferFormat{
Channels: 1, Channels: 1,
SFormat: c.Format.SFormat, SFormat: c.Format.SFormat,
Rate: c.Format.Rate, Rate: c.Format.Rate,

View File

@ -45,19 +45,19 @@ func TestResample(t *testing.T) {
log.Fatal(err) log.Fatal(err)
} }
format := ClipFormat{ format := BufferFormat{
Channels: 1, Channels: 1,
Rate: 48000, Rate: 48000,
SFormat: S16_LE, SFormat: S16_LE,
} }
clip := Clip{ buf := Buffer{
Format: format, Format: format,
Data: inPcm, Data: inPcm,
} }
// Resample pcm. // Resample pcm.
resampled, err := Resample(clip, 8000) resampled, err := Resample(buf, 8000)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -86,19 +86,19 @@ func TestStereoToMono(t *testing.T) {
log.Fatal(err) log.Fatal(err)
} }
format := ClipFormat{ format := BufferFormat{
Channels: 2, Channels: 2,
Rate: 44100, Rate: 44100,
SFormat: S16_LE, SFormat: S16_LE,
} }
clip := Clip{ buf := Buffer{
Format: format, Format: format,
Data: inPcm, Data: inPcm,
} }
// Convert audio. // Convert audio.
mono, err := StereoToMono(clip) mono, err := StereoToMono(buf)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }

View File

@ -68,7 +68,7 @@ type ALSA struct {
mu sync.Mutex // Provides synchronisation when changing modes concurrently. mu sync.Mutex // Provides synchronisation when changing modes concurrently.
title string // Name of audio title, or empty for the default title. title string // Name of audio title, or empty for the default title.
dev *yalsa.Device // ALSA device's Audio input device. 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. rb *ring.Buffer // Our buffer.
chunkSize int // This is the number of bytes that will be stored in rb at a time. chunkSize int // This is the number of bytes that will be stored in rb at a time.
Config // Configuration parameters for this device. 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()) d.l.Log(logger.Error, pkg+err.Error())
return err return err
} }
cf := pcm.ClipFormat{ cf := pcm.BufferFormat{
SFormat: sf, SFormat: sf,
Channels: ab.Format.Channels, Channels: ab.Format.Channels,
Rate: ab.Format.Rate, Rate: ab.Format.Rate,
} }
d.clip = pcm.Clip{ d.buf = pcm.Buffer{
Format: cf, Format: cf,
Data: ab.Data, Data: ab.Data,
} }
// Account for channel conversion. // 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. // Account for resampling.
chunkSize = (chunkSize / float64(d.dev.BufferFormat().Rate)) * float64(d.SampleRate) chunkSize = (chunkSize / float64(d.dev.BufferFormat().Rate)) * float64(d.SampleRate)
@ -386,7 +386,7 @@ 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, 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 { if err != nil {
d.l.Log(logger.Debug, pkg+"read failed", "error", err.Error()) d.l.Log(logger.Debug, pkg+"read failed", "error", err.Error())
err = d.open() // re-open 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. // 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 var err error
// If nothing needs to be changed, return the original. // If nothing needs to be changed, return the original.
if d.clip.Format.Channels == d.Channels && d.clip.Format.Rate == d.SampleRate { if d.buf.Format.Channels == d.Channels && d.buf.Format.Rate == d.SampleRate {
return d.clip return d.buf
} }
var formatted pcm.Clip var formatted pcm.Buffer
if d.clip.Format.Channels != d.Channels { if d.buf.Format.Channels != d.Channels {
// Convert channels. // Convert channels.
// TODO(Trek): Make this work for conversions other than stereo to mono. // TODO(Trek): Make this work for conversions other than stereo to mono.
if d.clip.Format.Channels == 2 && d.Channels == 1 { if d.buf.Format.Channels == 2 && d.Channels == 1 {
formatted, err = pcm.StereoToMono(d.clip) formatted, err = pcm.StereoToMono(d.buf)
if err != nil { if err != nil {
d.l.Log(logger.Fatal, pkg+"channel conversion failed", "error", err.Error()) 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. // Convert rate.
formatted, err = pcm.Resample(formatted, d.SampleRate) formatted, err = pcm.Resample(formatted, d.SampleRate)
if err != nil { if err != nil {

View File

@ -62,19 +62,19 @@ func main() {
log.Fatalf("Unhandled ALSA format: %v", SFString) log.Fatalf("Unhandled ALSA format: %v", SFString)
} }
format := pcm.ClipFormat{ format := pcm.BufferFormat{
Channels: channels, Channels: channels,
Rate: from, Rate: from,
SFormat: sf, SFormat: sf,
} }
clip := pcm.Clip{ buf := pcm.Buffer{
Format: format, Format: format,
Data: inPcm, Data: inPcm,
} }
// Resample audio. // Resample audio.
resampled, err := pcm.Resample(clip, to) resampled, err := pcm.Resample(buf, to)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }

View File

@ -59,18 +59,18 @@ func main() {
log.Fatalf("Unhandled sample format: %v", SFString) log.Fatalf("Unhandled sample format: %v", SFString)
} }
format := pcm.ClipFormat{ format := pcm.BufferFormat{
Channels: 2, Channels: 2,
SFormat: sf, SFormat: sf,
} }
clip := pcm.Clip{ buf := pcm.Buffer{
Format: format, Format: format,
Data: inPcm, Data: inPcm,
} }
// Convert audio. // Convert audio.
mono, err := pcm.StereoToMono(clip) mono, err := pcm.StereoToMono(buf)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }