A Go program displays song lyrics line by line

Music in YAML

The songFinder() function in Listing 3 is used to find YAML files with lyrics. Go supports the conversion of YAML data into Go structures as a built-in feature. It only requires the programmer to decorate Go structures, such as Lyrics from line 12, with instructions on the YAML format if the keys in the YAML text differ from the Go structure's attribute names.

Starting in line 12, Lyrics stipulates in reverse quotes that keys in YAML conventionally start with lowercase letters, while publicly accessible fields in Go structures are uppercase. For example, the first line of the Lyrics structure uses

Song string `yaml: "song"`

to stipulate that the Song field is of the type string, but is now named song in YAML instead of Song.

With this decoration, the Unmarshal() function in line 46 effortlessly converts the YAML data into the internal format of the Lyrics struct, without the programmer having to do anything else.

This only leaves the songFinder() to call filepath.Walk() and walk through all the .yaml files (or .yml, as matched by the regular expression) below the given directory, call parseSongFile() for each file found, and feed the data below the artist/title key to the lyrics map in line 33.

The songFinder() function returns the result as a variable and, in line with the Go convention, an error code set to nil if everything worked as planned, and if not, for the main program to take a closer look at what happened.

Event-Driven Actions

Managing the terminal UI, which consists of two superimposed list boxes of the same size, justifies a separate function handleUI() in Listing 4. To determine which list box is on top and therefore visible, the function updates the inFocus variable, setting it either to the main menu (lb) or the text window (ltext) list box.

As is the wont of graphical user interfaces, line 19 triggers an infinite loop whose only action is a select statement. It receives events from the uiEvents channel, sent out by the termui library for business logic to consume. For example, if the user presses Q, an event whose ID field is set to q is propelled through the channel. The case handler in line 23 then triggers a return, which terminates handleUI() and accordingly the calling main program.

Actions related to arrow keys only matter if inFocus indicates that the main menu is active. In this case, lines 27 and 32 call the ScrollDown() and ScrollUp() list box widget's functions. This is followed by the ui.Render(lb) command, which redraws the widget. This is critically important, because otherwise the user wouldn't visually notice the change.

If the channel uiEvents returns an event for the Enter key, the further processing depends on which mode is currently active. If the user is in the main menu, inFocus is set to lb, and line 37 uses the numerical index of the selected list box entry to retrieve its text representation (i.e., the artist and title) from the list box.

The if block then sets the inFocus variable to ltext, activating the lyrics window in the process. A scanner, newly defined in line 41, grabs the text string with the lyrics lines from the lyrics structure and returns the next string line each time its Scan() method is called. The switch from the main menu to the lyrics mode is initiated by line 39. This is also visualized for the user as soon as the lyrics list box is passed to ui.Render().

When the user presses Enter in lyrics mode, the if block starting in line 45 is used. The text scanner retrieves the next line of the song from the multiple-line string in the YAML data starting in line 47; it discards any blank lines and appends newly read lines to the array slice of the ltext list box. ScrollDown() marks the new line in the display as the selected element and highlights its text in red. Again, the whole thing is only visualized after calling ui.Render().

If the song ends (i.e., the scanner fails to deliver any further lines), the if block deletes the text lines from the ltext listbox starting in line 58 and then switches back to main menu mode by pointing inFocus at lb. The same thing happens if the user presses Esc; in this case, the case block switches to the main menu starting in line 64.

Quickly Built

To create the lyrics binary file, which controls the tool from A to Z, you simply compile all three listings, as shown in Listing 5. The initial call to go mod initializes a new Go module that tells the following go build to fetch all the required packages from GitHub and include these as well.

Listing 5

Generating a Binary

$ go mod init lyrics
$ go build lyrics.go find.go uihandler.go

After the successful build, make sure to fill your data directory with a few song lyrics in YAML format, then call lyrics, and remember, sing only at moderate volume to be considerate of your neighbors!

The Author

Mike Schilli works as a software engineer in the San Francisco Bay area, California. Each month in his column, which has been running since 1997, he researches practical applications of various programming languages. If you email him at mailto:mschilli@perlmeister.com he will gladly answer any questions.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Linux Magazine

Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

  • Waxing Lyrical

    Whether you listen to music on Spotify or a classic audio player like Rhythmbox, Lollypop, or Audacious, a tool named Lyrics-in-terminal will let you read the lyrics for the track you are currently playing.

  • ChordPro

    If you play a stringed instrument and want to record lyrics and chord changes, ChordPro gives you an elegant and convenient approach to getting it all on paper.

  • Introduction

    This month in Linux Voice.

  • Sayonara Player

    For a simple audio player, check out Sayonara Player, a great choice for enjoying all your favorite music, Internet radio, and podcasts.

  • Free Software Projects

    Linux is a wonderful and underrated audio production platform, with great applications for every audio task. MuseScore and LilyPond bring elegance and sophistication to score writing, and Chordii is a wonderfully simple guitar sheet-music maker.

comments powered by Disqus
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters

Support Our Work

Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.

Learn More