gomu/player/player.go

319 lines
6.0 KiB
Go
Raw Normal View History

// Package player is the place actually play the music
2021-02-26 11:18:58 +08:00
package player
2020-06-20 23:00:19 +08:00
import (
"os"
"sync"
2020-06-20 23:00:19 +08:00
"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-07-21 12:22:00 +08:00
"github.com/ztrue/tracerr"
2020-06-20 23:00:19 +08:00
)
2021-02-26 11:18:58 +08:00
type Audio interface {
Name() string
Path() string
}
2020-06-20 23:00:19 +08:00
type Player struct {
2020-06-21 23:47:02 +08:00
hasInit bool
2020-07-23 15:15:39 +08:00
isRunning bool
2021-02-19 09:56:01 +08:00
volume float64
2020-06-23 18:42:18 +08:00
2021-02-25 16:44:42 +08:00
vol *effects.Volume
2021-02-03 10:50:01 +08:00
ctrl *beep.Ctrl
2021-02-19 09:56:01 +08:00
format *beep.Format
2021-02-03 10:50:01 +08:00
length time.Duration
2021-02-26 11:18:58 +08:00
currentSong Audio
2021-02-03 10:50:01 +08:00
streamSeekCloser beep.StreamSeekCloser
2021-02-26 11:18:58 +08:00
2021-02-26 15:26:23 +08:00
songFinish func(Audio)
songStart func(Audio)
songSkip func(Audio)
mu sync.Mutex
2021-02-03 10:50:01 +08:00
}
2021-01-27 01:10:48 +08:00
2021-02-27 11:53:47 +08:00
// New returns new Player instance.
2021-02-26 11:18:58 +08:00
func New(volume int) *Player {
2020-08-02 16:36:47 +08:00
2020-07-11 12:47:28 +08:00
// Read initial volume from config
2021-02-26 11:18:58 +08:00
initVol := AbsVolume(volume)
// making sure user does not give invalid volume
2020-08-02 16:36:47 +08:00
if volume > 100 || volume < 0 {
initVol = 0
}
2020-06-20 23:00:19 +08:00
2020-07-11 12:47:28 +08:00
return &Player{volume: initVol}
}
2020-06-21 23:47:02 +08:00
2021-02-27 11:53:47 +08:00
// SetSongFinish accepts callback which will be executed when the song finishes.
2021-02-26 15:26:23 +08:00
func (p *Player) SetSongFinish(f func(Audio)) {
2021-02-26 11:18:58 +08:00
p.songFinish = f
}
2021-02-27 11:53:47 +08:00
// SetSongStart accepts callback which will be executed when the song starts.
2021-02-26 15:26:23 +08:00
func (p *Player) SetSongStart(f func(Audio)) {
2021-02-26 11:18:58 +08:00
p.songStart = f
}
2021-02-27 11:53:47 +08:00
// SetSongSkip accepts callback which will be executed when the song is skipped.
2021-02-26 15:26:23 +08:00
func (p *Player) SetSongSkip(f func(Audio)) {
2021-02-26 11:18:58 +08:00
p.songSkip = f
}
2021-02-27 11:53:47 +08:00
// executes songFinish callback.
2021-02-26 15:26:23 +08:00
func (p *Player) execSongFinish(a Audio) {
2021-02-26 12:59:22 +08:00
if p.songFinish != nil {
2021-02-26 15:26:23 +08:00
p.songFinish(a)
2021-02-26 12:59:22 +08:00
}
}
2021-02-27 11:53:47 +08:00
// executes songStart callback.
2021-02-26 15:26:23 +08:00
func (p *Player) execSongStart(a Audio) {
2021-02-26 12:59:22 +08:00
if p.songStart != nil {
2021-02-26 15:26:23 +08:00
p.songStart(a)
2021-02-26 12:59:22 +08:00
}
}
2021-02-27 11:53:47 +08:00
// executes songFinish callback.
2021-02-26 15:26:23 +08:00
func (p *Player) execSongSkip(a Audio) {
2021-02-26 12:59:22 +08:00
if p.songSkip != nil {
2021-02-26 15:26:23 +08:00
p.songSkip(a)
2021-02-26 12:59:22 +08:00
}
}
2021-02-27 11:53:47 +08:00
// Run plays the passed Audio.
2021-02-26 11:18:58 +08:00
func (p *Player) Run(currSong Audio) error {
2021-02-03 10:50:01 +08:00
2021-02-27 11:53:47 +08:00
p.isRunning = true
2021-02-26 15:26:23 +08:00
p.execSongStart(currSong)
2020-06-20 23:00:19 +08:00
2021-02-26 11:18:58 +08:00
f, err := os.Open(currSong.Path())
2020-07-06 17:02:59 +08:00
if err != nil {
2020-07-21 12:22:00 +08:00
return tracerr.Wrap(err)
2020-07-06 17:02:59 +08:00
}
2021-02-03 10:50:01 +08:00
stream, format, err := mp3.Decode(f)
2020-07-21 12:22:00 +08:00
if err != nil {
return tracerr.Wrap(err)
}
2021-02-01 13:48:34 +08:00
2021-02-26 11:18:58 +08:00
p.streamSeekCloser = stream
2020-06-21 23:47:02 +08:00
// song duration
p.length = format.SampleRate.D(p.streamSeekCloser.Len())
2020-06-21 23:47:02 +08:00
2021-02-16 02:25:01 +08:00
sr := beep.SampleRate(48000)
2020-06-21 23:47:02 +08:00
if !p.hasInit {
2020-07-20 21:48:13 +08:00
// p.mu.Lock()
2021-02-19 09:56:01 +08:00
err := speaker.Init(sr, sr.N(time.Second/10))
// p.mu.Unlock()
2020-07-03 00:50:32 +08:00
if err != nil {
2020-07-21 12:22:00 +08:00
return tracerr.Wrap(err)
2020-07-03 00:50:32 +08:00
}
2020-06-21 23:47:02 +08:00
p.hasInit = true
}
2020-06-20 23:00:19 +08:00
p.currentSong = currSong
2020-06-21 23:47:02 +08:00
2021-02-16 14:55:15 +08:00
// resample to adapt to sample rate of new songs
resampled := beep.Resample(4, format.SampleRate, sr, p.streamSeekCloser)
2020-06-23 18:42:18 +08:00
2021-02-16 02:25:01 +08:00
sstreamer := beep.Seq(resampled, beep.Callback(func() {
2021-02-27 11:51:12 +08:00
p.isRunning = false
p.format = nil
p.streamSeekCloser.Close()
2021-02-27 15:52:13 +08:00
go p.execSongFinish(currSong)
2020-06-20 23:00:19 +08:00
}))
2020-07-20 21:48:13 +08:00
ctrl := &beep.Ctrl{
2020-07-21 01:21:59 +08:00
Streamer: sstreamer,
Paused: false,
2020-07-20 21:48:13 +08:00
}
p.mu.Lock()
p.format = &format
2020-06-23 22:26:28 +08:00
p.ctrl = ctrl
p.mu.Unlock()
2020-06-23 18:42:18 +08:00
resampler := beep.ResampleRatio(4, 1, ctrl)
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
2021-02-25 16:44:42 +08:00
p.vol = volume
2021-02-19 12:22:41 +08:00
// starts playing the audio
2021-02-25 16:44:42 +08:00
speaker.Play(p.vol)
2020-06-20 23:00:19 +08:00
2020-07-21 12:22:00 +08:00
return nil
2020-06-20 23:00:19 +08:00
}
2021-02-27 11:53:47 +08:00
// Pause pauses Player.
2021-02-26 11:18:58 +08:00
func (p *Player) Pause() {
2020-06-20 23:00:19 +08:00
speaker.Lock()
2020-06-23 22:26:28 +08:00
p.ctrl.Paused = true
2020-07-23 15:15:39 +08:00
p.isRunning = false
2020-06-20 23:00:19 +08:00
speaker.Unlock()
}
2021-02-27 11:53:47 +08:00
// Play unpauses Player.
2021-02-26 11:18:58 +08:00
func (p *Player) Play() {
2020-06-20 23:00:19 +08:00
speaker.Lock()
2020-06-23 22:26:28 +08:00
p.ctrl.Paused = false
2020-07-23 15:15:39 +08:00
p.isRunning = true
2020-06-20 23:00:19 +08:00
speaker.Unlock()
}
// SetVolume set volume up and volume down using -0.5 or +0.5.
2021-02-26 11:18:58 +08:00
func (p *Player) SetVolume(v float64) float64 {
2020-06-26 17:09:15 +08:00
2020-07-12 09:48:48 +08:00
// check if no songs playing currently
2021-02-25 16:44:42 +08:00
if p.vol == nil {
2020-06-26 17:09:15 +08:00
p.volume += v
2020-07-04 10:17:52 +08:00
return p.volume
2020-06-26 17:09:15 +08:00
}
2020-06-21 23:47:02 +08:00
speaker.Lock()
2021-02-25 16:44:42 +08:00
p.vol.Volume += v
p.volume = p.vol.Volume
2020-06-21 23:47:02 +08:00
speaker.Unlock()
2020-06-26 17:09:15 +08:00
return p.volume
2020-06-21 23:47:02 +08:00
}
// TogglePause toggles the pause state.
2021-02-26 11:18:58 +08:00
func (p *Player) TogglePause() {
2020-07-22 21:08:55 +08:00
if p.ctrl == nil {
return
}
2020-06-23 22:26:28 +08:00
if p.ctrl.Paused {
2021-02-26 11:18:58 +08:00
p.Play()
2020-06-21 23:47:02 +08:00
} else {
2021-02-26 11:18:58 +08:00
p.Pause()
2020-06-21 23:47:02 +08:00
}
}
// Skip current song.
2021-02-26 11:18:58 +08:00
func (p *Player) Skip() {
2021-02-19 22:23:26 +08:00
2021-02-26 15:26:23 +08:00
p.execSongSkip(p.currentSong)
2021-02-26 12:59:22 +08:00
if p.currentSong == nil {
2020-07-12 12:21:57 +08:00
return
2020-06-23 18:42:18 +08:00
}
2020-07-12 12:21:57 +08:00
2021-02-27 11:51:12 +08:00
// drain the stream
speaker.Lock()
2020-07-12 12:21:57 +08:00
p.ctrl.Streamer = nil
p.streamSeekCloser.Close()
2021-02-27 11:51:12 +08:00
p.isRunning = false
p.format = nil
speaker.Unlock()
2021-02-27 11:51:12 +08:00
p.execSongFinish(p.currentSong)
2020-07-12 12:21:57 +08:00
}
2021-02-27 11:53:47 +08:00
// GetPosition returns the current position of audio file.
2021-02-26 11:18:58 +08:00
func (p *Player) GetPosition() time.Duration {
2021-02-26 15:26:23 +08:00
p.mu.Lock()
speaker.Lock()
defer speaker.Unlock()
defer p.mu.Unlock()
2021-02-26 15:26:23 +08:00
if p.format == nil || p.streamSeekCloser == nil {
return 1
}
2021-02-19 09:56:01 +08:00
return p.format.SampleRate.D(p.streamSeekCloser.Position())
}
// Seek is the function to move forward and rewind
2021-02-26 11:18:58 +08:00
func (p *Player) Seek(pos int) error {
p.mu.Lock()
2021-02-19 09:56:01 +08:00
speaker.Lock()
defer speaker.Unlock()
defer p.mu.Unlock()
2021-02-19 09:56:01 +08:00
err := p.streamSeekCloser.Seek(pos * int(p.format.SampleRate))
return err
}
2021-02-27 11:53:47 +08:00
// IsPaused is used to distinguish the player between pause and stop
2021-02-26 11:18:58 +08:00
func (p *Player) IsPaused() bool {
p.mu.Lock()
speaker.Lock()
defer speaker.Unlock()
defer p.mu.Unlock()
2021-02-19 09:56:01 +08:00
if p.ctrl == nil {
return false
}
return p.ctrl.Paused
}
2021-02-27 11:53:47 +08:00
// GetVolume returns current volume.
2021-02-26 11:18:58 +08:00
func (p *Player) GetVolume() float64 {
return p.volume
}
2021-02-27 11:53:47 +08:00
// GetCurrentSong returns current song.
2021-02-26 11:18:58 +08:00
func (p *Player) GetCurrentSong() Audio {
return p.currentSong
}
2021-02-27 11:53:47 +08:00
// HasInit checks if the speaker has been initialized or not. Speaker
// initialization will only happen once.
2021-02-26 11:18:58 +08:00
func (p *Player) HasInit() bool {
return p.hasInit
}
2021-02-27 11:53:47 +08:00
// IsRunning returns true if Player is running an audio.
2021-02-26 11:18:58 +08:00
func (p *Player) IsRunning() bool {
return p.isRunning
}
// GetLength return the length of the song in the queue
2021-02-26 11:18:58 +08:00
func GetLength(audioPath string) (time.Duration, error) {
2020-07-02 20:47:20 +08:00
f, err := os.Open(audioPath)
if err != nil {
2020-07-21 12:22:00 +08:00
return 0, tracerr.Wrap(err)
2020-07-02 20:47:20 +08:00
}
2020-07-06 17:02:59 +08:00
defer f.Close()
2020-07-02 20:47:20 +08:00
streamer, format, err := mp3.Decode(f)
if err != nil {
2020-07-21 12:22:00 +08:00
return 0, tracerr.Wrap(err)
2020-07-02 20:47:20 +08:00
}
2020-07-06 17:02:59 +08:00
defer streamer.Close()
2020-07-02 20:47:20 +08:00
return format.SampleRate.D(streamer.Len()), nil
}
2021-02-26 11:18:58 +08:00
// VolToHuman converts float64 volume that is used by audio library to human
// readable form (0 - 100)
2021-02-26 11:18:58 +08:00
func VolToHuman(volume float64) int {
return int(volume*10) + 100
}
2021-02-26 11:18:58 +08:00
// AbsVolume converts human readable form volume (0 - 100) to float64 volume
// that is used by the audio library
2021-02-26 11:18:58 +08:00
func AbsVolume(volume int) float64 {
return (float64(volume) - 100) / 10
}