gomu/player.go
2020-06-20 23:00:19 +08:00

182 lines
2.7 KiB
Go

package main
import (
"errors"
"net/http"
"os"
"path"
"strings"
"time"
"github.com/faiface/beep"
"github.com/faiface/beep/mp3"
"github.com/faiface/beep/speaker"
)
type Song struct {
name string
path string
position string
format string
}
type Player struct {
list []string
IsRunning bool
hasInit bool
ctrl *beep.Ctrl
current string
format *beep.Format
position string
currentSong Song
}
func Init(paths []string) (*Player, error) {
p := &Player{list: paths, IsRunning: false, hasInit: false}
if len(paths) == 0 {
return nil, errors.New("Cannot play with empty list")
}
return p, nil
}
func (p *Player) Push(song string) {
p.list = append(p.list, song)
}
func (p *Player) Pop() (string, error) {
if len(p.list) == 0 {
return "", errors.New("Empty list")
}
a := p.list[0]
p.list = p.list[1:]
p.current = a
return a, nil
}
func (p *Player) Run() error {
first, err := p.Pop()
if err != nil {
p.IsRunning = false
return err
}
f, err := os.Open(first)
fformat, err := GetFileContentType(f)
if err != nil {
return err
}
song := &Song{name: GetName(f.Name()), path: first, format: fformat}
p.currentSong = *song
if err != nil {
return err
}
defer f.Close()
streamer, format, err := mp3.Decode(f)
p.format = &format
if err != nil {
return err
}
defer streamer.Close()
if !p.hasInit {
speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/10))
p.hasInit = true
}
done := make(chan bool)
sstreamer := beep.Seq(streamer, beep.Callback(func() {
done <- true
close(done)
}))
p.ctrl = &beep.Ctrl{Streamer: sstreamer, Paused: false}
speaker.Play(p.ctrl)
position := func() string {
return format.SampleRate.D(streamer.Position()).Round(time.Second).String()
}
p.position = position()
p.IsRunning = true
for {
select {
case <-done:
p.position = ""
p.current = ""
if len(p.list) != 0 {
go p.Run()
} else {
p.IsRunning = false
p.format = nil
}
goto next
case <-time.After(time.Second):
speaker.Lock()
p.position = position()
speaker.Unlock()
}
}
next:
return nil
}
func (p *Player) Pause() {
speaker.Lock()
p.ctrl.Paused = true
p.IsRunning = false
speaker.Unlock()
}
func (p *Player) Play() {
speaker.Lock()
p.ctrl.Paused = false
p.IsRunning = true
speaker.Unlock()
}
func (p *Player) CurrentSong() Song {
return p.currentSong
}
func GetFileContentType(out *os.File) (string, error) {
buffer := make([]byte, 512)
_, err := out.Read(buffer)
if err != nil {
return "", err
}
contentType := http.DetectContentType(buffer)
return strings.SplitAfter(contentType, "/")[1], nil
}
func GetName(fn string) string {
return strings.TrimSuffix(fn, path.Ext(fn))
}