From 2e49ed5402e9273073fe117af85e9f0e612f803b Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Tue, 21 Apr 2020 23:51:46 +0900 Subject: [PATCH] audio: Add a mock implementation for MonoGame Updates #1078 --- audio/context.go | 70 +------------------------- audio/contextimpl_monogame.go | 58 +++++++++++++++++++++ audio/contextimpl_oto.go | 94 +++++++++++++++++++++++++++++++++++ 3 files changed, 153 insertions(+), 69 deletions(-) create mode 100644 audio/contextimpl_monogame.go create mode 100644 audio/contextimpl_oto.go diff --git a/audio/context.go b/audio/context.go index 39fc3aea2..db77547dd 100644 --- a/audio/context.go +++ b/audio/context.go @@ -18,8 +18,6 @@ import ( "io" "sync" - "github.com/hajimehoshi/oto" - "github.com/hajimehoshi/ebiten/internal/hooks" ) @@ -30,69 +28,6 @@ type context interface { var contextForTesting context -type otoContext struct { - sampleRate int - initCh <-chan struct{} - - c *oto.Context - once sync.Once -} - -func (c *otoContext) NewPlayer() io.WriteCloser { - return &otoPlayer{c: c} -} - -func (c *otoContext) Close() error { - if c.c == nil { - return nil - } - return c.c.Close() -} - -func (c *otoContext) ensureContext() error { - var err error - c.once.Do(func() { - <-c.initCh - c.c, err = oto.NewContext(c.sampleRate, channelNum, bytesPerSample/channelNum, bufferSize()) - }) - return err -} - -type otoPlayer struct { - c *otoContext - p *oto.Player - once sync.Once -} - -func (p *otoPlayer) Write(buf []byte) (int, error) { - // Initialize oto.Player lazily to enable calling NewContext in an 'init' function. - // Accessing oto.Player functions requires the environment to be already initialized, - // but if Ebiten is used for a shared library, the timing when init functions are called - // is unexpectable. - // e.g. a variable for JVM on Android might not be set. - if err := p.ensurePlayer(); err != nil { - return 0, err - } - return p.p.Write(buf) -} - -func (p *otoPlayer) Close() error { - if p.p == nil { - return nil - } - return p.p.Close() -} - -func (p *otoPlayer) ensurePlayer() error { - if err := p.c.ensureContext(); err != nil { - return err - } - p.once.Do(func() { - p.p = p.c.c.NewPlayer() - }) - return nil -} - func newContext(sampleRate int) context { if contextForTesting != nil { return contextForTesting @@ -106,10 +41,7 @@ func newContext(sampleRate int) context { }) return nil }) - return &otoContext{ - sampleRate: sampleRate, - initCh: ch, - } + return newContextImpl(sampleRate, ch) } type hook interface { diff --git a/audio/contextimpl_monogame.go b/audio/contextimpl_monogame.go new file mode 100644 index 000000000..ea1b84683 --- /dev/null +++ b/audio/contextimpl_monogame.go @@ -0,0 +1,58 @@ +// Copyright 2020 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. + +// +build monogame + +package audio + +import ( + "io" + "time" +) + +func newContextImpl(sampleRate int, initCh chan struct{}) context { + return &mockContext{ + sampleRate: sampleRate, + } +} + +type mockContext struct { + sampleRate int +} + +func (*mockContext) Close() error { + return nil +} + +func (m *mockContext) NewPlayer() io.WriteCloser { + return &mockPlayer{ + sampleRate: m.sampleRate, + } +} + +type mockPlayer struct { + sampleRate int +} + +func (m *mockPlayer) Write(bs []byte) (int, error) { + bytesPerSec := 4 * m.sampleRate + + n := len(bs) + time.Sleep(time.Duration(n) * time.Second / time.Duration(bytesPerSec)) + return n, nil +} + +func (*mockPlayer) Close() error { + return nil +} diff --git a/audio/contextimpl_oto.go b/audio/contextimpl_oto.go new file mode 100644 index 000000000..c3cbf7ba5 --- /dev/null +++ b/audio/contextimpl_oto.go @@ -0,0 +1,94 @@ +// Copyright 2020 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. + +// +build !monogame + +package audio + +import ( + "io" + "sync" + + "github.com/hajimehoshi/oto" +) + +func newContextImpl(sampleRate int, initCh chan struct{}) context { + return &otoContext{ + sampleRate: sampleRate, + initCh: initCh, + } +} + +type otoContext struct { + sampleRate int + initCh <-chan struct{} + + c *oto.Context + once sync.Once +} + +func (c *otoContext) NewPlayer() io.WriteCloser { + return &otoPlayer{c: c} +} + +func (c *otoContext) Close() error { + if c.c == nil { + return nil + } + return c.c.Close() +} + +func (c *otoContext) ensureContext() error { + var err error + c.once.Do(func() { + <-c.initCh + c.c, err = oto.NewContext(c.sampleRate, channelNum, bytesPerSample/channelNum, bufferSize()) + }) + return err +} + +type otoPlayer struct { + c *otoContext + p *oto.Player + once sync.Once +} + +func (p *otoPlayer) Write(buf []byte) (int, error) { + // Initialize oto.Player lazily to enable calling NewContext in an 'init' function. + // Accessing oto.Player functions requires the environment to be already initialized, + // but if Ebiten is used for a shared library, the timing when init functions are called + // is unexpectable. + // e.g. a variable for JVM on Android might not be set. + if err := p.ensurePlayer(); err != nil { + return 0, err + } + return p.p.Write(buf) +} + +func (p *otoPlayer) Close() error { + if p.p == nil { + return nil + } + return p.p.Close() +} + +func (p *otoPlayer) ensurePlayer() error { + if err := p.c.ensureContext(); err != nil { + return err + } + p.once.Do(func() { + p.p = p.c.c.NewPlayer() + }) + return nil +}