// Copyright 2018 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 audio_test import ( "bytes" "io" "math" "testing" "github.com/hajimehoshi/ebiten/v2/audio" ) func TestInfiniteLoop(t *testing.T) { indexToByte := func(index int) byte { return byte(math.Sin(float64(index)) * 256) } src := make([]byte, 256) for i := range src { src[i] = indexToByte(i) } l := audio.NewInfiniteLoop(bytes.NewReader(src), int64(len(src))) buf := make([]byte, len(src)*4) if _, err := io.ReadFull(l, buf); err != nil { t.Error(err) } for i, b := range buf { got := b want := indexToByte(i % len(src)) if got != want { t.Errorf("index: %d, got: %v, want: %v", i, got, want) } } n, err := l.Seek(int64(len(src))*5+128, io.SeekStart) if err != nil { t.Error(err) } if want := int64(128); n != want { t.Errorf("got: %v, want: %v", n, want) } n2, err := l.Seek(int64(len(src))*6+64, io.SeekCurrent) if err != nil { t.Error(err) } if want := int64(192); n2 != want { t.Errorf("got: %v, want: %v", n, want) } buf2 := make([]byte, len(src)*7) if _, err := io.ReadFull(l, buf2); err != nil { t.Error(err) } for i, b := range buf2 { got := b want := indexToByte((i + 192) % len(src)) if got != want { t.Errorf("index: %d, got: %v, want: %v", i, got, want) } } // Seek to negative position is an error. if _, err := l.Seek(-1, io.SeekStart); err == nil { t.Errorf("got: %v, want: %v", err, nil) } } func TestInfiniteLoopWithIntro(t *testing.T) { const ( srcLength = 17 * 4 introLength = 19 * 4 loopLength = 23 * 4 ) indexToByte := func(index int) byte { return byte(math.Sin(float64(index)) * 256) } src := make([]byte, srcLength) for i := range src { src[i] = indexToByte(i) } srcInf := audio.NewInfiniteLoop(bytes.NewReader(src), srcLength) srcInf.SetNoBlendForTesting(true) l := audio.NewInfiniteLoopWithIntro(srcInf, introLength, loopLength) l.SetNoBlendForTesting(true) buf := make([]byte, srcLength*4) if _, err := io.ReadFull(l, buf); err != nil { t.Error(err) } for i, b := range buf { got := b want := byte(0) if i < introLength { want = indexToByte(i % srcLength) } else { want = indexToByte(((i-introLength)%loopLength + introLength) % srcLength) } if got != want { t.Errorf("index: %d, got: %v, want: %v", i, got, want) } } n, err := l.Seek(srcLength*5+128, io.SeekStart) if err != nil { t.Error(err) } if want := int64((srcLength*5+128-introLength)%loopLength + introLength); n != want { t.Errorf("got: %v, want: %v", n, want) } n2, err := l.Seek(srcLength*6+64, io.SeekCurrent) if err != nil { t.Error(err) } if want := int64(((srcLength*11+192)-introLength)%loopLength + introLength); n2 != want { t.Errorf("got: %v, want: %v", n, want) } buf2 := make([]byte, srcLength*7) if _, err := io.ReadFull(l, buf2); err != nil { t.Error(err) } for i, b := range buf2 { got := b idx := ((int(n2+int64(i))-introLength)%loopLength + introLength) % srcLength want := indexToByte(idx) if got != want { t.Errorf("index: %d, got: %v, want: %v", i, got, want) } } // Seek to negative position is an error. if _, err := l.Seek(-1, io.SeekStart); err == nil { t.Errorf("got: %v, want: %v", err, nil) } } func TestInfiniteLoopWithIncompleteSize(t *testing.T) { // s1 should work as if 4092 is given. s1 := audio.NewInfiniteLoop(bytes.NewReader(make([]byte, 4096)), 4095) n1, err := s1.Seek(4093, io.SeekStart) if err != nil { t.Error(err) } if got, want := n1, int64(4093-4092); got != want { t.Errorf("got: %d, want: %d", got, want) } // s2 should work as if 2044 and 2044 are given. s2 := audio.NewInfiniteLoopWithIntro(bytes.NewReader(make([]byte, 4096)), 2047, 2046) n2, err := s2.Seek(4093, io.SeekStart) if err != nil { t.Error(err) } if got, want := n2, int64(2044+(4093-(2044+2044))); got != want { t.Errorf("got: %d, want: %d", got, want) } } type slowReader struct { src io.ReadSeeker eof bool } func (s *slowReader) Read(buf []byte) (int, error) { if len(buf) == 0 { if s.eof { return 0, io.EOF } return 0, nil } n, err := s.src.Read(buf[:1]) if err == io.EOF { s.eof = true } return n, err } func (s *slowReader) Seek(offset int64, whence int) (int64, error) { s.eof = false return s.src.Seek(offset, whence) } func TestInfiniteLoopWithSlowSource(t *testing.T) { src := make([]byte, 4096) for i := range src { src[i] = byte(i) } r := &slowReader{ src: bytes.NewReader(src), } loop := audio.NewInfiniteLoop(r, 4096) buf := make([]byte, 4096) // With a slow source, whose Read always reads at most one byte, // an infinite loop should adjust the reading size along with bitDepthInBytes (= 2). n0, err := loop.Read(buf) if err != nil { t.Error(err) } if got, want := n0, 0; got != want { t.Errorf("got: %d, want: %d", got, want) } n1, err := loop.Read(buf) if err != nil { t.Error(err) } if got, want := n1, 2; got != want { t.Errorf("got: %d, want: %d", got, want) } if got, want := buf[0], byte(0); got != want { t.Errorf("got: %d, want: %d", got, want) } if got, want := buf[1], byte(1); got != want { t.Errorf("got: %d, want: %d", got, want) } n2, err := loop.Read(buf) if err != nil { t.Error(err) } if got, want := n2, 0; got != want { t.Errorf("got: %d, want: %d", got, want) } n3, err := loop.Read(buf) if err != nil { t.Error(err) } if got, want := n3, 2; got != want { t.Errorf("got: %d, want: %d", got, want) } if got, want := buf[0], byte(2); got != want { t.Errorf("got: %d, want: %d", got, want) } if got, want := buf[1], byte(3); got != want { t.Errorf("got: %d, want: %d", got, want) } }