From 7e15ad1d8af4ee27f6c1d31e7b1608e2d751c402 Mon Sep 17 00:00:00 2001 From: Florian Fischer Date: Tue, 26 Jul 2022 21:37:24 +0200 Subject: 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 --- dummyBackend.go | 5 +-- go.mod | 12 +++--- go.sum | 28 +++++++++++--- main.go | 6 +-- player.go | 64 +++++++++++++++++++++++++++++--- spotify.go | 113 ++++---------------------------------------------------- web/client.html | 8 +++- wrms.go | 14 ++++--- 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 @@ -2,19 +2,19 @@ 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; + } 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 -- cgit v1.2.3