Skip to content

Commit

Permalink
Improve seeking with big output device buffers
Browse files Browse the repository at this point in the history
  • Loading branch information
devgianlu committed Oct 12, 2023
1 parent cf3e72f commit ca63705
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 8 deletions.
4 changes: 2 additions & 2 deletions cmd/daemon/controls.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,11 @@ func (s *Session) pause() error {
return fmt.Errorf("no stream")
}

s.stream.Pause()

streamPos := s.stream.PositionMs()
log.Debugf("pause track at %dms", streamPos)

s.stream.Pause()

s.state.playerState.Timestamp = time.Now().UnixMilli()
s.state.playerState.PositionAsOfTimestamp = streamPos
s.state.playerState.IsPaused = true
Expand Down
36 changes: 36 additions & 0 deletions output/driver_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,42 @@ func (out *output) Resume() error {
return nil
}

func (out *output) Drop() error {
out.cond.L.Lock()
defer out.cond.L.Unlock()

if out.closed || out.released {
return nil
}

if err := C.snd_pcm_drop(out.handle); err < 0 {
return alsaError("snd_pcm_drop", err)
}

// since we are not actually stopping the stream, prepare it again
if err := C.snd_pcm_prepare(out.handle); err < 0 {
return alsaError("snd_pcm_prepare", err)
}

return nil
}

func (out *output) DelayMs() (int64, error) {
out.cond.L.Lock()
defer out.cond.L.Unlock()

if out.closed || out.released {
return 0, nil
}

var frames C.snd_pcm_sframes_t
if err := C.snd_pcm_delay(out.handle, &frames); err < 0 {
return 0, alsaError("snd_pcm_delay", err)
}

return int64(frames) * 1000 / int64(out.sampleRate), nil
}

func (out *output) SetVolume(vol float32) {
if vol < 0 || vol > 1 {
panic(fmt.Sprintf("invalid volume value: %0.2f", vol))
Expand Down
10 changes: 10 additions & 0 deletions output/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ func (c *Output) Resume() error {
return c.output.Resume()
}

// Drop empties the audio buffer without waiting.
func (c *Output) Drop() error {
return c.output.Drop()
}

// DelayMs returns the output device delay in milliseconds.
func (c *Output) DelayMs() (int64, error) {
return c.output.DelayMs()
}

// SetVolume sets the volume (0-1).
func (c *Output) SetVolume(vol float32) {
c.output.SetVolume(vol)
Expand Down
21 changes: 16 additions & 5 deletions player/player.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,15 +143,26 @@ loop:
cmd.resp <- struct{}{}
p.ev <- Event{Type: EventTypeStopped}
case playerCmdSeek:
if source != nil {
err := source.SetPositionMs(cmd.data.(int64))
cmd.resp <- err
if source != nil && out != nil {
if err := source.SetPositionMs(cmd.data.(int64)); err != nil {
cmd.resp <- err
} else if err := out.Drop(); err != nil {
cmd.resp <- err
} else {
cmd.resp <- nil
}
} else {
cmd.resp <- nil
}
case playerCmdPosition:
if source != nil {
cmd.resp <- source.PositionMs()
if source != nil && out != nil {
delay, err := out.DelayMs()
if err != nil {
log.WithError(err).Warnf("failed getting output device delay")
delay = 0
}

cmd.resp <- source.PositionMs() - delay
} else {
cmd.resp <- int64(0)
}
Expand Down
2 changes: 1 addition & 1 deletion vorbis/decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -366,10 +366,10 @@ func (d *Decoder) SetPositionMs(pos int64) (err error) {
}
}

d.buf = nil
return nil
}

func (d *Decoder) PositionMs() int64 {
// TODO: account for alsa delay with snd_pcm_delay
return int64(vorbis.GranuleTime(&d.dspState, d.lastGranulepos) * 1000)
}

0 comments on commit ca63705

Please sign in to comment.