audio: Remove player's mutex

This commit is contained in:
Hajime Hoshi 2017-12-23 18:51:45 +09:00
parent 637ed0d965
commit 19ca74e86b

View File

@ -338,8 +338,7 @@ type Player struct {
seekedCh chan error seekedCh chan error
proceedCh chan []int16 proceedCh chan []int16
proceededCh chan proceededValues proceededCh chan proceededValues
syncCh chan func()
m sync.RWMutex
} }
type seekArgs struct { type seekArgs struct {
@ -379,6 +378,7 @@ func NewPlayer(context *Context, src ReadSeekCloser) (*Player, error) {
seekedCh: make(chan error), seekedCh: make(chan error),
proceedCh: make(chan []int16), proceedCh: make(chan []int16),
proceededCh: make(chan proceededValues), proceededCh: make(chan proceededValues),
syncCh: make(chan func()),
} }
// Get the current position of the source. // Get the current position of the source.
pos, err := p.src.Seek(0, io.SeekCurrent) pos, err := p.src.Seek(0, io.SeekCurrent)
@ -453,6 +453,8 @@ func (p *Player) readLoop() {
defer func() { defer func() {
// Note: the error is ignored // Note: the error is ignored
p.src.Close() p.src.Close()
// Receiving from a closed channel returns quickly
// i.e. `case <-p.readLoopEndedCh:` can check if this loops is ended.
close(p.readLoopEndedCh) close(p.readLoopEndedCh)
}() }()
@ -466,47 +468,36 @@ func (p *Player) readLoop() {
case s := <-p.seekCh: case s := <-p.seekCh:
pos, err := p.src.Seek(s.offset, s.whence) pos, err := p.src.Seek(s.offset, s.whence)
p.m.Lock()
p.buf = nil p.buf = nil
p.pos = pos p.pos = pos
p.srcEOF = false p.srcEOF = false
p.m.Unlock()
p.seekedCh <- err p.seekedCh <- err
t = time.After(time.Millisecond) t = time.After(time.Millisecond)
break break
case <-t: case <-t:
p.m.Lock()
if len(p.buf) >= 4096*16 { if len(p.buf) >= 4096*16 {
t = time.After(10 * time.Millisecond) t = time.After(10 * time.Millisecond)
p.m.Unlock()
break break
} }
p.m.Unlock()
buf := make([]byte, 4096) buf := make([]byte, 4096)
n, err := p.src.Read(buf) n, err := p.src.Read(buf)
p.m.Lock()
p.buf = append(p.buf, buf[:n]...) p.buf = append(p.buf, buf[:n]...)
if err == io.EOF { if err == io.EOF {
p.srcEOF = true p.srcEOF = true
} }
if p.srcEOF && len(p.buf) == 0 { if p.srcEOF && len(p.buf) == 0 {
t = nil t = nil
p.m.Unlock()
break break
} }
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
readErr = err readErr = err
t = nil t = nil
p.m.Unlock()
break break
} }
t = time.After(time.Millisecond) t = time.After(time.Millisecond)
p.m.Unlock()
case buf := <-p.proceedCh: case buf := <-p.proceedCh:
if readErr != nil { if readErr != nil {
@ -517,12 +508,9 @@ func (p *Player) readLoop() {
lengthInBytes := len(buf) * 2 lengthInBytes := len(buf) * 2
l := lengthInBytes l := lengthInBytes
p.m.Lock()
// Buffer size needs to be much more than the actual required length // Buffer size needs to be much more than the actual required length
// so that noise caused by empty buffer can be avoided. // so that noise caused by empty buffer can be avoided.
if len(p.buf) < lengthInBytes*4 && !p.srcEOF { if len(p.buf) < lengthInBytes*4 && !p.srcEOF {
p.m.Unlock()
p.proceededCh <- proceededValues{buf, nil} p.proceededCh <- proceededValues{buf, nil}
break break
} }
@ -536,16 +524,34 @@ func (p *Player) readLoop() {
p.pos += int64(l) p.pos += int64(l)
p.buf = p.buf[l:] p.buf = p.buf[l:]
p.m.Unlock()
p.proceededCh <- proceededValues{buf, nil} p.proceededCh <- proceededValues{buf, nil}
case f := <-p.syncCh:
f()
} }
} }
} }
func (p *Player) sync(f func()) bool {
ch := make(chan struct{})
ff := func() {
f()
close(ch)
}
select {
case p.syncCh <- ff:
<-ch
return true
case <-p.readLoopEndedCh:
return false
}
}
func (p *Player) eof() bool { func (p *Player) eof() bool {
p.m.RLock() r := false
r := p.srcEOF && len(p.buf) == 0 p.sync(func() {
p.m.RUnlock() r = p.srcEOF && len(p.buf) == 0
})
return r return r
} }
@ -585,18 +591,19 @@ func (p *Player) Pause() error {
// Current returns the current position. // Current returns the current position.
func (p *Player) Current() time.Duration { func (p *Player) Current() time.Duration {
p.m.RLock() sample := int64(0)
sample := p.pos / bytesPerSample / channelNum p.sync(func() {
p.m.RUnlock() sample = p.pos / bytesPerSample / channelNum
})
return time.Duration(sample) * time.Second / time.Duration(p.sampleRate) return time.Duration(sample) * time.Second / time.Duration(p.sampleRate)
} }
// Volume returns the current volume of this player [0-1]. // Volume returns the current volume of this player [0-1].
func (p *Player) Volume() float64 { func (p *Player) Volume() float64 {
p.m.RLock() v := 0.0
v := p.volume p.sync(func() {
p.m.RUnlock() v = p.volume
})
return v return v
} }
@ -608,7 +615,7 @@ func (p *Player) SetVolume(volume float64) {
panic("audio: volume must be in between 0 and 1") panic("audio: volume must be in between 0 and 1")
} }
p.m.Lock() p.sync(func() {
p.volume = volume p.volume = volume
p.m.Unlock() })
} }