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 Diff line number Diff line change
Expand Up @@ -3,51 +3,61 @@ package audio
import "time"

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

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

func NewBufferF32Frames(format Format, frames int) *BufferF32 {
n := format.ChannelCount * frames
samples := format.ChannelCount * frames
return &BufferF32{
Format: format,
Data: make([]float32, n, n),
format: format,
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) ChannelCount() int { return b.Format.ChannelCount }
func (b *BufferF32) Channel(index int) []float32 {
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 {
return len(b.Data) / b.ChannelCount()
}
func (b *BufferF32) Empty() bool { return b.frames == 0 }
func (b *BufferF32) FrameCount() int { return int(b.frames) }

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

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

func (b *BufferF32) DeepCopy() Buffer {
x := &BufferF32{
Format: b.Format,
Data: make([]float32, len(b.Data), len(b.Data)),
}
copy(x.Data, b.Data)
return x
x := *b
x.data = make([]float32, len(b.data), len(b.data))
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) {
b.Data = b.Data[lowFrame*b.ChannelCount() : highFrame*b.ChannelCount()]
func (b *BufferF32) CutLeading(low int) {
b.offset += uint32(low)
b.frames -= uint32(low)
}
54 changes: 32 additions & 22 deletions audio/buffer64.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ package audio
import "time"

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

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 {
n := format.ChannelCount * frames
return &BufferF64{
Format: format,
Data: make([]float64, n, n),
format: format,
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) ChannelCount() int { return b.Format.ChannelCount }
func (b *BufferF64) Channel(index int) []float64 {
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 {
return len(b.Data) / b.ChannelCount()
}
func (b *BufferF64) Empty() bool { return b.frames == 0 }
func (b *BufferF64) FrameCount() int { return int(b.frames) }

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

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

func (b *BufferF64) DeepCopy() Buffer {
x := &BufferF64{
Format: b.Format,
Data: make([]float64, len(b.Data), len(b.Data)),
}
copy(x.Data, b.Data)
return x
x := *b
x.data = make([]float64, len(b.data), len(b.data))
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) {
b.Data = b.Data[lowFrame*b.ChannelCount() : highFrame*b.ChannelCount()]
func (b *BufferF64) CutLeading(low int) {
b.offset += uint32(low)
b.frames -= uint32(low)
}
36 changes: 22 additions & 14 deletions audio/example/internal/effect/gain.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package effect
import (
"github.com/egonelbre/exp/audio"
"github.com/egonelbre/exp/audio/example/internal/atomic2"
"github.com/egonelbre/exp/audio/slice"
)

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

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

channelCount := buf.ChannelCount()
var active float64

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

current = active

if atomic2.AlmostEqual64(current, target) {
gain.current = target
} else {
Expand Down
82 changes: 47 additions & 35 deletions audio/generate/float32.go
Original file line number Diff line number Diff line change
@@ -1,29 +1,28 @@
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 {
channelCount := out.ChannelCount()
switch out := out.(type) {
case *audio.BufferF32:
switch channelCount {
case 1:
for i := 0; i < len(out.Data); i++ {
out.Data[i] = sample()
}
case 2:
for i := 0; i < len(out.Data); i += 2 {
v := sample()
out.Data[i] = v
out.Data[i+1] = v
}
default:
for i := 0; i < len(out.Data); i += channelCount {
v := sample()
for k := 0; k < channelCount; k++ {
out.Data[i+k] = v
}
}
main := out.Channel(0)
for i := range main {
main[i] = float32(sample())
}
for k := 1; k < channelCount; k++ {
copy(out.Channel(k), main)
}
case *audio.BufferF64:
main := out.Channel(0)
for i := range main {
main[i] = float64(sample())
}
for k := 1; k < channelCount; k++ {
copy(out.Channel(k), main)
}
default:
//TODO: implement the slowest path
Expand All @@ -36,24 +35,37 @@ func StereoF32(out audio.Buffer, sample func() (float32, float32)) error {
channelCount := out.ChannelCount()
switch out := out.(type) {
case *audio.BufferF32:
switch channelCount {
case 1:
for i := 0; i < len(out.Data); i++ {
left, right := sample()
out.Data[i] = (left + right) * 0.5
if channelCount >= 2 {
left, right := out.Channel(0), out.Channel(1)
for i := range left {
leftsample, rightsample := sample()
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 i := 0; i < len(out.Data); i += 2 {
out.Data[i], out.Data[i+1] = sample()
for k := 2; k < channelCount; k++ {
slice.Zero64(out.Channel(k))
}
default:
for i := 0; i < len(out.Data); i += channelCount {
left, right := sample()
out.Data[i] = left
out.Data[i+1] = right
for k := 2; k < channelCount; k++ {
out.Data[i+k] = 0
}
} else {
main := out.Channel(0)
for i := range main {
leftsample, rightsample := sample()
main[i] = float64(leftsample+rightsample) * 0.5
}
}
default:
Expand Down
Loading

0 comments on commit 8c77c79

Please sign in to comment.