Make the jukebox less of a heap
This commit is contained in:
@@ -348,17 +348,14 @@ func (c *Controller) ServeJukebox(r *http.Request) *spec.Response {
|
|||||||
c.Jukebox.Start()
|
c.Jukebox.Start()
|
||||||
case "skip":
|
case "skip":
|
||||||
index, err := params.GetInt("index")
|
index, err := params.GetInt("index")
|
||||||
var skipCurrent bool
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
skipCurrent = true
|
return spec.NewError(10, "please provide an index for skip actions")
|
||||||
}
|
}
|
||||||
c.Jukebox.Skip(index, skipCurrent)
|
c.Jukebox.Skip(index)
|
||||||
case "get":
|
case "get":
|
||||||
sub := spec.NewResponse()
|
sub := spec.NewResponse()
|
||||||
sub.JukeboxPlaylist = c.Jukebox.GetTracks()
|
sub.JukeboxPlaylist = c.Jukebox.GetTracks()
|
||||||
return sub
|
return sub
|
||||||
default:
|
|
||||||
return spec.NewError(10, "unknown value `%s` for parameter 'action'", act)
|
|
||||||
}
|
}
|
||||||
// all actions except get are expected to return a status
|
// all actions except get are expected to return a status
|
||||||
sub := spec.NewResponse()
|
sub := spec.NewResponse()
|
||||||
|
|||||||
@@ -29,18 +29,42 @@ type Jukebox struct {
|
|||||||
playing bool
|
playing bool
|
||||||
sr beep.SampleRate
|
sr beep.SampleRate
|
||||||
// used to notify the player to re read the members
|
// used to notify the player to re read the members
|
||||||
updates chan struct{}
|
updates chan update
|
||||||
quit chan struct{}
|
quit chan struct{}
|
||||||
done chan bool
|
done chan bool
|
||||||
info *strmInfo
|
info *strmInfo
|
||||||
|
speaker chan updateSpeaker
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type updateType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
set updateType = "set"
|
||||||
|
clear updateType = "clear"
|
||||||
|
skip updateType = "skip"
|
||||||
|
add updateType = "add"
|
||||||
|
remove updateType = "remove"
|
||||||
|
stop updateType = "stop"
|
||||||
|
start updateType = "start"
|
||||||
|
)
|
||||||
|
|
||||||
|
type update struct {
|
||||||
|
action updateType
|
||||||
|
index int
|
||||||
|
tracks []*db.Track
|
||||||
|
}
|
||||||
|
|
||||||
|
type updateSpeaker struct {
|
||||||
|
index int
|
||||||
|
}
|
||||||
|
|
||||||
func New(musicPath string) *Jukebox {
|
func New(musicPath string) *Jukebox {
|
||||||
return &Jukebox{
|
return &Jukebox{
|
||||||
musicPath: musicPath,
|
musicPath: musicPath,
|
||||||
sr: beep.SampleRate(48000),
|
sr: beep.SampleRate(48000),
|
||||||
updates: make(chan struct{}),
|
updates: make(chan update),
|
||||||
|
speaker: make(chan updateSpeaker, 1),
|
||||||
done: make(chan bool),
|
done: make(chan bool),
|
||||||
quit: make(chan struct{}),
|
quit: make(chan struct{}),
|
||||||
}
|
}
|
||||||
@@ -54,8 +78,10 @@ func (j *Jukebox) Listen() error {
|
|||||||
select {
|
select {
|
||||||
case <-j.quit:
|
case <-j.quit:
|
||||||
return nil
|
return nil
|
||||||
case <-j.updates:
|
case update := <-j.updates:
|
||||||
j.doUpdate()
|
j.doUpdate(update)
|
||||||
|
case speaker := <-j.speaker:
|
||||||
|
j.doUpdateSpeaker(speaker)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,40 +90,76 @@ func (j *Jukebox) Quit() {
|
|||||||
j.quit <- struct{}{}
|
j.quit <- struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *Jukebox) doUpdate() {
|
func (j *Jukebox) doUpdate(u update) {
|
||||||
var streamer beep.Streamer
|
|
||||||
var format beep.Format
|
|
||||||
if j.index >= len(j.playlist) {
|
|
||||||
j.Lock()
|
j.Lock()
|
||||||
j.index = 0
|
switch u.action {
|
||||||
j.playing = false
|
case set:
|
||||||
|
if j.playing {
|
||||||
|
speaker.Clear()
|
||||||
|
}
|
||||||
|
speaker.Clear()
|
||||||
|
if len(u.tracks) == 0 {
|
||||||
|
j.playlist = []*db.Track{}
|
||||||
j.Unlock()
|
j.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
j.Lock()
|
j.playlist = u.tracks
|
||||||
|
j.index = 0
|
||||||
|
j.playing = true
|
||||||
|
j.Unlock()
|
||||||
|
j.speaker <- updateSpeaker{j.index}
|
||||||
|
case clear:
|
||||||
|
speaker.Clear()
|
||||||
|
j.playing = false
|
||||||
|
j.playlist = []*db.Track{}
|
||||||
|
j.Unlock()
|
||||||
|
case skip:
|
||||||
|
speaker.Clear()
|
||||||
|
j.index = u.index
|
||||||
|
j.Unlock()
|
||||||
|
j.speaker <- updateSpeaker{j.index}
|
||||||
|
case add:
|
||||||
|
j.playlist = append(j.playlist, u.tracks...)
|
||||||
|
case remove:
|
||||||
|
if u.index < 0 || u.index > len(j.playlist) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
j.playlist = append(j.playlist[:u.index], j.playlist[u.index+1:]...)
|
||||||
|
j.Unlock()
|
||||||
|
case stop:
|
||||||
|
j.playing = false
|
||||||
|
j.info.ctrlStrmr.Paused = true
|
||||||
|
j.Unlock()
|
||||||
|
case start:
|
||||||
|
j.playing = true
|
||||||
|
j.info.ctrlStrmr.Paused = false
|
||||||
|
j.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *Jukebox) doUpdateSpeaker(su updateSpeaker) error {
|
||||||
|
if su.index > len(j.playlist)-1 {
|
||||||
|
j.playing = false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
f, err := os.Open(path.Join(
|
f, err := os.Open(path.Join(
|
||||||
j.musicPath,
|
j.musicPath,
|
||||||
j.playlist[j.index].RelPath(),
|
j.playlist[su.index].RelPath(),
|
||||||
))
|
))
|
||||||
j.Unlock()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
j.incIndex()
|
return err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
switch j.playlist[j.index].Ext() {
|
var streamer beep.Streamer
|
||||||
|
var format beep.Format
|
||||||
|
switch j.playlist[su.index].Ext() {
|
||||||
case "mp3":
|
case "mp3":
|
||||||
streamer, format, err = mp3.Decode(f)
|
streamer, format, err = mp3.Decode(f)
|
||||||
case "flac":
|
case "flac":
|
||||||
streamer, format, err = flac.Decode(f)
|
streamer, format, err = flac.Decode(f)
|
||||||
default:
|
|
||||||
j.incIndex()
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
j.incIndex()
|
return err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if j.playing {
|
|
||||||
j.Lock()
|
j.Lock()
|
||||||
{
|
{
|
||||||
j.info = &strmInfo{}
|
j.info = &strmInfo{}
|
||||||
@@ -110,79 +172,49 @@ func (j *Jukebox) doUpdate() {
|
|||||||
}
|
}
|
||||||
j.Unlock()
|
j.Unlock()
|
||||||
speaker.Play(beep.Seq(&j.info.ctrlStrmr, beep.Callback(func() {
|
speaker.Play(beep.Seq(&j.info.ctrlStrmr, beep.Callback(func() {
|
||||||
j.done <- false
|
j.speaker <- updateSpeaker{su.index + 1}
|
||||||
})))
|
})))
|
||||||
if v := <-j.done; v {
|
return nil
|
||||||
return
|
|
||||||
}
|
|
||||||
j.Lock()
|
|
||||||
j.index++
|
|
||||||
if j.index >= len(j.playlist) {
|
|
||||||
j.index = 0
|
|
||||||
j.playing = false
|
|
||||||
j.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
j.Unlock()
|
|
||||||
// in a go routine as otherwise this hangs as the
|
|
||||||
go func() {
|
|
||||||
j.updates <- struct{}{}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (j *Jukebox) incIndex() {
|
|
||||||
j.Lock()
|
|
||||||
defer j.Unlock()
|
|
||||||
j.index++
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *Jukebox) SetTracks(tracks []*db.Track) {
|
func (j *Jukebox) SetTracks(tracks []*db.Track) {
|
||||||
j.Lock()
|
j.updates <- update{
|
||||||
defer j.Unlock()
|
action: set,
|
||||||
j.index = 0
|
tracks: tracks,
|
||||||
if len(tracks) == 0 {
|
|
||||||
if j.playing {
|
|
||||||
j.done <- true
|
|
||||||
}
|
}
|
||||||
j.playing = false
|
|
||||||
j.playlist = []*db.Track{}
|
|
||||||
speaker.Clear()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if j.playing {
|
|
||||||
j.playlist = tracks
|
|
||||||
j.done <- true
|
|
||||||
speaker.Clear()
|
|
||||||
j.updates <- struct{}{}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
j.playlist = tracks
|
|
||||||
j.playing = true
|
|
||||||
j.updates <- struct{}{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *Jukebox) AddTracks(tracks []*db.Track) {
|
func (j *Jukebox) AddTracks(tracks []*db.Track) {
|
||||||
j.Lock()
|
j.updates <- update{
|
||||||
j.playlist = append(j.playlist, tracks...)
|
action: add,
|
||||||
j.Unlock()
|
tracks: tracks,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *Jukebox) ClearTracks() {
|
|
||||||
j.Lock()
|
|
||||||
j.index = 0
|
|
||||||
j.playing = false
|
|
||||||
j.playlist = []*db.Track{}
|
|
||||||
j.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *Jukebox) RemoveTrack(i int) {
|
func (j *Jukebox) RemoveTrack(i int) {
|
||||||
j.Lock()
|
j.updates <- update{
|
||||||
defer j.Unlock()
|
action: remove,
|
||||||
if i < 0 || i > len(j.playlist) {
|
index: i,
|
||||||
return
|
|
||||||
}
|
}
|
||||||
j.playlist = append(j.playlist[:i], j.playlist[i+1:]...)
|
}
|
||||||
|
|
||||||
|
func (j *Jukebox) Skip(i int) {
|
||||||
|
j.updates <- update{
|
||||||
|
action: skip,
|
||||||
|
index: i,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *Jukebox) ClearTracks() {
|
||||||
|
j.updates <- update{action: clear}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *Jukebox) Stop() {
|
||||||
|
j.updates <- update{action: stop}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *Jukebox) Start() {
|
||||||
|
j.updates <- update{action: start}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *Jukebox) Status() *spec.JukeboxStatus {
|
func (j *Jukebox) Status() *spec.JukeboxStatus {
|
||||||
@@ -217,35 +249,3 @@ func (j *Jukebox) GetTracks() *spec.JukeboxPlaylist {
|
|||||||
}
|
}
|
||||||
return jb
|
return jb
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *Jukebox) Stop() {
|
|
||||||
j.Lock()
|
|
||||||
j.playing = false
|
|
||||||
j.info.ctrlStrmr.Paused = true
|
|
||||||
j.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (j *Jukebox) Start() {
|
|
||||||
j.Lock()
|
|
||||||
j.playing = true
|
|
||||||
j.info.ctrlStrmr.Paused = false
|
|
||||||
j.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (j *Jukebox) Skip(i int, skipCurrent bool) {
|
|
||||||
j.Lock()
|
|
||||||
defer j.Unlock()
|
|
||||||
if i == j.index {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if skipCurrent {
|
|
||||||
j.index++
|
|
||||||
} else {
|
|
||||||
j.index = i
|
|
||||||
}
|
|
||||||
speaker.Clear()
|
|
||||||
if j.playing {
|
|
||||||
j.done <- true
|
|
||||||
}
|
|
||||||
j.updates <- struct{}{}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user