Music For Studying With mpv and dmenu
During my undergraduate years at UT, I started listening to LoFi-HipHop, a genre of music that's very mellow and soft. It's usually a slow beat accompanied with some melody or an old sampled record. And the music is surprisingly very focus-inducing. People typically play it as background music when sudying or playing video games.
Since
then, I play it everytime I have to get into the zone of studying, but
opening up the browser and youtube lofi hiphop can get cumbersome, and
at times cause me to lose track of certain thoughts. And so, because of
that and partly because I'm lazy, I wanted to play lofi with a single
key binding on my keyboard. What makes this task so tangible is the
fact that mpv can take in a link and only play the audio with
the --no-video
flag. So with this simple shell
command
mpv --no-video "https://www.youtube.com/watch?v=5qap5aO4i9A" &>/dev/null
one can easily alias this to a shorter command and play music whenever. But I also wanted to get a prompt with not only lofi, but also piano, or rain sounds, or basically kind of ambient noise that helps me focus. In addition to that, I wanted the script to use dmenu so that I can get a prompt of options outside of the terminal; and having the script be portable in a way were everytime I want to add a new sound, all I need to input would be the link of that sound and its name.
So to start off, I already know the script needs mpv, xclip, dmenu, libnotify to run. It's pretty obvious at this point why I need mpv. I prefer having some sort of feedback when choosing a sound to play, like "Hey piano is playing now", libnotify can output a notification to the screem with whatever message you feed it. I need dmenu to output standard output, and choose the type of sound I want to play. Lastly, and this is just an extra thing, which I don't really need but would be useful is copying a youtube link and having the script read my clipboard; this is where xclip comes to play, as it can dump clipboard output and feed it into mpv. To check for those programs, I wrote a dependency function which checks for each, and if any of the programs gives an error exit code, it echos the programs to
dependency_check () { [[ -z $(which dmenu) || -z $(which notify-send) || -z $(which mpv) || -z $(which xclip) ]] &>/dev/null && echo -e "Make sure you have the following dependencies:\n+ dmenu\n+ libnotify\n+ mpv\n+ xclip" && exit }
Now for the bread and butter of the script, I created an associative array (urls
) which I can feed the name and the link of to any new sound I want, and I can keep adding to it as I please
declare -A urls=( [Lofi]="https://www.youtube.com/watch?v=5qap5aO4i9A" [Piano]="https://www.youtube.com/watch?v=XULUBg_ZcAU" [Classical]="https://live.musopen.org:8085/streamvbr0" [Jazz]="https://www.youtube.com/watch?v=B2fKHoG_aLU" [Rain]="https://www.youtube.com/watch?v=mPZkdNFkNps" [Cafe]="https://www.youtube.com/watch?v=VMAPTo7RVCo" [Nature]="https://www.youtube.com/watch?v=eKFTSSKCzWA")
This associative array contains seven sounds that I use regularly, and like I said earlier, I can just keep adding to it. Now to display these entries, I feed them into a variable all_selection
through the printf
command. I also add three extra keywords for playing a random sound, playing from clipboard and stopping the music that's already playing, if there is any. I then have dmenu
output all these sound names and keywords as standard output
printf -v all_selection '%s\n' ${!urls[@]} Clipboard Local Random Stop selection=$(dmenu -i -p "What to play?" <<< "$all_selection") && pause
Reading the second line, there's this weird function pause()
that gets called after selecting a sound. This is just to kill the mpv process of that specifi user, because I do use that pause function a lot, and I did not want to keep calling the pkill command everytime.
pause () { pkill -u $USER -i mpv &>/dev/null }
To be able to shuffle between the sounds and pick a random sound, if the keyword Random
was selected, a function random_play
is called, which is pretty self explanatory
random_play () { local -a channel=("${urls[@]}") pause mpv --no-video "${channel[RANDOM % ${#channel[@]}]}" &>/dev/null }
Finally, adding some logic to the script with an if statement at first, if one of the sounds was chosen, then moving on to a case statement if anything is chosen
if [ "${urls[$selection]}" ]; then mpv --no-video "${urls[$selection]}" &>/dev/null else case $selection in Clipboard) mpv --no-video "$(xclip -o)" &>/dev/null ;; Random) random_play ;; Stop) pause && exit ;; Local) printf -v local_song '%s\n' "$(ls $HOME/Music/)" && chosen_song="$(dmenu -i -p "Local song" -l 20 <<< "$local_song")" && mpv --no-video "$HOME/Music/$chosen_song" &>/dev/null ;; *) notify-send "Nothing played" && exit ;; esac fi
Feel free to use the script here or clone it from my github.
--------------------------------------------------------