Skip to content

Commit

Permalink
Use non-interleaved audio buffers.
Browse files Browse the repository at this point in the history
  • Loading branch information
egonelbre committed Feb 11, 2017
1 parent 4070541 commit 8c77c79
Show file tree
Hide file tree
Showing 10 changed files with 390 additions and 185 deletions.
56 changes: 33 additions & 23 deletions audio/buffer32.go
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -3,51 +3,61 @@ package audio
import "time" import "time"


type BufferF32 struct { type BufferF32 struct {
Format Format format Format
Data []float32 offset uint32
frames uint32
stride uint32
data []float32
} }


func NewBufferF32(format Format, duration time.Duration) *BufferF32 { func NewBufferF32(format Format, duration time.Duration) *BufferF32 {
return NewBufferF32Frames(format, format.FrameCount(duration)) return NewBufferF32Frames(format, format.FrameCount(duration))
} }


func NewBufferF32Frames(format Format, frames int) *BufferF32 { func NewBufferF32Frames(format Format, frames int) *BufferF32 {
n := format.ChannelCount * frames samples := format.ChannelCount * frames
return &BufferF32{ return &BufferF32{
Format: format, format: format,
Data: make([]float32, n, n), offset: 0,
stride: uint32(frames),
frames: uint32(frames),
data: make([]float32, samples, samples),
} }
} }


func (b *BufferF32) SampleRate() int { return b.Format.SampleRate } func (b *BufferF32) Channel(index int) []float32 {
func (b *BufferF32) ChannelCount() int { return b.Format.ChannelCount } start := int(b.offset) + index*int(b.stride)
return b.data[start : start+int(b.frames)]
}


func (b *BufferF32) Empty() bool { return len(b.Data) == 0 } func (b *BufferF32) SampleRate() int { return b.format.SampleRate }
func (b *BufferF32) ChannelCount() int { return b.format.ChannelCount }


func (b *BufferF32) FrameCount() int { func (b *BufferF32) Empty() bool { return b.frames == 0 }
return len(b.Data) / b.ChannelCount() func (b *BufferF32) FrameCount() int { return int(b.frames) }
}


func (b *BufferF32) Duration() time.Duration { func (b *BufferF32) Duration() time.Duration {
return time.Duration(int(time.Second) * b.FrameCount() / b.SampleRate()) return time.Duration(int(time.Second) * b.FrameCount() / b.SampleRate())
} }


func (b *BufferF32) ShallowCopy() Buffer { func (b *BufferF32) ShallowCopy() Buffer {
return &BufferF32{ x := *b
Format: b.Format, return &x
Data: b.Data,
}
} }


func (b *BufferF32) DeepCopy() Buffer { func (b *BufferF32) DeepCopy() Buffer {
x := &BufferF32{ x := *b
Format: b.Format, x.data = make([]float32, len(b.data), len(b.data))
Data: make([]float32, len(b.Data), len(b.Data)), copy(x.data, b.data)
} return &x
copy(x.Data, b.Data) }
return x
func (b *BufferF32) Slice(low, high int) {
b.offset += uint32(low)
b.frames = uint32(high - low)
} }


func (b *BufferF32) Slice(lowFrame, highFrame int) { func (b *BufferF32) CutLeading(low int) {
b.Data = b.Data[lowFrame*b.ChannelCount() : highFrame*b.ChannelCount()] b.offset += uint32(low)
b.frames -= uint32(low)
} }
54 changes: 32 additions & 22 deletions audio/buffer64.go
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ package audio
import "time" import "time"


type BufferF64 struct { type BufferF64 struct {
Format Format format Format
Data []float64 offset uint32
frames uint32
stride uint32
data []float64
} }


