Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
October 27, 2022 06:44 pm GMT

Build A K-pop Radio in Go!

The full code can be found here:
https://github.com/raymond-design/kpop-cli

Intro:

We'll be learning how to use the Gorilla websockets websocket client and faiface/beep to stream K-pop music

Setup the project:

Let's first init our Go project by running:
go mod init [project name]

We'll need to download two libraries:

Beep:
go get github.com/faiface/beep

Gorilla Websocket:
go get github.com/gorilla/websocket

Start coding!

It will be helpful if we first setup our project structure.

First create a main.go file at in our project directory. This will be our entrypoint.

Then create 4 more directories:
connect
play
types
ui

The project structure should look something like this (I also recommend creating a .gitignore if you plan on pushing to git:

The Project opened up in VsCode

User Interface

Let's first create a file inside the ui folder. We name the file ui.go.

This file will define a function that prints song info the terminal! First let's import the "fmt" package:

package uiimport (    "fmt")

Now let's create a function named WriteToFunction. Make sure to capitalize the first letter (since we'll use it elsewhere):

func WriteToScreen(name string, author string, album string) {    fmt.Print("\033[H\033[2J")    fmt.Println("Now Playing:")    fmt.Println("Title: " + name)    fmt.Println("Artist: " + author)    if album != "" {        fmt.Println("Album: " + album)    }}

ui.go looks like this:

ui.go

Define Types

A helpful pattern in Go is to define related struct types in one place. Let's create a types.go file in the types directory.

The song info will be in json format. First import that:

package typesimport "encoding/json"

Next, we need to describe some types for WebSockets connection:

type SocketRes struct {    Op int64 `json:"op"`    D  json.RawMessage}type SendData struct {    Op int64 `json:"op"`}type HeartbeatData struct {    Message   string `json:"message"`    Heartbeat int64  `json:"heartbeat"`}

Next, we will define some structs related to the songs themselves(Song, Album, etc.):

type PlayingData struct {    Song       Song        `json:"song"`    Requester  interface{} `json:"requester"`    Event      interface{} `json:"event"`    StartTime  string      `json:"startTime"`    LastPlayed []Song      `json:"lastPlayed"`    Listeners  int64       `json:"listeners"`}type Song struct {    ID       int64         `json:"id"`    Title    string        `json:"title"`    Sources  []interface{} `json:"sources"`    Artists  []Album       `json:"artists"`    Albums   []Album       `json:"albums"`    Duration int64         `json:"duration"`}type Album struct {    ID         int64   `json:"id"`    Name       string  `json:"name"`    NameRomaji *string `json:"nameRomaji"`    Image      *string `json:"image"`}

Now that we finished definitions, we can create the client to stream audio!

Create the WebSocket Client

Head over to the connect directory and create a connect.go file.

In this package, we'll need to import Gorilla websocket and the two packages we've already created:

package connectimport (    "encoding/json"    "log"    "time"    "github.com/raymond-design/kpop-cli/types"    "github.com/raymond-design/kpop-cli/ui"    "github.com/gorilla/websocket")

We also need to define 3 package-level variables:

var conn *websocket.Connvar done = falsevar ticker *time.Ticker

Let's a create a function to initialize the connection:

func Start(url string) { }

(Later on, url string will be the WebSocket server url that we want to stream from)

Now paste the following:

conn_l, _, err := websocket.DefaultDialer.Dial(url, nil)if err != nil {    log.Fatal("Couldn't connect to websocket")}conn = conn_l

If the conn doesn't work, there could be an error with the URL!

Now, let's run an anonymous function Goroutine to maintain the WebSocket connection:

go func() {        for {            if done {                conn.Close()                break            }            _, msg, err := conn.ReadMessage()            if err != nil {                log.Fatal("Couldn't read websocket message")            }            handleMessage(msg)        }}()

We will keep on maintaining the connection until program break or a read error. The function should look something like this:

function code

Now we need to implement that handleMessage function!

