mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2025-01-11 19:48:54 +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
|
posInBytes int
|
||||||
source io.Closer
|
source io.Closer
|
||||||
decoder decoder
|
decoder decoder
|
||||||
|
decoderr io.Reader
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoded) Read(b []byte) (int, error) {
|
func (d *decoded) Read(b []byte) (int, error) {
|
||||||
|
if d.decoderr == nil {
|
||||||
|
d.decoderr = convert.NewReaderFromFloat32Reader(d.decoder)
|
||||||
|
}
|
||||||
|
|
||||||
l := d.totalBytes - d.posInBytes
|
l := d.totalBytes - d.posInBytes
|
||||||
if l > len(b) {
|
if l > len(b) {
|
||||||
l = len(b)
|
l = len(b)
|
||||||
@ -86,28 +91,21 @@ func (d *decoded) Read(b []byte) (int, error) {
|
|||||||
return 0, io.EOF
|
return 0, io.EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
bf := make([]float32, l/2)
|
|
||||||
retry:
|
retry:
|
||||||
n, err := d.decoder.Read(bf)
|
n, err := d.decoderr.Read(b[:l])
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
return 0, err
|
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.
|
// When l is too small, decoder's Read might return 0 for a while. Let's retry.
|
||||||
goto retry
|
goto retry
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < n; i++ {
|
d.posInBytes += n
|
||||||
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
|
|
||||||
if d.posInBytes == d.totalBytes || err == io.EOF {
|
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) {
|
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
|
next = next / 2 * 2
|
||||||
d.posInBytes = int(next)
|
d.posInBytes = int(next)
|
||||||
d.decoder.SetPosition(next / int64(d.decoder.Channels()) / 2)
|
d.decoder.SetPosition(next / int64(d.decoder.Channels()) / 2)
|
||||||
|
d.decoderr = nil
|
||||||
return next, nil
|
return next, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,6 +132,7 @@ func (d *decoded) Close() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
d.decoder = nil
|
d.decoder = nil
|
||||||
|
d.decoderr = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user