gomu/player.go

312 lines
4.9 KiB
Go
Raw Normal View History

2020-06-22 00:05:56 +08:00
// Copyright (C) 2020 Raziman
2020-06-20 23:00:19 +08:00
package main
import (
"errors"
"net/http"
"os"
"path"
"strings"
"time"
"github.com/faiface/beep"
2020-06-21 23:47:02 +08:00
"github.com/faiface/beep/effects"
2020-06-20 23:00:19 +08:00
"github.com/faiface/beep/mp3"
"github.com/faiface/beep/speaker"
2020-06-21 23:47:02 +08:00
"github.com/rivo/tview"
2020-06-20 23:00:19 +08:00
)
type Song struct {
name string
path string
position string
}
type Player struct {
2020-06-21 23:47:02 +08:00
queue []string
IsRunning bool
hasInit bool
current string
format *beep.Format
2020-06-22 21:18:42 +08:00
2020-06-23 18:42:18 +08:00
isSkipped bool
done chan bool
2020-06-21 23:47:02 +08:00
// to control the _volume internally
_volume *effects.Volume
2020-06-23 22:26:28 +08:00
ctrl *beep.Ctrl
2020-06-21 23:47:02 +08:00
volume float64
2020-06-23 18:42:18 +08:00
resampler *beep.Resampler
2020-06-21 23:47:02 +08:00
position time.Duration
length time.Duration
2020-06-20 23:00:19 +08:00
currentSong Song
2020-06-21 23:47:02 +08:00
// to access sections
list *tview.List
tree *tview.TreeView
playingBar *Progress
app *tview.Application
2020-06-20 23:00:19 +08:00
}
2020-06-22 13:18:25 +08:00
// add new song to the queue
2020-06-20 23:00:19 +08:00
func (p *Player) Push(song string) {
2020-06-21 23:47:02 +08:00
p.queue = append(p.queue, song)
2020-06-20 23:00:19 +08:00
}
2020-06-22 13:18:25 +08:00
// remove first song from the queue
2020-06-20 23:00:19 +08:00
func (p *Player) Pop() (string, error) {
2020-06-21 23:47:02 +08:00
if len(p.queue) == 0 {
2020-06-20 23:00:19 +08:00
return "", errors.New("Empty list")
}
2020-06-21 23:47:02 +08:00
a := p.queue[0]
p.queue = p.queue[1:]
2020-06-20 23:00:19 +08:00
p.current = a
return a, nil
}
2020-06-22 13:18:25 +08:00
// remove song from the queue
func (p *Player) Remove(index int) (string, error) {
2020-06-23 18:42:18 +08:00
if index > len(p.queue)-1 {
2020-06-22 13:18:25 +08:00
return "", errors.New("Index out of range")
}
removed := p.queue[index]
var rest []string
// check if given index is the last element
if index == len(p.queue)-1 {
rest = []string{}
} else {
rest = p.queue[index+1:]
}
p.queue = append(p.queue[:index], rest...)
return removed, nil
}
2020-06-21 23:47:02 +08:00
func (p *Player) Run() {
2020-06-20 23:00:19 +08:00
first, err := p.Pop()
2020-06-21 23:47:02 +08:00
// removes playing song from the queue
p.list.RemoveItem(0)
p.app.Draw()
2020-06-20 23:00:19 +08:00
if err != nil {
p.IsRunning = false
2020-06-21 23:47:02 +08:00
log(err.Error())
2020-06-23 20:15:29 +08:00
}
2020-06-20 23:00:19 +08:00
f, err := os.Open(first)
defer f.Close()
streamer, format, err := mp3.Decode(f)
2020-06-22 21:18:42 +08:00
2020-06-21 23:47:02 +08:00
// song duration
p.length = format.SampleRate.D(streamer.Len())
if !p.hasInit {
speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/10))
p.hasInit = true
}
2020-06-20 23:00:19 +08:00
p.format = &format
if err != nil {
2020-06-21 23:47:02 +08:00
log(err.Error())
2020-06-20 23:00:19 +08:00
}
defer streamer.Close()
2020-06-21 23:47:02 +08:00
if err != nil {
log(err.Error())
2020-06-20 23:00:19 +08:00
}
2020-06-21 23:47:02 +08:00
song := &Song{name: GetName(f.Name()), path: first}
p.currentSong = *song
2020-06-20 23:00:19 +08:00
done := make(chan bool)
2020-06-23 18:42:18 +08:00
p.done = done
2020-06-20 23:00:19 +08:00
sstreamer := beep.Seq(streamer, beep.Callback(func() {
2020-06-23 19:28:12 +08:00
// prevents from sending done channel if the song is skipped
2020-06-23 18:42:18 +08:00
if !p.isSkipped {
done <- true
} else {
p.isSkipped = false
}
2020-06-20 23:00:19 +08:00
}))
2020-06-21 23:47:02 +08:00
ctrl := &beep.Ctrl{Streamer: sstreamer, Paused: false}
2020-06-23 22:26:28 +08:00
p.ctrl = ctrl
2020-06-20 23:00:19 +08:00
2020-06-23 18:42:18 +08:00
resampler := beep.ResampleRatio(4, 1, ctrl)
p.resampler = resampler
2020-06-21 23:47:02 +08:00
volume := &effects.Volume{
2020-06-23 18:42:18 +08:00
Streamer: resampler,
2020-06-21 23:47:02 +08:00
Base: 2,
Volume: 0,
Silent: false,
2020-06-20 23:00:19 +08:00
}
2020-06-21 23:47:02 +08:00
// sets the volume of previous player
volume.Volume += p.volume
p._volume = volume
2020-06-20 23:00:19 +08:00
2020-06-21 23:47:02 +08:00
speaker.Play(p._volume)
position := func() time.Duration {
return format.SampleRate.D(streamer.Position())
}
p.position = position()
2020-06-20 23:00:19 +08:00
p.IsRunning = true
2020-06-21 23:47:02 +08:00
p.playingBar.NewProgress(song.name, int(p.length.Seconds()), 100)
p.playingBar.Run()
2020-06-23 18:42:18 +08:00
go func() {
2020-06-21 23:47:02 +08:00
i := 0
for {
2020-06-22 10:22:28 +08:00
2020-06-23 18:42:18 +08:00
// stop progress bar from progressing when paused
2020-06-22 10:22:28 +08:00
if !p.IsRunning {
continue
}
2020-06-21 23:47:02 +08:00
i++
p.playingBar.progress <- 1
2020-06-23 18:42:18 +08:00
if i > p.playingBar.full || p.isSkipped {
2020-06-21 23:47:02 +08:00
break
}
2020-06-23 18:42:18 +08:00
2020-06-21 23:47:02 +08:00
time.Sleep(time.Second)
}
}()
2020-06-23 18:42:18 +08:00
next:
2020-06-20 23:00:19 +08:00
for {
select {
case <-done:
2020-06-21 23:47:02 +08:00
close(done)
2020-06-23 20:15:29 +08:00
p.position = 0
p.current = ""
2020-06-22 21:18:42 +08:00
p.IsRunning = false
2020-06-23 20:15:29 +08:00
p.format = nil
2020-06-22 21:18:42 +08:00
2020-06-21 23:47:02 +08:00
if len(p.queue) != 0 {
2020-06-20 23:00:19 +08:00
go p.Run()
2020-06-23 18:42:18 +08:00
}
2020-06-22 21:18:42 +08:00
2020-06-23 18:42:18 +08:00
break next
2020-06-20 23:00:19 +08:00
case <-time.After(time.Second):
speaker.Lock()
p.position = position()
speaker.Unlock()
}
}
}
func (p *Player) Pause() {
speaker.Lock()
2020-06-23 22:26:28 +08:00
p.ctrl.Paused = true
2020-06-20 23:00:19 +08:00
p.IsRunning = false
speaker.Unlock()
}
func (p *Player) Play() {
speaker.Lock()
2020-06-23 22:26:28 +08:00
p.ctrl.Paused = false
2020-06-20 23:00:19 +08:00
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 {
2020-06-21 23:47:02 +08:00
return strings.TrimSuffix(path.Base(fn), path.Ext(fn))
}
// volume up and volume down using -0.5 or +0.5
func (p *Player) Volume(v float64) {
speaker.Lock()
p._volume.Volume += v
p.volume = p._volume.Volume
speaker.Unlock()
}
func (p *Player) TogglePause() {
2020-06-23 22:26:28 +08:00
if p.ctrl.Paused {
2020-06-21 23:47:02 +08:00
p.Play()
} else {
p.Pause()
}
}
2020-06-23 18:42:18 +08:00
// skips current song
func (p *Player) Skip() {
if len(p.queue) > 0 {
p.isSkipped = true
p.done <- true
}
}
2020-06-23 22:26:28 +08:00
func (p *Player) GetLength(index int) (time.Duration, error) {
if index > len(p.queue)-1 {
return 0, errors.New("Index out of range")
}
f, err := os.Open(p.queue[index])
defer f.Close()
if err != nil {
return 0, err
}
streamer, format, err := mp3.Decode(f)
defer streamer.Close()
if err != nil {
return 0, err
}
return format.SampleRate.D(streamer.Len()), nil
}