mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-26 03:38:55 +01:00
tmp
This commit is contained in:
parent
3fb469d5a5
commit
c889feb669
@ -1,13 +1,13 @@
|
|||||||
image: alpine/latest
|
image: alpine/latest
|
||||||
packages:
|
packages:
|
||||||
- alsa-lib-dev
|
|
||||||
- libx11-dev
|
- libx11-dev
|
||||||
- libxrandr-dev
|
|
||||||
- libxcursor-dev
|
- libxcursor-dev
|
||||||
- libxinerama-dev
|
- libxinerama-dev
|
||||||
- libxi-dev
|
- libxi-dev
|
||||||
|
- libxrandr-dev
|
||||||
- mesa-dev
|
- mesa-dev
|
||||||
- pkgconf
|
- pkgconf
|
||||||
|
- portaudio-dev
|
||||||
- go
|
- go
|
||||||
sources:
|
sources:
|
||||||
- https://github.com/hajimehoshi/ebiten
|
- https://github.com/hajimehoshi/ebiten
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
image: archlinux
|
image: archlinux
|
||||||
packages:
|
packages:
|
||||||
- alsa-lib
|
|
||||||
- mesa
|
|
||||||
- libxrandr
|
|
||||||
- libxcursor
|
- libxcursor
|
||||||
- libxinerama
|
|
||||||
- libxi
|
- libxi
|
||||||
|
- libxinerama
|
||||||
|
- libxrandr
|
||||||
|
- mesa
|
||||||
- pkg-config
|
- pkg-config
|
||||||
|
- portaudio
|
||||||
- go
|
- go
|
||||||
sources:
|
sources:
|
||||||
- https://github.com/hajimehoshi/ebiten
|
- https://github.com/hajimehoshi/ebiten
|
||||||
|
@ -8,8 +8,8 @@ packages:
|
|||||||
- libxinerama-dev
|
- libxinerama-dev
|
||||||
- libxrandr-dev
|
- libxrandr-dev
|
||||||
- libxxf86vm-dev
|
- libxxf86vm-dev
|
||||||
- libasound2-dev
|
|
||||||
- pkg-config
|
- pkg-config
|
||||||
|
- portaudio19-dev
|
||||||
- golang
|
- golang
|
||||||
sources:
|
sources:
|
||||||
- https://github.com/hajimehoshi/ebiten
|
- https://github.com/hajimehoshi/ebiten
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
image: fedora/latest
|
image: fedora/latest
|
||||||
packages:
|
packages:
|
||||||
- mesa-libGLU-devel
|
|
||||||
- mesa-libGLES-devel
|
|
||||||
- libXrandr-devel
|
|
||||||
- libXcursor-devel
|
- libXcursor-devel
|
||||||
|
- libXrandr-devel
|
||||||
- libXinerama-devel
|
- libXinerama-devel
|
||||||
- libXi-devel
|
- libXi-devel
|
||||||
- libXxf86vm-devel
|
- libXxf86vm-devel
|
||||||
- alsa-lib-devel
|
- mesa-libGLES-devel
|
||||||
|
- mesa-libGLU-devel
|
||||||
- pkg-config
|
- pkg-config
|
||||||
|
- portaudio-devel
|
||||||
- go
|
- go
|
||||||
sources:
|
sources:
|
||||||
- https://github.com/hajimehoshi/ebiten
|
- https://github.com/hajimehoshi/ebiten
|
||||||
|
@ -8,8 +8,8 @@ packages:
|
|||||||
- libxinerama-dev
|
- libxinerama-dev
|
||||||
- libxrandr-dev
|
- libxrandr-dev
|
||||||
- libxxf86vm-dev
|
- libxxf86vm-dev
|
||||||
- libasound2-dev
|
|
||||||
- pkg-config
|
- pkg-config
|
||||||
|
- portaudio19-dev
|
||||||
- golang
|
- golang
|
||||||
sources:
|
sources:
|
||||||
- https://github.com/hajimehoshi/ebiten
|
- https://github.com/hajimehoshi/ebiten
|
||||||
|
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@ -35,7 +35,7 @@ jobs:
|
|||||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install libasound2-dev libgl1-mesa-dev libalut-dev libxcursor-dev libxi-dev libxinerama-dev libxrandr-dev libxxf86vm-dev
|
sudo apt-get install libgl1-mesa-dev libxcursor-dev libxi-dev libxinerama-dev libxrandr-dev libxxf86vm-dev portaudio19-dev
|
||||||
|
|
||||||
- name: Install wasmbrowsertest
|
- name: Install wasmbrowsertest
|
||||||
run: |
|
run: |
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
// +build !android
|
|
||||||
// +build !darwin
|
|
||||||
// +build !js
|
|
||||||
// +build !windows
|
|
||||||
|
|
||||||
package readerdriver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
func IsAvailable() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type context struct {
|
|
||||||
sampleRate int
|
|
||||||
channelNum int
|
|
||||||
bitDepthInBytes int
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewContext(sampleRate int, channelNum int, bitDepthInBytes int) (Context, chan struct{}, error) {
|
|
||||||
panic(fmt.Sprintf("readerdriver: NewContext is not available on this environment: GOOS=%s", runtime.GOOS))
|
|
||||||
}
|
|
453
audio/internal/readerdriver/driver_unix.go
Normal file
453
audio/internal/readerdriver/driver_unix.go
Normal file
@ -0,0 +1,453 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// +build aix dragonfly freebsd hurd illumos linux netbsd openbsd solaris
|
||||||
|
// +build !android
|
||||||
|
|
||||||
|
package readerdriver
|
||||||
|
|
||||||
|
// #cgo pkg-config: portaudio-2.0
|
||||||
|
// #cgo LDFLAGS: -lportaudio
|
||||||
|
//
|
||||||
|
// #include "portaudio.h"
|
||||||
|
//
|
||||||
|
// int ebiten_readerdriver_streamCallback(void *input, void *output, unsigned long frameCount, PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData);
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
type paError struct {
|
||||||
|
value C.PaError
|
||||||
|
fname string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *paError) Error() string {
|
||||||
|
return fmt.Sprintf("PortAudio %s failed: %s", e.fname, C.GoString(C.Pa_GetErrorText(e.value)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func paErrorToError(err C.PaError, fname string) error {
|
||||||
|
if err == C.paNoError {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &paError{
|
||||||
|
value: err,
|
||||||
|
fname: fname,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsAvailable() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type context struct {
|
||||||
|
sampleRate int
|
||||||
|
channelNum int
|
||||||
|
bitDepthInBytes int
|
||||||
|
|
||||||
|
stream unsafe.Pointer // *C.PaStream
|
||||||
|
players map[*playerImpl]struct{}
|
||||||
|
buf []float32
|
||||||
|
m sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewContext(sampleRate, channelNum, bitDepthInBytes int) (Context, chan struct{}, error) {
|
||||||
|
ready := make(chan struct{})
|
||||||
|
close(ready)
|
||||||
|
|
||||||
|
c := &context{
|
||||||
|
sampleRate: sampleRate,
|
||||||
|
channelNum: channelNum,
|
||||||
|
bitDepthInBytes: bitDepthInBytes,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := paErrorToError(C.Pa_Initialize(), "Pa_Initialize"); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := paErrorToError(C.Pa_OpenDefaultStream(
|
||||||
|
&c.stream,
|
||||||
|
0,
|
||||||
|
C.int(c.channelNum),
|
||||||
|
C.paFloat32,
|
||||||
|
C.double(c.sampleRate),
|
||||||
|
C.paFramesPerBufferUnspecified,
|
||||||
|
(*C.PaStreamCallback)(C.ebiten_readerdriver_streamCallback),
|
||||||
|
unsafe.Pointer(c),
|
||||||
|
), "Pa_OpenDefaultStream"); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := paErrorToError(C.Pa_StartStream(c.stream), "Pa_StartStream"); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, ready, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *context) Suspend() error {
|
||||||
|
if err := paErrorToError(C.Pa_StopStream(c.stream), "Pa_StopStream"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *context) Resume() error {
|
||||||
|
if err := paErrorToError(C.Pa_StartStream(c.stream), "Pa_StartStream"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *context) addPlayer(player *playerImpl) {
|
||||||
|
c.m.Lock()
|
||||||
|
defer c.m.Unlock()
|
||||||
|
|
||||||
|
if c.players == nil {
|
||||||
|
c.players = map[*playerImpl]struct{}{}
|
||||||
|
}
|
||||||
|
c.players[player] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *context) removePlayer(player *playerImpl) {
|
||||||
|
c.m.Lock()
|
||||||
|
defer c.m.Unlock()
|
||||||
|
delete(c.players, player)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export ebiten_readerdriver_streamCallback
|
||||||
|
func ebiten_readerdriver_streamCallback(input, output unsafe.Pointer, frameCount C.ulong, timeInfo *C.PaStreamCallbackTimeInfo, statusFlags C.PaStreamCallbackFlags, userData unsafe.Pointer) C.int {
|
||||||
|
c := (*context)(userData)
|
||||||
|
|
||||||
|
var n int
|
||||||
|
if statusFlags&(C.paOutputUnderflow|C.paOutputOverflow) == 0 {
|
||||||
|
n = int(frameCount) * c.channelNum
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(c.buf) < n {
|
||||||
|
c.buf = make([]float32, n)
|
||||||
|
} else {
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
c.buf[i] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if n > 0 {
|
||||||
|
c.m.Lock()
|
||||||
|
players := make([]*playerImpl, 0, len(c.players))
|
||||||
|
for p := range c.players {
|
||||||
|
players = append(players, p)
|
||||||
|
}
|
||||||
|
c.m.Unlock()
|
||||||
|
|
||||||
|
for _, p := range players {
|
||||||
|
p.addBuffer(c.buf[:n])
|
||||||
|
}
|
||||||
|
for i := uintptr(0); i < uintptr(n); i++ {
|
||||||
|
*(*float32)(unsafe.Pointer(uintptr(output) + 4*i)) = c.buf[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := uintptr(n); i < uintptr(frameCount); i++ {
|
||||||
|
*(*float32)(unsafe.Pointer(uintptr(output) + 4*i)) = 0
|
||||||
|
}
|
||||||
|
return C.paContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
type player struct {
|
||||||
|
p *playerImpl
|
||||||
|
}
|
||||||
|
|
||||||
|
type playerImpl struct {
|
||||||
|
context *context
|
||||||
|
src io.Reader
|
||||||
|
cond *sync.Cond
|
||||||
|
volume float64
|
||||||
|
err error
|
||||||
|
state playerState
|
||||||
|
buf []byte
|
||||||
|
hasLoop bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *context) NewPlayer(src io.Reader) Player {
|
||||||
|
p := &player{
|
||||||
|
p: &playerImpl{
|
||||||
|
context: c,
|
||||||
|
src: src,
|
||||||
|
cond: sync.NewCond(&sync.Mutex{}),
|
||||||
|
volume: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
runtime.SetFinalizer(p, (*player).Close)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *player) Err() error {
|
||||||
|
return p.p.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *playerImpl) Err() error {
|
||||||
|
p.cond.L.Lock()
|
||||||
|
defer p.cond.L.Unlock()
|
||||||
|
|
||||||
|
return p.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *player) Play() {
|
||||||
|
p.p.Play()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *playerImpl) Play() {
|
||||||
|
ch := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
p.cond.L.Lock()
|
||||||
|
defer p.cond.L.Unlock()
|
||||||
|
close(ch)
|
||||||
|
p.playImpl()
|
||||||
|
}()
|
||||||
|
<-ch
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *playerImpl) playImpl() {
|
||||||
|
if p.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if p.state != playerPaused {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, p.context.maxBufferSize())
|
||||||
|
for len(p.buf) < p.context.maxBufferSize() {
|
||||||
|
n, err := p.src.Read(buf)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
p.setErrorImpl(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.buf = append(p.buf, buf[:n]...)
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p.state = playerPlay
|
||||||
|
|
||||||
|
p.cond.L.Unlock()
|
||||||
|
p.context.addPlayer(p)
|
||||||
|
p.cond.L.Lock()
|
||||||
|
|
||||||
|
p.cond.Signal()
|
||||||
|
|
||||||
|
if !p.hasLoop {
|
||||||
|
go p.loop()
|
||||||
|
p.hasLoop = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *player) Pause() {
|
||||||
|
p.p.Pause()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *playerImpl) Pause() {
|
||||||
|
p.cond.L.Lock()
|
||||||
|
defer p.cond.L.Unlock()
|
||||||
|
p.pauseImpl()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *playerImpl) pauseImpl() {
|
||||||
|
if p.state != playerPlay {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.state = playerPaused
|
||||||
|
p.cond.Signal()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *player) Reset() {
|
||||||
|
p.p.Reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *playerImpl) Reset() {
|
||||||
|
p.cond.L.Lock()
|
||||||
|
defer p.cond.L.Unlock()
|
||||||
|
p.resetImpl()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *playerImpl) resetImpl() {
|
||||||
|
if p.state == playerClosed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.state = playerPaused
|
||||||
|
p.buf = p.buf[:0]
|
||||||
|
p.cond.Signal()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *player) IsPlaying() bool {
|
||||||
|
return p.p.IsPlaying()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *playerImpl) IsPlaying() bool {
|
||||||
|
p.cond.L.Lock()
|
||||||
|
defer p.cond.L.Unlock()
|
||||||
|
return p.state == playerPlay
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *player) Volume() float64 {
|
||||||
|
return p.p.Volume()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *playerImpl) Volume() float64 {
|
||||||
|
p.cond.L.Lock()
|
||||||
|
defer p.cond.L.Unlock()
|
||||||
|
return p.volume
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *player) SetVolume(volume float64) {
|
||||||
|
p.p.SetVolume(volume)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *playerImpl) SetVolume(volume float64) {
|
||||||
|
p.cond.L.Lock()
|
||||||
|
defer p.cond.L.Unlock()
|
||||||
|
p.volume = volume
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *player) UnplayedBufferSize() int {
|
||||||
|
return p.p.UnplayedBufferSize()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *playerImpl) UnplayedBufferSize() int {
|
||||||
|
p.cond.L.Lock()
|
||||||
|
defer p.cond.L.Unlock()
|
||||||
|
return len(p.buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *player) Close() error {
|
||||||
|
runtime.SetFinalizer(p, nil)
|
||||||
|
return p.p.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *playerImpl) Close() error {
|
||||||
|
p.cond.L.Lock()
|
||||||
|
defer p.cond.L.Unlock()
|
||||||
|
return p.closeImpl()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *playerImpl) closeImpl() error {
|
||||||
|
p.cond.L.Unlock()
|
||||||
|
p.context.removePlayer(p)
|
||||||
|
p.cond.L.Lock()
|
||||||
|
|
||||||
|
if p.state == playerClosed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
p.state = playerClosed
|
||||||
|
p.buf = nil
|
||||||
|
p.cond.Signal()
|
||||||
|
return p.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *playerImpl) addBuffer(buf []float32) int {
|
||||||
|
p.cond.L.Lock()
|
||||||
|
defer p.cond.L.Unlock()
|
||||||
|
|
||||||
|
if p.state != playerPlay {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
bitDepthInBytes := p.context.bitDepthInBytes
|
||||||
|
n := len(p.buf) / bitDepthInBytes
|
||||||
|
if n > len(buf) {
|
||||||
|
n = len(buf)
|
||||||
|
}
|
||||||
|
volume := float32(p.volume)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
var v float32
|
||||||
|
switch bitDepthInBytes {
|
||||||
|
case 1:
|
||||||
|
v8 := p.buf[i]
|
||||||
|
v = float32(v8-(1<<7)) / (1 << 7)
|
||||||
|
case 2:
|
||||||
|
v16 := int16(p.buf[2*i]) | (int16(p.buf[2*i+1]) << 8)
|
||||||
|
v = float32(v16) / (1 << 15)
|
||||||
|
}
|
||||||
|
buf[i] += v * volume
|
||||||
|
}
|
||||||
|
p.buf = p.buf[n*bitDepthInBytes:]
|
||||||
|
if n > 0 {
|
||||||
|
p.cond.Signal()
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *playerImpl) shouldWait() bool {
|
||||||
|
switch p.state {
|
||||||
|
case playerPaused:
|
||||||
|
return true
|
||||||
|
case playerPlay:
|
||||||
|
// If the buffer has too much data, wait until the buffer data is consumed.
|
||||||
|
// If the source reaches EOF, wait until the state is reset.
|
||||||
|
return len(p.buf) >= p.context.maxBufferSize()
|
||||||
|
case playerClosed:
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
panic("not reached")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *playerImpl) wait() bool {
|
||||||
|
p.cond.L.Lock()
|
||||||
|
defer p.cond.L.Unlock()
|
||||||
|
|
||||||
|
for p.shouldWait() {
|
||||||
|
p.cond.Wait()
|
||||||
|
}
|
||||||
|
return p.state == playerPlay
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *playerImpl) setError(err error) {
|
||||||
|
p.cond.L.Lock()
|
||||||
|
defer p.cond.L.Unlock()
|
||||||
|
p.setErrorImpl(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *playerImpl) setErrorImpl(err error) {
|
||||||
|
p.err = err
|
||||||
|
p.closeImpl()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *playerImpl) 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.cond.L.Lock()
|
||||||
|
p.buf = append(p.buf, buf[:n]...)
|
||||||
|
if err == io.EOF && len(p.buf) == 0 {
|
||||||
|
p.resetImpl()
|
||||||
|
}
|
||||||
|
p.cond.L.Unlock()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user