func NewBufferF64(format Format, duration time.Duration) *BufferF64 { func NewBufferF64(format Format, duration time.Duration) *BufferF64 {
Expand All @@ -14,40 +17,47 @@ func NewBufferF64(format Format, duration time.Duration) *BufferF64 {
func NewBufferF64Frames(format Format, frames int) *BufferF64 { func NewBufferF64Frames(format Format, frames int) *BufferF64 {
n := format.ChannelCount * frames n := format.ChannelCount * frames
return &BufferF64{ return &BufferF64{
Format: format, format: format,
Data: make([]float64, n, n), offset: 0,
stride: uint32(frames),
frames: uint32(frames),
data: make([]float64, n, n),
} }
} }


func (b *BufferF64) SampleRate() int { return b.Format.SampleRate } func (b *BufferF64) Channel(index int) []float64 {
func (b *BufferF64) ChannelCount() int { return b.Format.ChannelCount } start := int(b.offset) + index*int(b.stride)
return b.data[start : start+int(b.frames)]
}


func (b *BufferF64) Empty() bool { return len(b.Data) == 0 } func (b *BufferF64) SampleRate() int { return b.format.SampleRate }
func (b *BufferF64) ChannelCount() int { return b.format.ChannelCount }


func (b *BufferF64) FrameCount() int { func (b *BufferF64) Empty() bool { return b.frames == 0 }
return len(b.Data) / b.ChannelCount() func (b *BufferF64) FrameCount() int { return int(b.frames) }
}


func (b *BufferF64) Duration() time.Duration { func (b *BufferF64) Duration() time.Duration {
return time.Duration(int(time.Second) * b.FrameCount() / b.SampleRate()) return time.Duration(int(time.Second) * b.FrameCount() / b.SampleRate())
} }


func (b *BufferF64) ShallowCopy() Buffer { func (b *BufferF64) ShallowCopy() Buffer {
return &BufferF64{ x := *b
Format: b.Format, return &x
Data: b.Data,
}
} }


func (b *BufferF64) DeepCopy() Buffer { func (b *BufferF64) DeepCopy() Buffer {
x := &BufferF64{ x := *b
Format: b.Format, x.data = make([]float64, len(b.data), len(b.data))
Data: make([]float64, len(b.Data), len(b.Data)), copy(x.data, b.data)
} return &x
copy(x.Data, b.Data) }
return x
func (b *BufferF64) Slice(low, high int) {
b.offset += uint32(low)
b.frames = uint32(high - low)
} }


func (b *BufferF64) Slice(lowFrame, highFrame int) { func (b *BufferF64) CutLeading(low int) {
b.Data = b.Data[lowFrame*b.ChannelCount() : highFrame*b.ChannelCount()] b.offset += uint32(low)
b.frames -= uint32(low)
} }
36 changes: 22 additions & 14 deletions audio/example/internal/effect/gain.go
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package effect
import ( import (
"github.com/egonelbre/exp/audio" "github.com/egonelbre/exp/audio"
"github.com/egonelbre/exp/audio/example/internal/atomic2" "github.com/egonelbre/exp/audio/example/internal/atomic2"
"github.com/egonelbre/exp/audio/slice"
) )


type Gain struct { type Gain struct {
Expand All @@ -20,43 +21,50 @@ func (gain *Gain) Process(buf audio.Buffer) error {
target := gain.Value.Get() target := gain.Value.Get()
current := gain.current current := gain.current


channelCount := buf.ChannelCount()
if target == current { if target == current {
switch buf := buf.(type) { switch buf := buf.(type) {
case *audio.BufferF32: case *audio.BufferF32:
current := float32(current) for k := 0; k < channelCount; k++ {
for i, v := range buf.Data { slice.Scale32(buf.Channel(k), float32(current))
buf.Data[i] = v * current
} }
case *audio.BufferF64: case *audio.BufferF64:
for i, v := range buf.Data { for k := 0; k < channelCount; k++ {
buf.Data[i] = v * current slice.Scale64(buf.Channel(k), current)
} }
default: default:
return audio.ErrUnknownBuffer return audio.ErrUnknownBuffer
} }
return nil return nil
} }


channelCount := buf.ChannelCount() var active float64

switch buf := buf.(type) { switch buf := buf.(type) {
case *audio.BufferF32: case *audio.BufferF32:
for i := 0; i < len(buf.Data); i += channelCount { for k := 0; k < channelCount; k++ {
for k := 0; k < channelCount; k++ { active = current
buf.Data[k] *= float32(current) data := buf.Channel(k)
for i := range data {
data[i] *= float32(active)
active = (active + target) * 0.5
} }
current = (current + target) * 0.5
} }
case *audio.BufferF64: case *audio.BufferF64:
for i := 0; i < len(buf.Data); i += channelCount { for k := 0; k < channelCount; k++ {
for k := 0; k < channelCount; k++ { active = current
buf.Data[k] *= current data := buf.Channel(k)
for i := range data {
data[i] *= float64(active)
active = (active + target) * 0.5
} }
current = (current + target) * 0.5
} }
default: default:
return audio.ErrUnknownBuffer return audio.ErrUnknownBuffer
} }


current = active

if atomic2.AlmostEqual64(current, target) { if atomic2.AlmostEqual64(current, target) {
gain.current = target gain.current = target
} else { } else {
Expand Down
82 changes: 47 additions & 35 deletions audio/generate/float32.go
Original file line number Original file line Diff line number Diff line change
@@ -1,29 +1,28 @@
package generate package generate


import "github.com/egonelbre/exp/audio" import (
"github.com/egonelbre/exp/audio"
"github.com/egonelbre/exp/audio/slice"
)


func MonoF32(out audio.Buffer, sample func() float32) error { func MonoF32(out audio.Buffer, sample func() float32) error {
channelCount := out.ChannelCount() channelCount := out.ChannelCount()
switch out := out.(type) { switch out := out.(type) {
case *audio.BufferF32: case *audio.BufferF32:
switch channelCount { main := out.Channel(0)
case 1: for i := range main {
for i := 0; i < len(out.Data); i++ { main[i] = float32(sample())
out.Data[i] = sample() }
} for k := 1; k < channelCount; k++ {
case 2: copy(out.Channel(k), main)
for i := 0; i < len(out.Data); i += 2 { }
v := sample() case *audio.BufferF64:
out.Data[i] = v main := out.Channel(0)
out.Data[i+1] = v for i := range main {
} main[i] = float64(sample())
default: }
for i := 0; i < len(out.Data); i += channelCount { for k := 1; k < channelCount; k++ {
v := sample() copy(out.Channel(k), main)
for k := 0; k < channelCount; k++ {
out.Data[i+k] = v
}
}
} }
default: default:
//TODO: implement the slowest path //TODO: implement the slowest path
Expand All @@ -36,24 +35,37 @@ func StereoF32(out audio.Buffer, sample func() (float32, float32)) error {
channelCount := out.ChannelCount() channelCount := out.ChannelCount()
switch out := out.(type) { switch out := out.(type) {
case *audio.BufferF32: case *audio.BufferF32:
switch channelCount { if channelCount >= 2 {
case 1: left, right := out.Channel(0), out.Channel(1)
for i := 0; i < len(out.Data); i++ { for i := range left {
left, right := sample() leftsample, rightsample := sample()
out.Data[i] = (left + right) * 0.5 left[i], right[i] = float32(leftsample), float32(rightsample)
}
for k := 2; k < channelCount; k++ {
slice.Zero32(out.Channel(k))
}
} else {
main := out.Channel(0)
for i := range main {
leftsample, rightsample := sample()
main[i] = float32(leftsample+rightsample) * 0.5
}
}
case *audio.BufferF64:
if channelCount >= 2 {
left, right := out.Channel(0), out.Channel(1)
for i := range left {
leftsample, rightsample := sample()
left[i], right[i] = float64(leftsample), float64(rightsample)
} }
case 2: for k := 2; k < channelCount; k++ {
for i := 0; i < len(out.Data); i += 2 { slice.Zero64(out.Channel(k))
out.Data[i], out.Data[i+1] = sample()
} }
default: } else {
for i := 0; i < len(out.Data); i += channelCount { main := out.Channel(0)
left, right := sample() for i := range main {
out.Data[i] = left leftsample, rightsample := sample()
out.Data[i+1] = right main[i] = float64(leftsample+rightsample) * 0.5
for k := 2; k < channelCount; k++ {
out.Data[i+k] = 0
}
} }
} }
default: default:
Expand Down
Loading

0 comments on commit 8c77c79

Please sign in to comment.