Video Demo: https://youtu.be/puraf7z56Hc
Final project for CS50Python through HarvardX. A music visualizer that chooses colors based on words in each song's title, so that the visualization will match the music's mood, or at least its name. For example, the song "Almost Blue" by Elvis Costello would be visualized in shades of blue, as in this picture:
This project is the beginning of (I hope!) a larger project for a context-aware music visualizer. Over the years, I've enjoyed using music visualizers built into software such as iTunes, but they've always been lacking something. A sad, mournful song might be visualized with happy, upbeat colors. A song with "blue" in the title might be visualized in red. It kind of drives me crazy. If a human was actively controlling the animation, they'd probably choose colors and shapes that work well with the music, but none of the visualizers I've used have had that capability.
Thus, the Musicolorizer. It is a simple visualizer that compares song titles to words in a predefined dictionary, and if it finds a match, it uses the colors defined for that dictionary match to visualize the music when it plays. If there are no matches, it generates random colors.
The libraries used in this project include:
- librosa (music analysis)
- pygame (visual display)
- numpy (mathy array stuff used to work with the spectrogram, short-time Fourier analysis, etc.)
- random (generating random colorways)
- os environ (setting up the use of ffmpeg to work with sound files)
The program begins with two settings that define the slices of time that make up the song's spectrogram. n_fft is the frame length, the "number of samples in an analysis window (or frame)."1 hop_length is "The number of samples between successive frames, e.g., the columns of a spectrogram."1
The colormap is a small dictionary listing certain keywords that may be found in song titles, and the colors that go along with them. In a fully-fledged visualizer, there would be many more of these, but for the purposes of this project, I just used a few that were in songs I had on hand. This is the current colormap:
| Keyword | Color |
|---|---|
| blue | blue |
| sad | blue |
| winter | blue |
| happy | yellow |
| love | pink, red |
| red | red |
| green | true green |
| spring | yellow-green |
| daydreams | purple/magenta |
These functions are used to process the song's title (case-insensitively), search the colormap for matching colorways, and generate random colors if there is no match to the colormap dictionary.
ColorBar is a class that defines the vertical colored bars in the visualizer including height and color (inherited from the colorway definition mentioned earlier), and renders the bars using pygame.draw.rect().2
limitation is a function forcing the values of decibel to stay in a certain range, to be used when defining the gradient effect in the colorbars.
This function gets the spectrogram information for a specific location in the track, to be used to generate a colorbar.
The main function controls the overall operation of the program. It begins by getting the song title from user input, setting the colorway using get_colorway, then loading the song to analyze with librosa. It uses librosa.amplitude_to_db() and librosa.mel_frequencies() to convert the spectrogram into a dB-scaled spectrogram3 and compute acoustic frequencies.4 librosa.frames_to_time() then converts frame counts into seconds.5 At this point, pygame comes into the picture! Display settings such as pygame.display.info()6 and pygame.display.set_mode()7 are used to define and initialize the visualizer screen. Colorbars are generated, then pygame.mixer.music.load()8 and pygame.mixer.music.play()9 load and play the music. pygame.display.flip()10 is used to update the screen. The program runs until the user closes the visualizer.
Some choices were made out of frustration, some because I am still new to this and did not feel comfortable expanding further, and some because they were lower priority. These are things I didn't do, that I would change if I continue with this:
- Playlists, not one song at a time
- Selecting songs, not typing them in
- More colorways in the colormap
- Combination colorways if a song has two dictionary words in the title
- Overlaying lyrics
- GUI
- Perhaps varying the shapes; not always vertical rectangles
- Turning it into some kind of plug-in that could be used with a music player?
- ...and more that I'm probably forgetting now!
Other choices were done because I wanted them!
- The bars being the full height of the window because I like the way it looks
- Songs without dictionary terms being random colors, not a single "undefined" colorway, which was boring
I had extreme trouble figuring out librosa from the docs, which are highly technical and seem to target music engineers, which I am not. The docs were more helpful once I had a hint of where to start. Pygame wasn't quite as difficult but it was hard to know where to start with that, as well, since I wasn't building a game and lots of tutorials are focused on games. These are used to figure out how to use pygame and librosa, and to explain many terms and how to make the libraries do what I needed to do:
- https://medium.com/analytics-vidhya/how-to-create-a-music-visualizer-7fad401f5a69
- https://www.pygame.org/docs/
- https://librosa.org/doc/latest/
- https://www.reddit.com/r/VideoEditing/comments/ssci0l/comment/hwxw2fd/
- https://medium.com/@chaosqueenbee/audio-visualizer-6571b9085f0e -- the most helpful -- clear explanations and her visualizer inspired the look of mine. I did use code snippets from her work.
- https://khareanu1612.medium.com/audio-signal-processing-with-spectrograms-and-librosa-b66a0a6bc5cc
Footnotes
-
[https://www.pygame.org/docs/ref/draw.html#pygame.draw.rect] ↩
-
[https://librosa.org/doc/main/generated/librosa.amplitude_to_db.html] ↩
-
[https://librosa.org/doc/main/generated/librosa.mel_frequencies.html] ↩
-
[https://librosa.org/doc/latest/generated/librosa.frames_to_time.html] ↩
-
[https://www.pygame.org/docs/ref/display.html#pygame.display.Info] ↩
-
[https://www.pygame.org/docs/ref/display.html#pygame.display.set_mode] ↩
-
[https://www.pygame.org/docs/ref/music.html#pygame.mixer.music.load] ↩
-
[https://www.pygame.org/docs/ref/music.html#pygame.mixer.music.play] ↩
-
[https://www.pygame.org/docs/ref/display.html#pygame.display.flip] ↩




