diff --git a/audio/audio.go b/audio/audio.go index f5b32060e..f7c1c3598 100644 --- a/audio/audio.go +++ b/audio/audio.go @@ -63,11 +63,11 @@ type Context struct { sampleRate int err error ready bool + suspended bool players map[*playerImpl]struct{} - m sync.Mutex - semaphore chan struct{} + m sync.Mutex } var ( @@ -75,6 +75,8 @@ var ( theContextLock sync.Mutex ) +var emptyBytes = make([]byte, 256) + // NewContext creates a new audio context with the given sample rate. // // The sample rate is also used for decoding MP3 with audio/mp3 package @@ -99,16 +101,19 @@ func NewContext(sampleRate int) (*Context, error) { c: newContext(sampleRate), players: map[*playerImpl]struct{}{}, inited: make(chan struct{}), - semaphore: make(chan struct{}, 1), } theContext = c h := getHook() h.OnSuspendAudio(func() { - c.semaphore <- struct{}{} + c.m.Lock() + c.suspended = true + c.m.Unlock() }) h.OnResumeAudio(func() { - <-c.semaphore + c.m.Lock() + c.suspended = false + c.m.Unlock() }) h.AppendHookOnBeforeUpdate(func() error { @@ -455,10 +460,14 @@ func (p *playerImpl) read() ([]byte, bool) { const bufSize = 2048 - p.context.semaphore <- struct{}{} - defer func() { - <-p.context.semaphore - }() + // If audio is suspended, fill zero values not to cause delay (#975). + // TODO: Oto's players should be able to be suspended and resumed. + p.context.m.Lock() + s := p.context.suspended + p.context.m.Unlock() + if s { + return emptyBytes, true + } newBuf := make([]byte, bufSize-len(p.buf)) n, err := p.src.Read(newBuf)