mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-24 02:38:53 +01:00
audio/internal/convert: Add Float32Reader and NewReaderFromFloat32Reader
This commit is contained in:
parent
13f6549cb6
commit
f16f6cf4b9
78
audio/internal/convert/float32.go
Normal file
78
audio/internal/convert/float32.go
Normal file
@ -0,0 +1,78 @@
|
||||
// Copyright 2019 The Ebiten Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package convert
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
type Float32Reader interface {
|
||||
Read([]float32) (int, error)
|
||||
}
|
||||
|
||||
func NewReaderFromFloat32Reader(r Float32Reader) io.Reader {
|
||||
return &f32Reader{r: r}
|
||||
}
|
||||
|
||||
type f32Reader struct {
|
||||
r Float32Reader
|
||||
eof bool
|
||||
buf *byte
|
||||
}
|
||||
|
||||
func (f *f32Reader) Read(buf []byte) (int, error) {
|
||||
if f.eof {
|
||||
return 0, io.EOF
|
||||
}
|
||||
if len(buf) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
if f.buf != nil {
|
||||
buf[0] = *f.buf
|
||||
f.buf = nil
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
bf := make([]float32, len(buf)/2)
|
||||
if len(buf) == 1 {
|
||||
bf = make([]float32, 1)
|
||||
}
|
||||
|
||||
n, err := f.r.Read(bf)
|
||||
if err != nil && err != io.EOF {
|
||||
return 0, err
|
||||
}
|
||||
if err == io.EOF {
|
||||
f.eof = true
|
||||
}
|
||||
|
||||
b := buf
|
||||
if len(buf) == 1 && n > 0 {
|
||||
b = make([]byte, 2)
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
f := bf[i]
|
||||
s := int16(f * (1<<15 - 1))
|
||||
b[2*i] = uint8(s)
|
||||
b[2*i+1] = uint8(s >> 8)
|
||||
}
|
||||
|
||||
if len(buf) == 1 && len(b) == 2 {
|
||||
buf[0] = b[0]
|
||||
f.buf = &b[1]
|
||||
return 1, err
|
||||
}
|
||||
return n * 2, err
|
||||
}
|
111
audio/internal/convert/float32_test.go
Normal file
111
audio/internal/convert/float32_test.go
Normal file
@ -0,0 +1,111 @@
|
||||
// Copyright 2019 The Ebiten Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package convert_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
. "github.com/hajimehoshi/ebiten/audio/internal/convert"
|
||||
)
|
||||
|
||||
type f32reader struct {
|
||||
data []float32
|
||||
pos int
|
||||
}
|
||||
|
||||
func (f *f32reader) Read(buf []float32) (int, error) {
|
||||
if f.pos == len(f.data) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
n := copy(buf, f.data[f.pos:])
|
||||
f.pos += n
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func newFloat32Reader(data []float32) Float32Reader {
|
||||
return &f32reader{data: data}
|
||||
}
|
||||
|
||||
func TestFloat32Reader(t *testing.T) {
|
||||
in1 := make([]float32, 256)
|
||||
for i := range in1 {
|
||||
in1[i] = float32(math.Sin(float64(i)))
|
||||
}
|
||||
in2 := make([]float32, 65536)
|
||||
for i := range in2 {
|
||||
in2[i] = float32(math.Cos(float64(i)))
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
In []float32
|
||||
N int
|
||||
}{
|
||||
{
|
||||
In: in1,
|
||||
N: 1,
|
||||
},
|
||||
{
|
||||
In: in1,
|
||||
N: 2,
|
||||
},
|
||||
{
|
||||
In: in1,
|
||||
N: 3,
|
||||
},
|
||||
{
|
||||
In: in1,
|
||||
N: 1024,
|
||||
},
|
||||
{
|
||||
In: in2,
|
||||
N: 1,
|
||||
},
|
||||
{
|
||||
In: in2,
|
||||
N: 4096,
|
||||
},
|
||||
}
|
||||
|
||||
for i, c := range cases {
|
||||
r := NewReaderFromFloat32Reader(newFloat32Reader(c.In))
|
||||
|
||||
got := []byte{}
|
||||
for {
|
||||
buf := make([]byte, c.N)
|
||||
n, err := r.Read(buf)
|
||||
if err != nil {
|
||||
if n == 0 && err == io.EOF {
|
||||
break
|
||||
}
|
||||
t.Fatal(err)
|
||||
}
|
||||
got = append(got, buf[:n]...)
|
||||
}
|
||||
|
||||
want := make([]byte, len(c.In)*2)
|
||||
for i, f := range c.In {
|
||||
s := int16(f * (1<<15 - 1))
|
||||
want[2*i] = byte(s)
|
||||
want[2*i+1] = byte(s >> 8)
|
||||
}
|
||||
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("case: %d, got: %v, want: %v", i, got, want)
|
||||
}
|
||||
}
|
||||
}
|
@ -75,9 +75,14 @@ type decoded struct {
|
||||
posInBytes int
|
||||
source io.Closer
|
||||
decoder decoder
|
||||
decoderr io.Reader
|
||||
}
|
||||
|
||||
func (d *decoded) Read(b []byte) (int, error) {
|
||||
if d.decoderr == nil {
|
||||
d.decoderr = convert.NewReaderFromFloat32Reader(d.decoder)
|
||||
}
|
||||
|
||||
l := d.totalBytes - d.posInBytes
|
||||
if l > len(b) {
|
||||
l = len(b)
|
||||
@ -86,28 +91,21 @@ func (d *decoded) Read(b []byte) (int, error) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
bf := make([]float32, l/2)
|
||||
retry:
|
||||
n, err := d.decoder.Read(bf)
|
||||
n, err := d.decoderr.Read(b[:l])
|
||||
if err != nil && err != io.EOF {
|
||||
return 0, err
|
||||
}
|
||||
if n == 0 && len(bf) > 0 && err != io.EOF {
|
||||
if n == 0 && l > 0 && err != io.EOF {
|
||||
// When l is too small, decoder's Read might return 0 for a while. Let's retry.
|
||||
goto retry
|
||||
}
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
f := bf[i]
|
||||
s := int16(f * (1<<15 - 1))
|
||||
b[2*i] = uint8(s)
|
||||
b[2*i+1] = uint8(s >> 8)
|
||||
}
|
||||
d.posInBytes += 2 * n
|
||||
d.posInBytes += n
|
||||
if d.posInBytes == d.totalBytes || err == io.EOF {
|
||||
return 2 * n, io.EOF
|
||||
return n, io.EOF
|
||||
}
|
||||
return 2 * n, nil
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (d *decoded) Seek(offset int64, whence int) (int64, error) {
|
||||
@ -124,6 +122,7 @@ func (d *decoded) Seek(offset int64, whence int) (int64, error) {
|
||||
next = next / 2 * 2
|
||||
d.posInBytes = int(next)
|
||||
d.decoder.SetPosition(next / int64(d.decoder.Channels()) / 2)
|
||||
d.decoderr = nil
|
||||
return next, nil
|
||||
}
|
||||
|
||||
@ -133,6 +132,7 @@ func (d *decoded) Close() error {
|
||||
return err
|
||||
}
|
||||
d.decoder = nil
|
||||
d.decoderr = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user