audio: NewPlayer now accepts io.ReadCloser

Fixes #414
This commit is contained in:
Hajime Hoshi 2018-03-22 00:33:17 +09:00
parent 9009b293e5
commit 18113f259a
2 changed files with 28 additions and 27 deletions

View File

@ -132,7 +132,7 @@ func (p *players) hasPlayer(player *Player) bool {
return ok return ok
} }
func (p *players) hasSource(src ReadSeekCloser) bool { func (p *players) hasSource(src io.ReadCloser) bool {
p.RLock() p.RLock()
defer p.RUnlock() defer p.RUnlock()
for player := range p.players { for player := range p.players {
@ -328,7 +328,7 @@ func BytesReadSeekCloser(b []byte) ReadSeekCloser {
// Player is an audio player which has one stream. // Player is an audio player which has one stream.
type Player struct { type Player struct {
players *players players *players
src ReadSeekCloser src io.ReadCloser
srcEOF bool srcEOF bool
sampleRate int sampleRate int
@ -362,11 +362,14 @@ type proceededValues struct {
// without a header (e.g. RIFF header). // without a header (e.g. RIFF header).
// The sample rate must be same as that of the audio context. // The sample rate must be same as that of the audio context.
// //
// The player is seekable when src is io.Seeker.
// Attempt to seek the player that is not io.Seeker causes panic.
//
// Note that the given src can't be shared with other Player objects. // Note that the given src can't be shared with other Player objects.
// //
// NewPlayer tries to call Seek of src to get the current position. // NewPlayer tries to call Seek of src to get the current position.
// NewPlayer returns error when the Seek returns error. // NewPlayer returns error when the Seek returns error.
func NewPlayer(context *Context, src ReadSeekCloser) (*Player, error) { func NewPlayer(context *Context, src io.ReadCloser) (*Player, error) {
if context.players.hasSource(src) { if context.players.hasSource(src) {
return nil, errors.New("audio: src cannot be shared with another Player") return nil, errors.New("audio: src cannot be shared with another Player")
} }
@ -385,12 +388,14 @@ func NewPlayer(context *Context, src ReadSeekCloser) (*Player, error) {
proceededCh: make(chan proceededValues), proceededCh: make(chan proceededValues),
syncCh: make(chan func()), syncCh: make(chan func()),
} }
// Get the current position of the source. if seeker, ok := p.src.(io.Seeker); ok {
pos, err := p.src.Seek(0, io.SeekCurrent) // Get the current position of the source.
if err != nil { pos, err := seeker.Seek(0, io.SeekCurrent)
return nil, err if err != nil {
return nil, err
}
p.pos = pos
} }
p.pos = pos
runtime.SetFinalizer(p, (*Player).Close) runtime.SetFinalizer(p, (*Player).Close)
go func() { go func() {
@ -473,7 +478,11 @@ func (p *Player) readLoop() {
return return
case s := <-p.seekCh: case s := <-p.seekCh:
pos, err := p.src.Seek(s.offset, s.whence) seeker, ok := p.src.(io.Seeker)
if !ok {
panic("not reached")
}
pos, err := seeker.Seek(s.offset, s.whence)
p.buf = nil p.buf = nil
p.pos = pos p.pos = pos
p.srcEOF = false p.srcEOF = false
@ -600,15 +609,25 @@ func (p *Player) IsPlaying() bool {
// Rewind rewinds the current position to the start. // Rewind rewinds the current position to the start.
// //
// The passed source to NewPlayer must be io.Seeker, or Rewind panics.
//
// Rewind returns error when seeking the source stream returns error. // Rewind returns error when seeking the source stream returns error.
func (p *Player) Rewind() error { func (p *Player) Rewind() error {
if _, ok := p.src.(io.Seeker); !ok {
panic("audio: player to be rewinded must be io.Seeker")
}
return p.Seek(0) return p.Seek(0)
} }
// Seek seeks the position with the given offset. // Seek seeks the position with the given offset.
// //
// The passed source to NewPlayer must be io.Seeker, or Seek panics.
//
// Seek returns error when seeking the source stream returns error. // Seek returns error when seeking the source stream returns error.
func (p *Player) Seek(offset time.Duration) error { func (p *Player) Seek(offset time.Duration) error {
if _, ok := p.src.(io.Seeker); !ok {
panic("audio: player to be sought must be io.Seeker")
}
o := int64(offset) * bytesPerSample * channelNum * int64(p.sampleRate) / int64(time.Second) o := int64(offset) * bytesPerSample * channelNum * int64(p.sampleRate) / int64(time.Second)
o &= mask o &= mask
select { select {

View File

@ -19,7 +19,6 @@ package main
import ( import (
"errors" "errors"
"fmt" "fmt"
"io"
"log" "log"
"math" "math"
@ -73,23 +72,6 @@ func (s *stream) Read(data []byte) (int, error) {
return len(data), nil return len(data), nil
} }
// Seek is io.Seeker's Seek.
//
// whence must be io.SeekStart or io.SeekCurrent.
func (s *stream) Seek(offset int64, whence int) (int64, error) {
const length = sampleRate / frequency
switch whence {
case io.SeekStart:
s.position = offset
case io.SeekCurrent:
s.position += offset
default:
return 0, errors.New("whence must be io.SeekStart or io.SeekCurrent")
}
s.position %= length * 4
return s.position, nil
}
// Close is io.Closer's Close. // Close is io.Closer's Close.
func (s *stream) Close() error { func (s *stream) Close() error {
return nil return nil