streaming
This commit is contained in:
parent
c56f9f7a4b
commit
788b787276
@ -8,15 +8,9 @@ import (
|
|||||||
"../icecast"
|
"../icecast"
|
||||||
)
|
)
|
||||||
|
|
||||||
// type stream_t struct {
|
type Next func() (string, error)
|
||||||
// filepath string
|
|
||||||
// bitrate int
|
|
||||||
// channels int
|
|
||||||
// encoding_src string
|
|
||||||
// encoding_dst string
|
|
||||||
// }
|
|
||||||
|
|
||||||
func Play(filename string) {
|
func Start(next_cb Next) {
|
||||||
|
|
||||||
sig := make(chan os.Signal, 1)
|
sig := make(chan os.Signal, 1)
|
||||||
signal.Notify(sig, os.Interrupt, os.Kill)
|
signal.Notify(sig, os.Interrupt, os.Kill)
|
||||||
@ -26,41 +20,51 @@ func Play(filename string) {
|
|||||||
channels := 1
|
channels := 1
|
||||||
bytes_per_sec := int(samplerate) * channels * bytes_per_sample
|
bytes_per_sec := int(samplerate) * channels * bytes_per_sample
|
||||||
|
|
||||||
r, e := os.Open(filename)
|
|
||||||
chk(e)
|
|
||||||
defer r.Close()
|
|
||||||
|
|
||||||
_, er := icecast.Connect()
|
_, er := icecast.Connect()
|
||||||
chk(er)
|
chk(er)
|
||||||
|
|
||||||
audio := make([]byte, 2 * 1024)
|
audio := make([]byte, 2 * 1024)
|
||||||
|
|
||||||
dt := time.Second * time.Duration(len(audio)) / time.Duration(bytes_per_sec)
|
dt := time.Second * time.Duration(len(audio)) / time.Duration(bytes_per_sec)
|
||||||
|
|
||||||
now := time.Now()
|
for fname, exit := next_cb(); exit == nil; fname, exit = next_cb() {
|
||||||
|
|
||||||
for {
|
r, e := os.Open(fname)
|
||||||
|
chk(e)
|
||||||
|
defer r.Close()
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
for {
|
||||||
|
|
||||||
|
n, err := r.Read(audio)
|
||||||
|
if n == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
chk(err)
|
||||||
|
|
||||||
|
pre := time.Now()
|
||||||
|
|
||||||
|
icecast.Send(audio)
|
||||||
|
|
||||||
|
post := time.Now()
|
||||||
|
lag := time.Duration((post.Sub(pre)))
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-sig:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
dt = (time.Second * time.Duration(n) / time.Duration(bytes_per_sec)) - lag
|
||||||
|
|
||||||
|
time.Sleep(dt)
|
||||||
|
|
||||||
n, err := r.Read(audio)
|
|
||||||
if n == 0 {
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
chk(err)
|
|
||||||
|
|
||||||
icecast.Send(audio)
|
fmt.Println(time.Duration(time.Now().Sub(now)))
|
||||||
|
|
||||||
select {
|
|
||||||
case <-sig:
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(dt)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(time.Duration(time.Now().Sub(now)))
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -13,12 +13,15 @@ import (
|
|||||||
|
|
||||||
var pln = log.Println
|
var pln = log.Println
|
||||||
|
|
||||||
|
type Pop func(*Playlist)
|
||||||
|
|
||||||
type Playlist struct {
|
type Playlist struct {
|
||||||
NAME string
|
NAME string
|
||||||
CTRACK config.Track_t
|
CTRACK config.Track_t
|
||||||
CALBUM config.Album_t
|
CALBUM config.Album_t
|
||||||
LIST []uint32
|
LIST []uint32
|
||||||
MAX int
|
MAX int
|
||||||
|
CALLB Pop
|
||||||
}
|
}
|
||||||
|
|
||||||
type PrettyPlaylist struct {
|
type PrettyPlaylist struct {
|
||||||
@ -29,6 +32,19 @@ type PrettyPlaylist struct {
|
|||||||
MAX int
|
MAX int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MinimalTrack struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
NAME string `json:"name"`
|
||||||
|
MAKER string `json:"maker"`
|
||||||
|
ALBUM string `json:"album"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MinimalPlaylist struct {
|
||||||
|
NAME string `json:"name"`
|
||||||
|
CTRACK string `json:"ctrack"`
|
||||||
|
CALBUM string `json:"calbum"`
|
||||||
|
LIST []MinimalTrack `json:"list"`
|
||||||
|
}
|
||||||
|
|
||||||
func MakeRandom(name string, max int) (*Playlist, error) {
|
func MakeRandom(name string, max int) (*Playlist, error) {
|
||||||
err := archive.Build()
|
err := archive.Build()
|
||||||
@ -80,19 +96,22 @@ func (pp *PrettyPlaylist) Unpretty() *Playlist {
|
|||||||
p := &Playlist{NAME: pp.NAME, CTRACK: pp.CTRACK, CALBUM: pp.CALBUM, MAX: pp.MAX}
|
p := &Playlist{NAME: pp.NAME, CTRACK: pp.CTRACK, CALBUM: pp.CALBUM, MAX: pp.MAX}
|
||||||
p.LIST = make([]uint32, len(pp.LIST))
|
p.LIST = make([]uint32, len(pp.LIST))
|
||||||
for i := 0; i < len(pp.LIST); i++ {
|
for i := 0; i < len(pp.LIST); i++ {
|
||||||
p.LIST = append(p.LIST, pp.LIST[i].ID)
|
p.LIST[i] = pp.LIST[i].ID
|
||||||
}
|
}
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
// type PrettyPlaylist struct {
|
func (p *Playlist) Minimal() *MinimalPlaylist {
|
||||||
// NAME string
|
mp := &MinimalPlaylist{NAME: p.NAME, CTRACK: p.CTRACK.NAME}
|
||||||
// CTRACK config.Track_t
|
mp.CALBUM = p.CALBUM.MAKER + " - " + p.CALBUM.NAME
|
||||||
// CALBUM config.Album_t
|
mp.LIST = make([]MinimalTrack, len(p.LIST))
|
||||||
// LIST []config.Track_t
|
for i := 0; i < len(p.LIST); i++ {
|
||||||
// MAX int
|
track := archive.Archive_map[p.LIST[i]]
|
||||||
// }
|
strid := strconv.FormatUint(uint64(track.ID), 10)
|
||||||
|
mp.LIST[i] = MinimalTrack{ID: strid, NAME: track.NAME, MAKER: track.MAKER, ALBUM: track.ALBUM}
|
||||||
|
}
|
||||||
|
return mp
|
||||||
|
}
|
||||||
|
|
||||||
func (pp *PrettyPlaylist) Print() {
|
func (pp *PrettyPlaylist) Print() {
|
||||||
|
|
||||||
@ -115,9 +134,9 @@ func print(slice []uint32) {
|
|||||||
// https://github.com/golang/go/wiki/SliceTricks
|
// https://github.com/golang/go/wiki/SliceTricks
|
||||||
// https://stackoverflow.com/questions/33834742/remove-and-adding-elements-to-array-in-go-lang
|
// https://stackoverflow.com/questions/33834742/remove-and-adding-elements-to-array-in-go-lang
|
||||||
|
|
||||||
func (p *Playlist) Pop() error {
|
func (p *Playlist) Pop() (string, error) {
|
||||||
if len(p.LIST) == 0 {
|
if len(p.LIST) == 0 {
|
||||||
return errors.New("Playlist is empty")
|
return "", errors.New("Playlist is empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
t := p.LIST[0]
|
t := p.LIST[0]
|
||||||
@ -130,7 +149,11 @@ func (p *Playlist) Pop() error {
|
|||||||
p.CTRACK = archive.Archive_map[t]
|
p.CTRACK = archive.Archive_map[t]
|
||||||
p.CALBUM = config.Xcfg.Archive.ALBUMS[p.CTRACK.AID]
|
p.CALBUM = config.Xcfg.Archive.ALBUMS[p.CTRACK.AID]
|
||||||
|
|
||||||
return nil
|
if p.CALLB != nil {
|
||||||
|
p.CALLB(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.CTRACK.PATH, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Playlist) Push(track_id uint32) error {
|
func (p *Playlist) Push(track_id uint32) error {
|
||||||
@ -191,26 +214,43 @@ func (p *Playlist) Move(track_id uint32, at_index int) error {
|
|||||||
return errors.New("Invalid track ID")
|
return errors.New("Invalid track ID")
|
||||||
}
|
}
|
||||||
|
|
||||||
slice_head := p.LIST[:k]
|
// pln("k: " + strconv.Itoa(k))
|
||||||
slice_move := p.LIST[k+1:at_index+1]
|
// pln("at_index: " + strconv.Itoa(at_index))
|
||||||
slice_tail := make([]uint32, len(p.LIST[at_index+1:]))
|
// pln("------")
|
||||||
copy(slice_tail, p.LIST[at_index+1:])
|
|
||||||
|
|
||||||
// slice_move = append(slice_move, track_id)
|
var slice_head, slice_move, slice_tail []uint32
|
||||||
|
if k < at_index {
|
||||||
|
slice_head = make([]uint32, len(p.LIST[:k]))
|
||||||
|
copy(slice_head, p.LIST[:k])
|
||||||
|
// slice_head = p.LIST[:k]
|
||||||
|
slice_move = make([]uint32, len(p.LIST[k+1:at_index+1]))
|
||||||
|
copy(slice_move, p.LIST[k+1:at_index+1])
|
||||||
|
// slice_move = p.LIST[k:at_index]
|
||||||
|
slice_tail = make([]uint32, len(p.LIST[at_index+1:]))
|
||||||
|
copy(slice_tail, p.LIST[at_index+1:])
|
||||||
|
p.LIST = append(slice_head, append(append(slice_move, track_id), slice_tail...)...)
|
||||||
|
} else if k > at_index {
|
||||||
|
slice_head = make([]uint32, len(p.LIST[:at_index]))
|
||||||
|
copy(slice_head, p.LIST[:at_index])
|
||||||
|
// slice_head = p.LIST[:at_index]
|
||||||
|
slice_move = make([]uint32, len(p.LIST[at_index:k]))
|
||||||
|
copy(slice_move, p.LIST[at_index:k])
|
||||||
|
// slice_tail = p.LIST[k+1:]
|
||||||
|
slice_tail = make([]uint32, len(p.LIST[k+1:]))
|
||||||
|
copy(slice_tail, p.LIST[k+1:])
|
||||||
|
p.LIST = append(append(append(slice_head, track_id), slice_move...), slice_tail...)
|
||||||
|
}
|
||||||
|
|
||||||
pln("from_index: " + strconv.Itoa(k))
|
|
||||||
pln("at_index: " + strconv.Itoa(at_index))
|
|
||||||
pln("------")
|
|
||||||
pln("slice head: ")
|
|
||||||
print(slice_head)
|
|
||||||
pln("slice move: ")
|
|
||||||
print(slice_move)
|
|
||||||
pln("** ")
|
|
||||||
pln("slice tail: ")
|
|
||||||
print(slice_tail)
|
|
||||||
pln("...........................")
|
|
||||||
|
|
||||||
p.LIST = append(slice_head, append(append(slice_move, track_id), slice_tail...)...)
|
// pln("slice head: ")
|
||||||
|
// print(slice_head)
|
||||||
|
// pln("slice move: ")
|
||||||
|
// print(slice_move)
|
||||||
|
// pln("** ")
|
||||||
|
// pln("slice tail: ")
|
||||||
|
// print(slice_tail)
|
||||||
|
// pln("...........................")
|
||||||
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
12
serve.go
12
serve.go
@ -5,19 +5,25 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"./config"
|
"./config"
|
||||||
|
"./audio"
|
||||||
"./playlist"
|
"./playlist"
|
||||||
"./www"
|
"./www"
|
||||||
)
|
)
|
||||||
|
|
||||||
var pln = fmt.Println
|
var pln = fmt.Println
|
||||||
|
var p *playlist.Playlist = nil
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
config.Loadconfig("config.ini")
|
config.Loadconfig("config.ini")
|
||||||
|
|
||||||
p, err := playlist.MakeRandom("YOYO", 10)
|
p, err := playlist.MakeRandom("YOYO", 10)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
p.Pop()
|
|
||||||
www.Init(p)
|
www.Init(p)
|
||||||
|
|
||||||
|
go audio.Start(p.Pop)
|
||||||
|
|
||||||
log.Fatal(http.ListenAndServe(":8718", nil))
|
log.Fatal(http.ListenAndServe(":8718", nil))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,13 +19,10 @@
|
|||||||
console.log("update")
|
console.log("update")
|
||||||
list = Array.from(document.querySelectorAll('#sortable>li'));
|
list = Array.from(document.querySelectorAll('#sortable>li'));
|
||||||
var i = list.indexOf(u.item[0])
|
var i = list.indexOf(u.item[0])
|
||||||
console.log(u.item.attr("id") + " is now " + i)
|
|
||||||
on_update_operation(u.item.attr("id"), i)
|
on_update_operation(u.item.attr("id"), i)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$( "#sortable" ).disableSelection();
|
$( "#sortable" ).disableSelection();
|
||||||
|
|
||||||
// connect to ppop
|
|
||||||
sock = new WebSocket("ws://localhost:8718/ppop");
|
sock = new WebSocket("ws://localhost:8718/ppop");
|
||||||
sock.onopen = function() {
|
sock.onopen = function() {
|
||||||
console.log("ppop open")
|
console.log("ppop open")
|
||||||
@ -34,31 +31,63 @@
|
|||||||
console.log("ppop closed")
|
console.log("ppop closed")
|
||||||
sock = new WebSocket("ws://localhost:8718/ppop");
|
sock = new WebSocket("ws://localhost:8718/ppop");
|
||||||
}
|
}
|
||||||
|
|
||||||
sock.onmessage = function(msg) {
|
sock.onmessage = function(msg) {
|
||||||
console.log(msg.data)
|
check_fix_list(JSON.parse(msg.data))
|
||||||
}
|
}
|
||||||
|
|
||||||
// setInterval(function() {
|
|
||||||
// sock.send(JSON.stringify({"op": "tick", "id": "0", "index": 0}));
|
|
||||||
// },3000);
|
|
||||||
|
|
||||||
} );
|
} );
|
||||||
var sock = null;
|
var sock = null;
|
||||||
function on_update_operation(id, index) {
|
function on_update_operation(id, index) {
|
||||||
if(sock) {
|
if(sock) {
|
||||||
console.log("sending")
|
console.log("sending")
|
||||||
sock.send(JSON.stringify({"op": "move", "id": id, "index": index}));
|
sock.send(JSON.stringify({"op": "move", "id": id, "index": index}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function check_fix_list(playlist) {
|
||||||
|
|
||||||
|
console.log(playlist)
|
||||||
|
|
||||||
|
$("#pname").text("Playlist: " + playlist.name)
|
||||||
|
$("#tname").text("Current Track: " + playlist.ctrack)
|
||||||
|
$("#aname").text("Current Album: " + playlist.calbum)
|
||||||
|
|
||||||
|
|
||||||
|
list = Array.from(document.querySelectorAll('#sortable>li'))
|
||||||
|
|
||||||
|
|
||||||
|
console.log("length ul: " + list.length)
|
||||||
|
console.log("length playlist: " + playlist.list.length)
|
||||||
|
|
||||||
|
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
if(i >= playlist.list.length) {
|
||||||
|
break
|
||||||
|
} else if (list[i].id != playlist.list[i]["id"]) {
|
||||||
|
console.log("difference at index: " + i)
|
||||||
|
list[i].setAttribute("id", playlist.list[i]["id"])
|
||||||
|
list[i].innerHTML = "<span>" + playlist.list[i]["maker"] +
|
||||||
|
" - " + playlist.list[i]["album"] +
|
||||||
|
" - " + playlist.list[i]["name"] + "</span>" ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ul = document.getElementById('sortable');
|
||||||
|
if(list.length > playlist.list.length) {
|
||||||
|
for(let i = playlist.list.length; i < list.length; i++) {
|
||||||
|
ul.removeChild(list[i])
|
||||||
|
}
|
||||||
|
} else if (list.length < playlist.list.length) {
|
||||||
|
|
||||||
|
}
|
||||||
|
console.log("synched!!")
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<title>Radiodiodio</title>
|
<title>Radiodiodio</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Playlist: {{.NAME}}</h1>
|
<h1 id="pname">Playlist: {{.NAME}}</h1>
|
||||||
|
<audio controls src="http://192.168.1.80:8088/wwww"></audio>
|
||||||
<h2>Current Track: {{.CTRACK.NAME}} </h2>
|
<h2 id="tname">Current Track: {{.CTRACK.NAME}}</h2>
|
||||||
<h2>Current Album : {{.CALBUM.NAME}} - {{.CALBUM.MAKER}}</h2>
|
<h2 id="aname">Current Album : {{.CALBUM.NAME}} - {{.CALBUM.MAKER}}</h2>
|
||||||
|
|
||||||
<ul id="sortable">
|
<ul id="sortable">
|
||||||
{{range $.LIST}}
|
{{range $.LIST}}
|
||||||
|
|||||||
36
www/www.go
36
www/www.go
@ -10,9 +10,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// todo: hub - https://stackoverflow.com/questions/31532652/go-websocket-send-all-clients-a-message
|
// todo: hub - https://stackoverflow.com/questions/31532652/go-websocket-send-all-clients-a-message
|
||||||
|
var wsc *websocket.Conn
|
||||||
|
|
||||||
var pp *playlist.PrettyPlaylist
|
|
||||||
var p *playlist.Playlist
|
var p *playlist.Playlist
|
||||||
|
var pp *playlist.PrettyPlaylist
|
||||||
|
var mp *playlist.MinimalPlaylist
|
||||||
|
|
||||||
var pln = log.Println
|
var pln = log.Println
|
||||||
|
|
||||||
@ -29,7 +31,9 @@ var upgrader = websocket.Upgrader{
|
|||||||
|
|
||||||
func Init(playlist *playlist.Playlist) {
|
func Init(playlist *playlist.Playlist) {
|
||||||
p = playlist
|
p = playlist
|
||||||
|
p.CALLB = pop_callback
|
||||||
pp = p.Pretty()
|
pp = p.Pretty()
|
||||||
|
mp = p.Minimal()
|
||||||
http.HandleFunc("/pp", pp_handler)
|
http.HandleFunc("/pp", pp_handler)
|
||||||
http.HandleFunc("/ppop", pp_operations)
|
http.HandleFunc("/ppop", pp_operations)
|
||||||
}
|
}
|
||||||
@ -51,10 +55,11 @@ func pp_operations(w http.ResponseWriter, r *http.Request) {
|
|||||||
pln(err)
|
pln(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
wsc = c // this should be hub
|
||||||
go readop(c)
|
go readop(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func readop(c *websocket.Conn) {
|
func readop(c *websocket.Conn) {
|
||||||
for {
|
for {
|
||||||
opdata := &op_t{}
|
opdata := &op_t{}
|
||||||
if err := c.ReadJSON(&opdata); err != nil {
|
if err := c.ReadJSON(&opdata); err != nil {
|
||||||
@ -66,27 +71,24 @@ func readop(c *websocket.Conn) {
|
|||||||
if id, err := strconv.ParseUint(opdata.ID, 10, 32); err == nil {
|
if id, err := strconv.ParseUint(opdata.ID, 10, 32); err == nil {
|
||||||
p.Move(uint32(id), opdata.INDEX)
|
p.Move(uint32(id), opdata.INDEX)
|
||||||
pp = p.Pretty()
|
pp = p.Pretty()
|
||||||
pp.Print()
|
mp = p.Minimal()
|
||||||
|
// pp.Print()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pln(opdata.OP)
|
if err := c.WriteJSON(mp); err != nil {
|
||||||
pln(opdata.ID)
|
|
||||||
pln(opdata.INDEX)
|
|
||||||
if err := c.WriteJSON(opdata); err != nil {
|
|
||||||
pln(err)
|
pln(err)
|
||||||
return //connection lost?
|
return //connection lost?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// func pp_operations(ws *websocket.Conn) {
|
func pop_callback(list *playlist.Playlist) {
|
||||||
// opdata := &op_t{}
|
if list == p {
|
||||||
// if err := websocket.JSON.Receive(ws, &opdata); err != nil {
|
pp = p.Pretty()
|
||||||
// pln(err)
|
mp = p.Minimal()
|
||||||
// }
|
if wsc != nil {
|
||||||
// // websocket.JSON.Send(ws, "ok")
|
wsc.WriteJSON(mp) // this should be hub broadcasted
|
||||||
// pln(opdata.OP)
|
}
|
||||||
// pln(opdata.ID)
|
}
|
||||||
// pln(opdata.INDEX)
|
}
|
||||||
// }
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user