implement progress bar

This commit is contained in:
raziman 2020-06-21 23:47:02 +08:00
parent 8c41ff87e2
commit d73bcb66e4
2 changed files with 228 additions and 62 deletions

184
player.go
View File

@ -9,121 +9,158 @@ import (
"time"
"github.com/faiface/beep"
"github.com/faiface/beep/effects"
"github.com/faiface/beep/mp3"
"github.com/faiface/beep/speaker"
"github.com/rivo/tview"
)
type Song struct {
name string
path string
position string
format string
}
type handler func(list *tview.List, tree *tview.TreeView, playingBar *tview.Box)
type Player struct {
list []string
IsRunning bool
hasInit bool
ctrl *beep.Ctrl
current string
format *beep.Format
position string
queue []string
IsRunning bool
hasInit bool
current string
format *beep.Format
// to control the _volume internally
_volume *effects.Volume
volume float64
position time.Duration
length time.Duration
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
// to access sections
list *tview.List
tree *tview.TreeView
playingBar *Progress
app *tview.Application
afterPlayHandler handler
beforePlayHandler handler
}
func (p *Player) Push(song string) {
p.list = append(p.list, song)
p.queue = append(p.queue, song)
}
func (p *Player) Pop() (string, error) {
if len(p.list) == 0 {
if len(p.queue) == 0 {
return "", errors.New("Empty list")
}
a := p.list[0]
p.list = p.list[1:]
a := p.queue[0]
p.queue = p.queue[1:]
p.current = a
return a, nil
}
func (p *Player) Run() error {
func (p *Player) Run() {
first, err := p.Pop()
// removes playing song from the queue
p.list.RemoveItem(0)
p.app.Draw()
if err != nil {
p.IsRunning = false
return err
log(err.Error())
}
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()
// 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
}
p.format = &format
if err != nil {
log(err.Error())
}
defer streamer.Close()
if err != nil {
log(err.Error())
}
song := &Song{name: GetName(f.Name()), path: first}
p.currentSong = *song
done := make(chan bool)
sstreamer := beep.Seq(streamer, beep.Callback(func() {
done <- true
close(done)
}))
p.ctrl = &beep.Ctrl{Streamer: sstreamer, Paused: false}
ctrl := &beep.Ctrl{Streamer: sstreamer, Paused: false}
speaker.Play(p.ctrl)
position := func() string {
return format.SampleRate.D(streamer.Position()).Round(time.Second).String()
volume := &effects.Volume{
Streamer: ctrl,
Base: 2,
Volume: 0,
Silent: false,
}
// sets the volume of previous player
volume.Volume += p.volume
p._volume = volume
speaker.Play(p._volume)
position := func() time.Duration {
return format.SampleRate.D(streamer.Position())
}
p.position = position()
p.IsRunning = true
p.playingBar.NewProgress(song.name, int(p.length.Seconds()), 100)
p.playingBar.Run()
go func () {
i := 0
for {
i++
p.playingBar.progress <- 1
if i > p.playingBar.full {
break
}
time.Sleep(time.Second)
}
}()
for {
select {
case <-done:
p.position = ""
close(done)
p.playingBar._progress = 0
p.position = 0
p.current = ""
if len(p.list) != 0 {
if len(p.queue) != 0 {
go p.Run()
} else {
p.IsRunning = false
@ -139,20 +176,19 @@ func (p *Player) Run() error {
next:
return nil
}
func (p *Player) Pause() {
speaker.Lock()
p.ctrl.Paused = true
p._volume.Streamer.(*beep.Ctrl).Paused = true
p.IsRunning = false
speaker.Unlock()
}
func (p *Player) Play() {
speaker.Lock()
p.ctrl.Paused = false
p._volume.Streamer.(*beep.Ctrl).Paused = false
p.IsRunning = true
speaker.Unlock()
}
@ -161,7 +197,6 @@ func (p *Player) CurrentSong() Song {
return p.currentSong
}
func GetFileContentType(out *os.File) (string, error) {
buffer := make([]byte, 512)
@ -177,5 +212,38 @@ func GetFileContentType(out *os.File) (string, error) {
}
func GetName(fn string) string {
return strings.TrimSuffix(fn, path.Ext(fn))
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() {
if p._volume.Streamer.(*beep.Ctrl).Paused {
p.Play()
} else {
p.Pause()
}
}
func (p *Player) BeforePlayHook(handler func(
list *tview.List,
tree *tview.TreeView,
playingBar *tview.Box),
) {
p.beforePlayHandler = handler
}
func (p *Player) AfterPlayHook(handler func(
list *tview.List,
tree *tview.TreeView,
playingBar *tview.Box),
) {
p.afterPlayHandler = handler
}

View File

@ -1,8 +1,106 @@
package main
import "github.com/rivo/tview"
import (
"fmt"
"strconv"
"strings"
"time"
func NowPlayingBar() *tview.Box {
return tview.NewBox().SetBorder(true).
SetTitle("Currently Playing")
"github.com/gdamore/tcell"
"github.com/rivo/tview"
)
func PlayingBar(app *tview.Application) *Progress {
textView := tview.NewTextView().
SetChangedFunc(func () {
app.Draw()
})
progress := InitProgressBar(textView)
return progress
}
type Progress struct {
textView *tview.TextView
full int
limit int
progress chan int
frame *tview.Frame
_progress int
}
// full is the maximum amount of value can be sent to channel
// limit is the progress bar size
func InitProgressBar(txt *tview.TextView) *Progress {
p := &Progress{textView: txt}
p.progress = make(chan int)
p.textView.SetTextAlign(tview.AlignCenter)
p.frame = tview.NewFrame(p.textView).SetBorders(1, 1, 1, 1, 1, 1)
p.frame.SetBorder(true).SetTitle("Now Playing")
p.SetSongTitle("-")
p.textView.SetText(fmt.Sprintf("%s %s %s", "0s", strings.Repeat("□", 100), "0s"))
return p
}
func (p *Progress) Run() {
go func() { // Simple channel status gauge (progress bar)
for {
p._progress += <-p.progress
p.textView.Clear()
if p._progress > p.full {
p._progress = 0
break
}
start, err := time.ParseDuration(strconv.Itoa(p._progress) + "s")
if err != nil {
panic(err)
}
end, err := time.ParseDuration(strconv.Itoa(p.full) + "s")
if err != nil {
panic(err)
}
x := p._progress * p.limit / p.full
p.textView.SetText(fmt.Sprintf("%s %s%s %s",
start.String(),
strings.Repeat("■", x),
strings.Repeat("□", p.limit-x),
end.String(),
))
}
}()
}
func (p *Progress) SetSongTitle(title string) {
p.frame.Clear()
p.frame.AddText(title, true, tview.AlignCenter, tcell.ColorGreen)
}
func (p *Progress) NewProgress(songTitle string, full, limit int) {
p.full = full
p.limit = limit
p._progress = 0
p.SetSongTitle(songTitle)
}