ebiten/audio/internal/readerdriver/driver_android.go

294 lines
5.1 KiB
Go

// Copyright 2021 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 readerdriver
import (
"io"
"runtime"
"sync"
"time"
"github.com/hajimehoshi/ebiten/v2/audio/internal/oboe"
)
func IsAvailable() bool {
return true
}
type context struct {
sampleRate int
channelNum int
bitDepthInBytes int
}
func NewContext(sampleRate int, channelNum int, bitDepthInBytes int) (Context, chan struct{}, error) {
ready := make(chan struct{})
close(ready)
c := &context{
sampleRate: sampleRate,
channelNum: channelNum,
bitDepthInBytes: bitDepthInBytes,
}
return c, ready, nil
}
func (c *context) NewPlayer(src io.Reader) Player {
p := &player{
context: c,
src: src,
cond: sync.NewCond(&sync.Mutex{}),
volume: 1,
}
runtime.SetFinalizer(p, (*player).Close)
return p
}
func (c *context) Suspend() error {
return oboe.Suspend()
}
func (c *context) Resume() error {
return oboe.Resume()
}
func (c *context) Close() error {
// TODO: Implement this
return nil
}
type player struct {
context *context
p *oboe.Player
src io.Reader
err error
cond *sync.Cond
closed bool
volume float64
}
func (p *player) Pause() {
p.cond.L.Lock()
defer p.cond.L.Unlock()
if p.err != nil {
return
}
if p.closed {
return
}
if p.p == nil {
return
}
if err := p.p.Pause(); err != nil {
p.setErrorImpl(err)
return
}
}
func (p *player) Play() {
p.cond.L.Lock()
defer p.cond.L.Unlock()
if p.err != nil {
return
}
if p.p != nil && p.p.IsPlaying() {
return
}
defer p.cond.Signal()
var runLoop bool
if p.p == nil {
p.p = oboe.NewPlayer(p.context.sampleRate, p.context.channelNum, p.context.bitDepthInBytes, p.volume, func() {
p.cond.Signal()
})
runLoop = true
// Fill the first part before playing to reduce noises (#1632).
buf := make([]byte, p.context.oneBufferSize())
n, err := p.src.Read(buf)
if err != nil && err != io.EOF {
p.setErrorImpl(err)
return
}
p.p.AppendBuffer(buf[:n])
}
if err := p.p.Play(); err != nil {
p.setErrorImpl(err)
return
}
if runLoop {
go p.loop()
}
}
func (p *player) IsPlaying() bool {
p.cond.L.Lock()
defer p.cond.L.Unlock()
if p.p == nil {
return false
}
return p.p.IsPlaying()
}
func (p *player) Reset() {
p.cond.L.Lock()
defer p.cond.L.Unlock()
if p.err != nil {
return
}
if p.closed {
return
}
if p.p == nil {
return
}
defer func() {
p.p = nil
p.cond.Signal()
}()
if err := p.p.Close(); err != nil {
p.setErrorImpl(err)
return
}
}
func (p *player) Volume() float64 {
p.cond.L.Lock()
defer p.cond.L.Unlock()
return p.volume
}
func (p *player) SetVolume(volume float64) {
p.cond.L.Lock()
defer p.cond.L.Unlock()
p.volume = volume
if p.p == nil {
return
}
p.p.SetVolume(volume)
}
func (p *player) UnplayedBufferSize() int64 {
p.cond.L.Lock()
defer p.cond.L.Unlock()
if p.p == nil {
return 0
}
return p.p.UnplayedBufferSize()
}
func (p *player) Err() error {
p.cond.L.Lock()
defer p.cond.L.Unlock()
return p.err
}
func (p *player) Close() error {
p.cond.L.Lock()
defer p.cond.L.Unlock()
return p.closeImpl()
}
func (p *player) closeImpl() error {
defer p.cond.Signal()
runtime.SetFinalizer(p, nil)
p.closed = true
if p.p == nil {
return p.err
}
if err := p.p.Close(); err != nil && p.err == nil {
// Do not call setErrorImpl, or this can cause infinite recursive.
p.err = err
return p.err
}
p.p = nil
return p.err
}
func (p *player) setError(err error) {
p.cond.L.Lock()
defer p.cond.L.Unlock()
p.setErrorImpl(err)
}
func (p *player) setErrorImpl(err error) {
p.err = err
p.closeImpl()
}
func (p *player) shouldWait() bool {
if p.closed {
return false
}
if p.p == nil {
return false
}
if p.p.IsPlaying() {
return p.p.UnplayedBufferSize() >= int64(p.context.maxBufferSize())
}
return true
}
func (p *player) wait() bool {
p.cond.L.Lock()
defer p.cond.L.Unlock()
for p.shouldWait() {
p.cond.Wait()
}
return p.p != nil && p.p.IsPlaying()
}
func (p *player) write(buf []byte) {
p.cond.L.Lock()
defer p.cond.L.Unlock()
if p.closed {
return
}
if p.p == nil {
return
}
p.p.AppendBuffer(buf)
}
func (p *player) loop() {
buf := make([]byte, 4096)
for {
if !p.wait() {
return
}
n, err := p.src.Read(buf)
if err != nil && err != io.EOF {
p.setError(err)
return
}
p.write(buf[:n])
// Now p.p.Reset() doesn't close the stream gracefully. Then buffer size check is necessary here.
if err == io.EOF && p.UnplayedBufferSize() == 0 {
// Even when the unplayed buffer size is 0, the audio data in the hardware might not be played yet (#1632).
// Just wait for a while.
time.Sleep(100 * time.Millisecond)
p.Reset()
return
}
}
}