aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Fischer <florian.fischer@muhq.space>2022-07-26 21:37:24 +0200
committerFlorian Fischer <florian.fischer@muhq.space>2022-07-26 21:37:24 +0200
commit7e15ad1d8af4ee27f6c1d31e7b1608e2d751c402 (patch)
tree157a86cd3597d51b3fe72e73bd4e782bda07b2d0
parent8171af55daad6ce9a5cdacf95527801e83f25c08 (diff)
downloadWRMS-7e15ad1d8af4ee27f6c1d31e7b1608e2d751c402.tar.gz
WRMS-7e15ad1d8af4ee27f6c1d31e7b1608e2d751c402.zip
intermediate commit
* use mpv to play songs * fix CurrentSong being a pointer to a changing slot in the playlist array * wire up play and pause * continue playing after a song is finished
-rw-r--r--dummyBackend.go5
-rw-r--r--go.mod12
-rw-r--r--go.sum28
-rw-r--r--main.go6
-rw-r--r--player.go64
-rw-r--r--spotify.go113
-rw-r--r--web/client.html8
-rw-r--r--wrms.go14
8 files changed, 115 insertions, 135 deletions
diff --git a/dummyBackend.go b/dummyBackend.go
index d4461db..460dd5e 100644
--- a/dummyBackend.go
+++ b/dummyBackend.go
@@ -7,10 +7,7 @@ import (
type DummyBackend struct{}
-func (dummy *DummyBackend) Play(song *Song) {
-}
-
-func (dummy *DummyBackend) Pause() {
+func (dummy *DummyBackend) Play(song *Song, player *Player) {
}
func (dummy *DummyBackend) Search(keyword string) []Song {
diff --git a/go.mod b/go.mod
index 1e70a32..013d1ec 100644
--- a/go.mod
+++ b/go.mod
@@ -3,18 +3,18 @@ module muhq.space/wrms
go 1.18
require (
+ github.com/google/uuid v1.3.0
+ github.com/librespot-org/librespot-golang v0.0.0-20220325184705-31669e5a889f
+ nhooyr.io/websocket v1.8.7
+)
+
+require (
github.com/badfortrains/mdns v0.0.0-20160325001438-447166384f51 // indirect
- github.com/bitly/go-simplejson v0.5.0 // indirect
github.com/golang/protobuf v1.5.0 // indirect
- github.com/google/uuid v1.3.0 // indirect
github.com/klauspost/compress v1.10.3 // indirect
- github.com/librespot-org/librespot-golang v0.0.0-20220325184705-31669e5a889f // indirect
github.com/miekg/dns v1.1.8 // indirect
- github.com/xlab/portaudio-go v0.0.0-20170905165025-132d041879db // indirect
- github.com/xlab/vorbis-go v0.0.0-20210911202351-b5b85f1ec645 // indirect
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480 // indirect
golang.org/x/net v0.0.0-20190420063019-afa5a82059c6 // indirect
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 // indirect
google.golang.org/protobuf v1.27.1 // indirect
- nhooyr.io/websocket v1.8.7 // indirect
)
diff --git a/go.sum b/go.sum
index fdd34ea..afd47b7 100644
--- a/go.sum
+++ b/go.sum
@@ -1,55 +1,71 @@
github.com/badfortrains/mdns v0.0.0-20160325001438-447166384f51 h1:b6+GdpGhuzxPETrVLwY77iq0L/BqLpcPRmaLNW+SoRY=
github.com/badfortrains/mdns v0.0.0-20160325001438-447166384f51/go.mod h1:qHRkxMBkkpAWD2poeBYQt6O5IS4dE6w1Cr4Z8Q3feXI=
-github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
-github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
+github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
+github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
+github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
+github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
+github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
+github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/klauspost/compress v1.10.3 h1:OP96hzwJVBIHYU52pVTI6CczrxPvrGfgqF9N5eTO0Q8=
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
+github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/librespot-org/librespot-golang v0.0.0-20220325184705-31669e5a889f h1:tTMPsyVClxxV+CnlLqzgxTTTFM6J7i//rSejN1Md0b0=
github.com/librespot-org/librespot-golang v0.0.0-20220325184705-31669e5a889f/go.mod h1:LeHPXRci2Bg6RBmw8BDEFH5nfb8oGU6mll+qTH8UIzw=
+github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/miekg/dns v1.1.8 h1:1QYRAKU3lN5cRfLCkPU08hwvLJFhvjP6MqNMmQz6ZVI=
github.com/miekg/dns v1.1.8/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
+github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
-github.com/xlab/portaudio-go v0.0.0-20170905165025-132d041879db h1:sSIQlvfIWUHLDhEWUL2K2CeYv9CDksC00VxuxPUe4lw=
github.com/xlab/portaudio-go v0.0.0-20170905165025-132d041879db/go.mod h1:r57mRacDQMS6Fz8ubv1nE8zZ0DbQ/sY0NkCmdxeUXmY=
github.com/xlab/vorbis-go v0.0.0-20190125051917-087364aef51d/go.mod h1:AMqfx3jFwPqem3u8mF2lsRodZs30jG/Mag5HZ3mB3sA=
-github.com/xlab/vorbis-go v0.0.0-20210911202351-b5b85f1ec645 h1:lYg/+vV/Fd5WM1+Ptg54Am3y4mDXaMSrT+mKUHV5uVc=
-github.com/xlab/vorbis-go v0.0.0-20210911202351-b5b85f1ec645/go.mod h1:AMqfx3jFwPqem3u8mF2lsRodZs30jG/Mag5HZ3mB3sA=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480 h1:O5YqonU5IWby+w98jVUG9h7zlCWCcH4RHyPVReBmhzk=
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/net v0.0.0-20190420063019-afa5a82059c6 h1:HdqqaWmYAUI7/dmByKKEw+yxDksGSo+9GjkUc9Zp34E=
golang.org/x/net v0.0.0-20190420063019-afa5a82059c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -59,12 +75,14 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
diff --git a/main.go b/main.go
index 657b211..545c3b2 100644
--- a/main.go
+++ b/main.go
@@ -55,7 +55,7 @@ func genericVoteHandler(w http.ResponseWriter, r *http.Request, vote string) {
connId := uuidCookie.Value
songUri := r.URL.Query().Get("song")
- log.Println("%s song %s via url %s", vote, songUri, r.URL)
+ log.Println(fmt.Sprintf("%s song %s via url %s", vote, songUri, r.URL))
wrms.AdjustSongWeight(connId, songUri, vote)
}
@@ -85,7 +85,7 @@ func addHandler(w http.ResponseWriter, r *http.Request) {
return
}
- log.Println(fmt.Sprintf("Added song %s", string(data)))
+ log.Println("Added song", string(data))
wrms.AddSong(song)
wrms.Broadcast(Event{"add", []Song{song}})
fmt.Fprintf(w, "Added song %s", string(data))
@@ -128,7 +128,7 @@ func wsEndpoint(w http.ResponseWriter, r *http.Request) {
initialCmds := [][]byte{}
if wrms.Playing {
- data, err := json.Marshal(Event{"play", []Song{*wrms.CurrentSong}})
+ data, err := json.Marshal(Event{"play", []Song{wrms.CurrentSong}})
if err != nil {
log.Fatal(err)
return
diff --git a/player.go b/player.go
index 6e377a7..a672288 100644
--- a/player.go
+++ b/player.go
@@ -1,20 +1,25 @@
package main
import (
+ "fmt"
+ "io"
"log"
+ "os/exec"
+ "syscall"
)
type Backend interface {
- Play(song *Song)
- Pause()
+ Play(song *Song, player *Player)
Search(keyword string) []Song
}
type Player struct {
Backends map[string]Backend
+ mpv *exec.Cmd
+ wrms *Wrms
}
-func NewPlayer() Player {
+func NewPlayer(wrms *Wrms) Player {
available_backends := map[string]Backend{}
spotify, err := NewSpotify()
@@ -26,14 +31,63 @@ func NewPlayer() Player {
available_backends["dummy"] = &DummyBackend{}
- return Player{available_backends}
+ return Player{available_backends, nil, wrms}
}
func (player *Player) Play(song *Song) {
- player.Backends[song.Source].Play(song)
+ if player.mpv != nil {
+ log.Println("Send SIGCONT to mpv subprocess")
+ player.mpv.Process.Signal(syscall.SIGCONT)
+ return
+ }
+
+ player.Backends[song.Source].Play(song, player)
+}
+
+func (player *Player) runMpv() {
+ out, err := player.mpv.CombinedOutput()
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ log.Println("mpv finished with", out, "resetting mpv, the current song and call play")
+ player.mpv = nil
+ player.wrms.CurrentSong.Uri = ""
+ player.wrms.PlayPause()
+}
+
+func (player *Player) PlayUri(uri string) {
+ player.mpv = exec.Command("mpv", uri)
+ go player.runMpv()
+}
+
+func (player *Player) PlayData(data io.Reader) {
+ if player.mpv != nil {
+ log.Fatal("Player has already an mpv subprocess")
+ }
+
+ player.mpv = exec.Command("mpv", "-")
+ stdin, err := player.mpv.StdinPipe()
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ go func() {
+ defer stdin.Close()
+
+ if _, err := io.Copy(stdin, data); err != nil {
+ log.Fatal(fmt.Sprintf("Failed to write song data to mpv (%v)", err))
+ }
+ }()
+
+ go player.runMpv()
}
func (player *Player) Pause() {
+ if player.mpv != nil {
+ log.Println("Send SIGSTOP to mpv subprocess")
+ player.mpv.Process.Signal(syscall.SIGSTOP)
+ }
}
func (player *Player) Search(pattern string) []Song {
diff --git a/spotify.go b/spotify.go
index 6f11e84..951bbd9 100644
--- a/spotify.go
+++ b/spotify.go
@@ -9,29 +9,19 @@ package main
import (
"errors"
- "log"
"fmt"
+ "log"
"os"
"strings"
- "sync"
- "unsafe"
"github.com/librespot-org/librespot-golang/Spotify"
"github.com/librespot-org/librespot-golang/librespot"
"github.com/librespot-org/librespot-golang/librespot/core"
"github.com/librespot-org/librespot-golang/librespot/utils"
- "github.com/xlab/portaudio-go/portaudio"
- "github.com/xlab/vorbis-go/decoder"
)
const (
deviceName = "wrms"
- // The number of samples per channel in the decoded audio
- samplesPerChannel = 2048
- // The samples bit depth
- bitDepth = 16
- // The samples format
- sampleFormat = portaudio.PaFloat32
)
type SpotifyBackend struct {
@@ -41,10 +31,6 @@ type SpotifyBackend struct {
func NewSpotify() (*SpotifyBackend, error) {
spotify := SpotifyBackend{}
- if err := portaudio.Initialize(); paError(err) {
- return nil, errors.New(paErrorText(err))
- }
-
var err error
username := os.Getenv("WRMS_SPOTIFY_USER")
@@ -62,8 +48,8 @@ func NewSpotify() (*SpotifyBackend, error) {
return &spotify, nil
}
-func (spotify *SpotifyBackend) Play(song *Song) {
- trackID := song.Uri
+func (spotify *SpotifyBackend) Play(song *Song, player *Player) {
+ trackID := song.Uri
session := spotify.session
fmt.Println("Loading track for play: ", trackID)
@@ -76,8 +62,8 @@ func (spotify *SpotifyBackend) Play(song *Song) {
fmt.Println("Track:", track.GetName())
- // As a demo, select the OGG 160kbps variant of the track. The "high quality" setting in the official Spotify
- // app is the OGG 320kbps variant.
+ // For now, select the OGG 160kbps variant of the track. The "high quality"
+ // setting in the official Spotify app is the OGG 320kbps variant.
var selectedFile *Spotify.AudioFile
for _, file := range track.GetFile() {
if file.GetFormat() == Spotify.AudioFile_OGG_VORBIS_160 {
@@ -87,50 +73,11 @@ func (spotify *SpotifyBackend) Play(song *Song) {
// Synchronously load the track
audioFile, err := session.Player().LoadTrack(selectedFile, track.GetGid())
-
- // TODO: channel to be notified of chunks downloaded (or reader?)
-
if err != nil {
- fmt.Printf("Error while loading track: %s\n", err)
- } else {
- // We have the track audio, let's play it! Initialize the OGG decoder, and start a PortAudio stream.
- // Note that we skip the first 167 bytes as it is a Spotify-specific header. You can decode it by
- // using this: https://sourceforge.net/p/despotify/code/HEAD/tree/java/trunk/src/main/java/se/despotify/client/player/SpotifyOggHeader.java
- fmt.Println("Setting up OGG decoder...")
- dec, err := decoder.New(audioFile, samplesPerChannel)
- if err != nil {
- log.Fatalln(err)
- }
-
- info := dec.Info()
-
- go func() {
- dec.Decode()
- dec.Close()
- }()
-
- fmt.Println("Setting up PortAudio stream...")
- fmt.Printf("PortAudio channels: %d / SampleRate: %f\n", info.Channels, info.SampleRate)
-
- var wg sync.WaitGroup
- var stream *portaudio.Stream
- callback := paCallback(&wg, int(info.Channels), dec.SamplesOut())
-
- if err := portaudio.OpenDefaultStream(&stream, 0, info.Channels, sampleFormat, info.SampleRate,
- samplesPerChannel, callback, nil); paError(err) {
- log.Fatalln(paErrorText(err))
- }
-
- fmt.Println("Starting playback...")
- if err := portaudio.StartStream(stream); paError(err) {
- log.Fatalln(paErrorText(err))
- }
-
- wg.Wait()
+ log.Fatal(fmt.Sprintf("Error while loading track: %s\n", err))
}
-}
-func (spotify *SpotifyBackend) Pause() {
+ player.PlayData(audioFile)
}
func (spotify *SpotifyBackend) Search(keyword string) []Song {
@@ -156,53 +103,9 @@ func (spotify *SpotifyBackend) Search(keyword string) []Song {
results := []Song{}
for _, track := range res.Tracks.Hits {
uriParts := strings.Split(track.Uri, ":")
- results = append(results, NewSong(track.Name, track.Artists[0].Name, "spotify", uriParts[len(uriParts) - 1]))
+ results = append(results, NewSong(track.Name, track.Artists[0].Name, "spotify", uriParts[len(uriParts)-1]))
// fmt.Printf(" => %v (%s)\n", track, track.Uri)
}
return results
}
-
-// PortAudio helpers
-func paError(err portaudio.Error) bool {
- return portaudio.ErrorCode(err) != portaudio.PaNoError
-}
-
-func paErrorText(err portaudio.Error) string {
- return "PortAudio error: " + portaudio.GetErrorText(err)
-}
-
-func paCallback(wg *sync.WaitGroup, channels int, samples <-chan [][]float32) portaudio.StreamCallback {
- wg.Add(1)
- return func(_ unsafe.Pointer, output unsafe.Pointer, sampleCount uint,
- _ *portaudio.StreamCallbackTimeInfo, _ portaudio.StreamCallbackFlags, _ unsafe.Pointer) int32 {
-
- const (
- statusContinue = int32(portaudio.PaContinue)
- statusComplete = int32(portaudio.PaComplete)
- )
-
- frame, ok := <-samples
- if !ok {
- wg.Done()
- return statusComplete
- }
- if len(frame) > int(sampleCount) {
- frame = frame[:sampleCount]
- }
-
- var idx int
- out := (*(*[1 << 32]float32)(unsafe.Pointer(output)))[:int(sampleCount)*channels]
- for _, sample := range frame {
- if len(sample) > channels {
- sample = sample[:channels]
- }
- for i := range sample {
- out[idx] = sample[i]
- idx++
- }
- }
-
- return statusContinue
- }
-}
diff --git a/web/client.html b/web/client.html
index 38e1652..ba57839 100644
--- a/web/client.html
+++ b/web/client.html
@@ -15,6 +15,10 @@
.vote.on {
color: #f48024
}
+
+ .searchResult:hover {
+ background-color: #999;
+ }
</style>
<script>
@@ -228,6 +232,8 @@
searchResultsOverlay.style.display = "none";
});
+ listItem.classList.add("searchResult")
+
let searchResultLabel = document.createTextNode(song.artist + " - " + song.title);
listItem.appendChild(searchResultLabel);
@@ -240,7 +246,7 @@
window.onload = function() {
document.getElementById("ppbutton").addEventListener("click", function() {
- new HttpClient().get("/playpause", showSearchResults);
+ new HttpClient().get("/playpause", console.log);
});
}
</script>
diff --git a/wrms.go b/wrms.go
index ee21c27..4df78fa 100644
--- a/wrms.go
+++ b/wrms.go
@@ -59,14 +59,14 @@ type Wrms struct {
Connections []Connection
Songs []Song
Queue Playlist
- CurrentSong *Song
+ CurrentSong Song
Player Player
Playing bool
}
func NewWrms() Wrms {
wrms := Wrms{}
- wrms.Player = NewPlayer()
+ wrms.Player = NewPlayer(&wrms)
return wrms
}
@@ -91,7 +91,7 @@ func (wrms *Wrms) AddSong(song Song) {
}
func (wrms *Wrms) Next() {
- wrms.CurrentSong = wrms.Queue.PopSong()
+ wrms.CurrentSong = *wrms.Queue.PopSong()
for i, s := range wrms.Songs {
if s.Uri == wrms.CurrentSong.Uri {
wrms.Songs[i] = wrms.Songs[len(wrms.Songs)-1]
@@ -104,13 +104,15 @@ func (wrms *Wrms) Next() {
func (wrms *Wrms) PlayPause() {
var ev Event
if !wrms.Playing {
- if wrms.CurrentSong == nil {
+ if wrms.CurrentSong.Uri == "" {
+ log.Println("No song currently playing play the next")
wrms.Next()
}
- ev = Event{"play", []Song{*wrms.CurrentSong}}
- wrms.Player.Play(wrms.CurrentSong)
+ ev = Event{"play", []Song{wrms.CurrentSong}}
+ wrms.Player.Play(&wrms.CurrentSong)
} else {
ev = Event{"pause", []Song{}}
+ wrms.Player.Pause()
}
wrms.Playing = !wrms.Playing