Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

handle uint8 to float conversion #20

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 48 additions & 25 deletions int_buffer.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,39 +32,62 @@ func (buf *IntBuffer) AsFloatBuffer() *FloatBuffer {
return newB
}

// GetSourceBitDepth returns buf.SourceBitDepth if populated, otherwise returns an estimate
// of the source bit depth based on the range of integer values contained in the buffer.
func (buf *IntBuffer) GetSourceBitDepth() int {
if buf == nil {
return 0
}
if buf.SourceBitDepth != 0 {
return buf.SourceBitDepth
}

max := int64(0)
min := int64(0)
for _, s := range buf.Data {
if int64(s) > max {
max = int64(s)
} else if int64(s) < min {
min = int64(s)
}
}
if -min > max {
max = -min
}

// 8-bit PCM uses unsigned ints (bytes)
// Require max > 0 (vals in a silent 8-bit buffer should be ~128)
if min >= 0 && max > 0 && max <= 255 {
return 8
}
for _, n := range []int{16, 24, 32} {
// max abs val of an n-bit signed int is 2^(n-1)
if max <= 1<<(n-1) {
return n
}
}
return 64
}

// AsFloat32Buffer returns a copy of this buffer but with data converted to float 32.
func (buf *IntBuffer) AsFloat32Buffer() *Float32Buffer {
newB := &Float32Buffer{}
newB.Data = make([]float32, len(buf.Data))
max := int64(0)
// try to guess the bit depths without knowing the source
if buf.SourceBitDepth == 0 {
for _, s := range buf.Data {
if int64(s) > max {
max = int64(s)
}
}
buf.SourceBitDepth = 8
if max > 127 {
buf.SourceBitDepth = 16
}
// greater than int16, expecting int24
if max > 32767 {
buf.SourceBitDepth = 24
}
// int 32
if max > 8388607 {
buf.SourceBitDepth = 32
newB.SourceBitDepth = buf.GetSourceBitDepth()
var toFloat func(int) float32
if newB.SourceBitDepth == 8 {
// 8-bit uses unsigned ints
toFloat = func(d int) float32 {
return float32(d)/255*2 - 1
}
// int 64
if max > 4294967295 {
buf.SourceBitDepth = 64
} else {
factor := 1.0 / math.Pow(2, float64(newB.SourceBitDepth-1))
toFloat = func(d int) float32 {
return float32(float64(d) * factor)
}
}
newB.SourceBitDepth = buf.SourceBitDepth
factor := math.Pow(2, float64(buf.SourceBitDepth)-1)
for i := 0; i < len(buf.Data); i++ {
newB.Data[i] = float32(float64(buf.Data[i]) / factor)
newB.Data[i] = toFloat(buf.Data[i])
}
newB.Format = &Format{
NumChannels: buf.Format.NumChannels,
Expand Down
53 changes: 46 additions & 7 deletions int_buffer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,19 @@ import (

func TestIntBuffer_AsFloat32Buffer(t *testing.T) {
type fields struct {
Range int
Range []int
SourceBitDepth int
}
tests := []struct {
name string
fields fields
}{
{name: "16bit range",
fields: fields{Range: int(int16(1<<15 - 1)), SourceBitDepth: 16}},
{name: "24bit range",
fields: fields{Range: int(int32(1 << 23)), SourceBitDepth: 24}},
{name: "8bit range", // [0, 255]
fields: fields{Range: []int{0, 1<<8 - 1}, SourceBitDepth: 8}},
{name: "16bit range", // [-32768, 32767]
fields: fields{Range: []int{-1<<15, 1<<15 - 1}, SourceBitDepth: 16}},
{name: "24bit range", // [-8388608, 8388607]
fields: fields{Range: []int{-1<<23, 1<<23 - 1}, SourceBitDepth: 24}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand All @@ -25,9 +27,9 @@ func TestIntBuffer_AsFloat32Buffer(t *testing.T) {
SourceBitDepth: tt.fields.SourceBitDepth,
}
intData := []int{
-tt.fields.Range,
tt.fields.Range[0],
0,
tt.fields.Range,
tt.fields.Range[1],
}
buf.Data = intData
got := buf.AsFloat32Buffer()
Expand All @@ -39,3 +41,40 @@ func TestIntBuffer_AsFloat32Buffer(t *testing.T) {
})
}
}

func TestIntBuffer_GetSourceBitDepth(t *testing.T) {
type fields struct {
Range []int
SourceBitDepth int
}
tests := []struct {
name string
fields fields
}{
{name: "empty buf",
fields: fields{Range: []int{0, 0}, SourceBitDepth: 16}},
{name: "signed byte",
fields: fields{Range: []int{-128, 127}, SourceBitDepth: 16}},
{name: "8bit range", // [0, 255]
fields: fields{Range: []int{0, 1<<8 - 1}, SourceBitDepth: 8}},
{name: "16bit range", // [-32768, 32767]
fields: fields{Range: []int{-1<<15, 1<<15 - 1}, SourceBitDepth: 16}},
{name: "24bit range", // [-8388608, 8388607]
fields: fields{Range: []int{-1<<23, 1<<23 - 1}, SourceBitDepth: 24}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
buf := &IntBuffer{}
intData := []int{
tt.fields.Range[0],
0,
tt.fields.Range[1],
}
buf.Data = intData
got := buf.GetSourceBitDepth()
if got != tt.fields.SourceBitDepth {
t.Errorf("%d was misestimated as %d", tt.fields.SourceBitDepth, got)
}
})
}
}