128 lines
2.1 KiB
Go
128 lines
2.1 KiB
Go
package player
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/faiface/beep"
|
|
"github.com/faiface/beep/mp3"
|
|
"github.com/faiface/beep/speaker"
|
|
)
|
|
|
|
type Station struct {
|
|
Name string
|
|
URL string
|
|
}
|
|
|
|
type Status struct {
|
|
Playing bool
|
|
Station string
|
|
Channels []string
|
|
}
|
|
|
|
type Player struct {
|
|
stations []Station
|
|
current int
|
|
oldStreamer beep.StreamCloser
|
|
oldCtrl *beep.Ctrl
|
|
mixer *beep.Mixer
|
|
sr beep.SampleRate
|
|
}
|
|
|
|
func NewPlayer(stations []Station) *Player {
|
|
return &Player{
|
|
stations: stations,
|
|
current: -1,
|
|
mixer: &beep.Mixer{},
|
|
sr: beep.SampleRate(48000),
|
|
}
|
|
}
|
|
|
|
func (p *Player) Init() error {
|
|
|
|
if err := speaker.Init(p.sr, p.sr.N(time.Second/10)); err != nil {
|
|
return err
|
|
}
|
|
|
|
speaker.Play(p.mixer)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *Player) Select(channel int) error {
|
|
if channel < 0 || channel >= len(p.stations) {
|
|
return fmt.Errorf("unknown channel: %d", channel)
|
|
}
|
|
|
|
res, err := http.Get(p.stations[channel].URL)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
streamer, format, err := mp3.Decode(res.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resampled := Resample(4, format.SampleRate, p.sr, streamer)
|
|
|
|
p.PlayStream(resampled)
|
|
p.current = channel
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *Player) PlayStream(streamer beep.StreamCloser) {
|
|
ctrl := &beep.Ctrl{
|
|
Streamer: streamer,
|
|
}
|
|
p.mixer.Add(ctrl)
|
|
|
|
speaker.Lock()
|
|
|
|
if p.oldCtrl != nil {
|
|
p.oldCtrl.Paused = true
|
|
p.oldCtrl.Streamer = nil
|
|
p.oldStreamer.Close()
|
|
}
|
|
|
|
ctrl.Paused = true
|
|
ctrl.Streamer = streamer
|
|
ctrl.Paused = false
|
|
|
|
speaker.Unlock()
|
|
|
|
p.oldCtrl = ctrl
|
|
p.oldStreamer = streamer
|
|
}
|
|
|
|
func (p *Player) Stop() {
|
|
speaker.Lock()
|
|
if p.oldCtrl != nil {
|
|
p.oldCtrl.Paused = true
|
|
p.oldCtrl.Streamer = nil
|
|
p.oldStreamer.Close()
|
|
}
|
|
speaker.Unlock()
|
|
|
|
p.current = -1
|
|
}
|
|
|
|
func (p *Player) Status() Status {
|
|
channels := make([]string, len(p.stations))
|
|
for i, stat := range p.stations {
|
|
channels[i] = stat.Name
|
|
}
|
|
|
|
if p.current < 0 || p.current >= len(p.stations) {
|
|
return Status{Channels: channels}
|
|
}
|
|
|
|
return Status{
|
|
Playing: true,
|
|
Station: p.stations[p.current].Name,
|
|
Channels: channels,
|
|
}
|
|
}
|