func handleMessage(in []byte) {    var msg types.SocketRes    json.Unmarshal(in, &msg)    switch msg.Op {    case 0:        var data types.HeartbeatData        json.Unmarshal(msg.D, &data)        setHeartbeat(data.Heartbeat)    case 1:        var data types.PlayingData        json.Unmarshal(msg.D, &data)        album := "None"        if len(data.Song.Albums) > 0 {            album = data.Song.Albums[0].Name        }        ui.WriteToScreen(data.Song.Title, data.Song.Artists[0].Name, album)    }}

In the start function, we continually call this function which will grab the current song data and print it.

To make the code cleaner, the actual set heartbeat logic will be in two other functions:

func sendHeartBeat() {    data := types.SendData{        Op: 9,    }    conn.WriteJSON(data)}func setHeartbeat(repeat int64) {    sendHeartBeat()    ticker = time.NewTicker(time.Duration(repeat) * time.Millisecond)    go func() {        <-ticker.C        sendHeartBeat()    }()}

If you want to read more about WebSockets connections, here's a helpful article:
https://www.programmerall.com/article/821816187/

Finally, we just need a stopping function that will break out of that for loop:

func Stop() {    ticker.Stop()    done = true}

Now that we have these WebSockets connection functions, we can bring sound into the app!

Including Sound!

To bring in sound, we will be importing faiface/beep:

package playimport (    "log"    "net/http"    "time"    "github.com/faiface/beep"    "github.com/faiface/beep/mp3"    "github.com/faiface/beep/speaker")

We will also create a global var from this beep package:

var stream beep.StreamSeekCloser

We will need two functions. One to play and one to stop.

The play function is quite simple. We will check the validity of the http url and then use the beep/mp3 to starting streaming audio contents!

func Play(url string) {    resp, err := http.Get(url)    if err != nil {        log.Fatal("http error")    }    l_streamer, format, err := mp3.Decode(resp.Body)    stream = l_streamer    if err != nil {        log.Fatal("decoding error")    }    speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/10))    speaker.Play(stream)}

The stop function is even simpler. We just close the stream:

func Stop() {    stream.Close()}

The code looks something like this:

Play Audio Code

Project Entrypoint

Now we can create the entrypoint to our app! Let's import our packages:

package mainimport (    "fmt"    "os"    "os/signal"    "github.com/raymond-design/kpop-cli/connect"    "github.com/raymond-design/kpop-cli/play")

Now let's define the server URL that we'll stream from:

const JPOP string = "https://listen.moe/fallback"const KPOP string = "https://listen.moe/kpop/fallback"const JSOCKET string = "wss://listen.moe/gateway_v2"const KSOCKET string = "wss://listen.moe/kpop/gateway_v2"

By the way, you can also stream J-pop music now!

Now create the main function:

func main(){    c := make(chan os.Signal, 1)    signal.Notify(c, os.Interrupt)    mode := "kpop"    var stream string    var socket string    if len(os.Args) == 2 {        mode = os.Args[1]    }}

We can use a switch function to switch between K-pop and J-pop music:

switch mode {    case "kpop":        stream = KPOP        socket = KSOCKET    case "jpop":        stream = JPOP        socket = JSOCKET    default:        fmt.Println("Error")        os.Exit(1)}

Now, we can connect and start streaming music!

connect.Start(socket)play.Play(stream)interrupt := make(chan os.Signal, 1)signal.Notify(interrupt, os.Interrupt)<-interruptfmt.Println("Exiting Player")play.Stop()connect.Stop()

(Notice we stop first stop decoding audio, then disconnect from the WebSockets server)

The main function looks like this:

Main Function

Listening to the radio

  • Run a go get to get all dependencies.
  • Run go build . in the project.
  • Run ./kpop-cli kpop to play K-pop music or ./kpop-cli jpop (If you implemented J-pop).

Now you know how to implement sound and WebSocket streaming in Go!

Also try streaming other types of data in the future



The full code can be found here:
https://github.com/raymond-design/kpop-cli


Original Link: https://dev.to/rayfrompsu/build-a-k-pop-radio-in-go-2080

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To