mirror of
https://github.com/issadarkthing/gomu.git
synced 2025-04-26 13:49:21 +08:00
add player.go
This commit is contained in:
parent
8e576f426e
commit
56318c2ec7
1
go.mod
1
go.mod
@ -3,6 +3,7 @@ module github.com/issadarkthing/gomu
|
||||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/faiface/beep v1.0.2 // indirect
|
||||
github.com/gdamore/tcell v1.3.0
|
||||
github.com/rivo/tview v0.0.0-20200528200248-fe953220389f
|
||||
)
|
||||
|
23
go.sum
23
go.sum
@ -1,18 +1,40 @@
|
||||
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/faiface/beep v1.0.2 h1:UB5DiRNmA4erfUYnHbgU4UB6DlBOrsdEFRtcc8sCkdQ=
|
||||
github.com/faiface/beep v1.0.2/go.mod h1:1yLb5yRdHMsovYYWVqYLioXkVuziCSITW1oarTeduQM=
|
||||
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
|
||||
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
||||
github.com/gdamore/tcell v1.1.1/go.mod h1:K1udHkiR3cOtlpKG5tZPD5XxrF7v2y7lDq7Whcj+xkQ=
|
||||
github.com/gdamore/tcell v1.3.0 h1:r35w0JBADPZCVQijYebl6YMWWtHRqVEGt7kL2eBADRM=
|
||||
github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherwasm v0.1.1/go.mod h1:kx4n9a+MzHH0BJJhvlsQ65hqLFXDO/m256AsaDPQ+/4=
|
||||
github.com/gopherjs/gopherwasm v1.0.0/go.mod h1:SkZ8z7CWBz5VXbhJel8TxCmAcsQqzgWGR/8nMhyhZSI=
|
||||
github.com/hajimehoshi/go-mp3 v0.1.1 h1:Y33fAdTma70fkrxnc9u50Uq0lV6eZ+bkAlssdMmCwUc=
|
||||
github.com/hajimehoshi/go-mp3 v0.1.1/go.mod h1:4i+c5pDNKDrxl1iu9iG90/+fhP37lio6gNhjCx9WBJw=
|
||||
github.com/hajimehoshi/oto v0.1.1/go.mod h1:hUiLWeBQnbDu4pZsAhOnGqMI1ZGibS6e2qhQdfpwz04=
|
||||
github.com/hajimehoshi/oto v0.3.1 h1:cpf/uIv4Q0oc5uf9loQn7PIehv+mZerh+0KKma6gzMk=
|
||||
github.com/hajimehoshi/oto v0.3.1/go.mod h1:e9eTLBB9iZto045HLbzfHJIc+jP3xaKrjZTghvb6fdM=
|
||||
github.com/jfreymuth/oggvorbis v1.0.0/go.mod h1:abe6F9QRjuU9l+2jek3gj46lu40N4qlYxh2grqkLEDM=
|
||||
github.com/jfreymuth/vorbis v1.0.0/go.mod h1:8zy3lUAm9K/rJJk223RKy6vjCZTWC61NA2QD06bfOE0=
|
||||
github.com/lucasb-eyer/go-colorful v0.0.0-20181028223441-12d3b2882a08/go.mod h1:NXg0ArsFk0Y01623LgUqoqcouGDB+PwCCQlrwrG6xJ4=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0=
|
||||
github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mewkiz/flac v1.0.5/go.mod h1:EHZNU32dMF6alpurYyKHDLYpW1lYpBZ5WrXi/VuNIGs=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/rivo/tview v0.0.0-20200528200248-fe953220389f h1:tRx/LLIP2PSA7johw9xhf+6NUCLC4BbMhpGdm110MGI=
|
||||
github.com/rivo/tview v0.0.0-20200528200248-fe953220389f/go.mod h1:6lkG1x+13OShEf0EaOCaTQYyB7d5nSbb181KtjlS+84=
|
||||
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
golang.org/x/exp v0.0.0-20180710024300-14dda7b62fcd/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
|
||||
golang.org/x/mobile v0.0.0-20180806140643-507816974b79/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 h1:sfkvUWPNGwSV+8/fNqctR5lS2AqCSqYwXdrjCxp/dXo=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -20,3 +42,4 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0/go.mod h1:OdE7CF6DbADk7lN8LIKRzRJTTZXIjtWgA5THM5lhBAw=
|
||||
|
181
player.go
Normal file
181
player.go
Normal file
@ -0,0 +1,181 @@
|
||||
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))
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user