subsonic-tui/vendor/github.com/ebitengine/oto/v3/driver_windows.go
Sagi Dayan a3923cf42c initial commit
Signed-off-by: Sagi Dayan <sagidayan@gmail.com>
2024-03-29 17:56:39 +03:00

164 lines
3.5 KiB
Go

// Copyright 2022 The Oto 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 oto
import (
"errors"
"fmt"
"time"
"github.com/ebitengine/oto/v3/internal/mux"
)
var errDeviceNotFound = errors.New("oto: device not found")
type context struct {
sampleRate int
channelCount int
mux *mux.Mux
wasapiContext *wasapiContext
winmmContext *winmmContext
nullContext *nullContext
ready chan struct{}
err atomicError
}
func newContext(sampleRate int, channelCount int, format mux.Format, bufferSizeInBytes int) (*context, chan struct{}, error) {
ctx := &context{
sampleRate: sampleRate,
channelCount: channelCount,
mux: mux.New(sampleRate, channelCount, format),
ready: make(chan struct{}),
}
// Initializing drivers might take some time. Do this asynchronously.
go func() {
defer close(ctx.ready)
xc, err0 := newWASAPIContext(sampleRate, channelCount, ctx.mux, bufferSizeInBytes)
if err0 == nil {
ctx.wasapiContext = xc
return
}
wc, err1 := newWinMMContext(sampleRate, channelCount, ctx.mux, bufferSizeInBytes)
if err1 == nil {
ctx.winmmContext = wc
return
}
if errors.Is(err0, errDeviceNotFound) && errors.Is(err1, errDeviceNotFound) {
ctx.nullContext = newNullContext(sampleRate, channelCount, ctx.mux)
return
}
ctx.err.TryStore(fmt.Errorf("oto: initialization failed: WASAPI: %v, WinMM: %v", err0, err1))
}()
return ctx, ctx.ready, nil
}
func (c *context) Suspend() error {
<-c.ready
if c.wasapiContext != nil {
return c.wasapiContext.Suspend()
}
if c.winmmContext != nil {
return c.winmmContext.Suspend()
}
if c.nullContext != nil {
return c.nullContext.Suspend()
}
return nil
}
func (c *context) Resume() error {
<-c.ready
if c.wasapiContext != nil {
return c.wasapiContext.Resume()
}
if c.winmmContext != nil {
return c.winmmContext.Resume()
}
if c.nullContext != nil {
return c.nullContext.Resume()
}
return nil
}
func (c *context) Err() error {
if err := c.err.Load(); err != nil {
return err
}
select {
case <-c.ready:
default:
return nil
}
if c.wasapiContext != nil {
return c.wasapiContext.Err()
}
if c.winmmContext != nil {
return c.winmmContext.Err()
}
if c.nullContext != nil {
return c.nullContext.Err()
}
return nil
}
type nullContext struct {
suspended bool
}
func newNullContext(sampleRate int, channelCount int, mux *mux.Mux) *nullContext {
c := &nullContext{}
go c.loop(sampleRate, channelCount, mux)
return c
}
func (c *nullContext) loop(sampleRate int, channelCount int, mux *mux.Mux) {
var buf32 [4096]float32
sleep := time.Duration(float64(time.Second) * float64(len(buf32)) / float64(channelCount) / float64(sampleRate))
for {
if c.suspended {
time.Sleep(time.Second)
continue
}
mux.ReadFloat32s(buf32[:])
time.Sleep(sleep)
}
}
func (c *nullContext) Suspend() error {
c.suspended = true
return nil
}
func (c *nullContext) Resume() error {
c.suspended = false
return nil
}
func (*nullContext) Err() error {
return nil
}