In this article I describe how you can customize and pimp your Linux-Terminal with customized sound for specified events.
Requirements:
- Difficulty: Easy
- Needs:
- mplayer, vlc or similar
- bash basics
- linux basics
- some short sound files
- that's it, you are good to go 😉
Introduction
Hey,
after the first part of ricing your system, we are working with sound now...
I had the idea for this script already years ago. Some time after playing through the Portal game series. Back then I was mainly using Windows.
I really like the sounds the turrets are making.
So I wondered why not use those sounds somewhere else? Maybe let my PC play them for me at certain events? There surely is a time where it fits.
But first I needed the sounds, so I googled and found a wiki page which provides all the sound files of the turrets. And even better I found a forum with a zip with all sounds included. So we don't have to download them individually, yay 😀
First Implementation
So, under Windows, it was obvious to open up the controls and just change the sounds to the ones I just downloaded and extracted. And we are done.
Now some years later, I came up with this idea again. But this time while using Linux. The problem: in most distros are no default sounds or even a control panel for it, especially not in Arch, which is what I am running. So I had to find a different solution. Something custom. My solution: we simply script it ourself in bash
Introduction TODO
I installed mplayer, although every other via console usable player should be right. (I switched and I am now using cvlc, the console version of the vlc player. Works like charm.)
If you are using vlc like I am now, just type cvlc --play-and-exit instead of mplayer in every file we will create.
Then I looked through the sounds available and picked the ones, I want to use. And I renamed them, so I could access them easily via bash. And then I wrote myself a (very) little bash script:
#!/bin/bash RAND=$[ $RANDOM % 20 ] mplayer /home/lukas/scripts/error/error_$RAND.wav >/dev/null 2>&1 &
So what did we just type?
The shebang of course, to say with what we want to run our program.
Then to make things random and not the same every time, a random number in range of the amount of available files.
And then I call mplayer (or whatever you want to use; for me now cvlc see above ) with the file name according to the use case, so in this case for error sounds. Every file with the formatvideo game error_*.wav will be played. Which sounds you use doesn't matter. You can use the portal sounds like I did, then you will hear something like "ahou", "ohh", "please stop" or "critical error". But if you have the sound files of any other video game or similar you can use them as well. (Maybe you like the sounds of cl4p-tp from Borderlands more. Sounds from Borderlangs can also be downloaded)
And then I redirect the terminal output to /dev/null to not get it in my console every time the script is called. This would be very annoying. And would make it hard to read what you were doing.
And we want this all to run in the background. So we can continue while our sound is being played and we don't have to wait. Therefore we put an & at the end so bash will run it "parallel" in the background.
Smy events when a sound should be played:
- on start up
- before shutdown
- on error in terminal
- while find/locate
- while pacman
- while whoami
- before lockscreen (if you got one)
- while unpacking zips/rars
Screensaver
The scripts look a lot alike, but there are some small differences. So pay close attention.
The first sound will play right before your system locks into a screensaver. This will only work if you have any chance to execute a script before your system gets locked.
When I created this script, I was using i3lock, the lock screen which suits the i3 window manager.
Because I modified the look of i3lock, I already called a script which then calls i3lock. I just had to add this one line.
#!/bin/bash /home/lukas/scripts/screensaver.sh ICON=/home/lukas/scripts/logos/asset1.png ... i3lock -u -i $TMPBG
xautolock was started on system start to execute this file after 15mins.
We don't want to wait for the sound to finish playing, it is already taking long enough to lock the screen. Therefor the & at the end of the line.
And the screensaver.sh looks like this:
#!/bin/bash RAND=$[ $RANDOM % 4 ] mplayer /home/lukas/scripts/screensaver/screensaver_$RAND.wav >/dev/null 2>&1
The matching files have to be called screensaver_[index].wav or the code has to be change accordingly.
Name and the file format don't matter. As long as they are all the same. And you need some sort of indexing across the files.
Console error
The code looks almost the same.
#!/bin/bash RAND=$[ $RANDOM % 20 ] mplayer /home/lukas/scripts/error/error_$RAND.wav >/dev/null 2>&1 &
Using Bash
But now to the call.
As you see, my prompt is making a little smiley [^_^]. And that's because of my configuration of my .bashrc where I set my prompt according to the success of a command.
If I get a positive response ^_^ in green, on a failure 0_0 in red.
The code for the smileys alone looks like this:
PS1='$(if [[ $? == 0 ]]; then echo ""\n┌[\e[0;32m^_^""; else echo ""\n┌[\e[1;31m0_0""; fi)\e[0m]-[\h]-[\e[0;32;49m\w\[\e[0m]\n└>'
But you might already can guess where this is going.
So this is the code to set the PS1 alias the prompt. And then after echoing 0_0, we simply call our script to make a matching sound:
PS1='$(if [[ $? == 0 ]]; then echo ""\n┌[\e[0;32m^_^""; else echo ""\n┌[\e[1;31m0_0""; /home/lukas/scripts/error.sh & fi)\e[0m]-[\h]-[\e[0;32;49m\w\[\e[0m] \n└>'
And like this, every time we enter a command the PS1 gets set and we are right there. We will get back to the .bashrc later.
Using ZSH and oh-my-zsh
If you use ZSH as your default shell, you surely have oh-my-zsh, a framework to manage your ZSH config, installed.
In this case, your shell won't use the .bashrc. Instead it uses .zshrc, so you need to configure your prompt there. But if you are using a theme, the prompt configuration is in the theme file you selected.
You can find all downloaded themes under ~/.oh-my-zsh/themes/.
I modified the agnoster default themes to my needs:
prompt_status() {
local symbols
symbols=()
[[ $RETVAL -ne 0 ]] && symbols+="%{%F{red}%}✘"
[[ $RETVAL == 0 ]] && symbols+="%{%F{green}%}^_^"
[[ $UID -eq 0 ]] && symbols+="%{%F{yellow}%}⚡"
[[ $(jobs -l | wc -l) -gt 0 ]] && symbols+="%{%F{cyan}%}⚙"
[[ -n "$symbols" ]] && prompt_segment black default "$symbols"
[[ $RETVAL -ne 0 ]] && /home/lukas/scripts/error.sh &
}
Startup
Now, startup is really easy. I used the XFCE session and startup tool, which either is pre-installed with XFCE or comes with the utils package.
If you use a different desktop environment, you can go for their program doing this kind of stuff. An other option would be to create a .desktop file by hand or create a daemon or a service which calls our script on startup. Be sure to active the daemon so it will work.
I'm not going into each option, there are tutorials which tell you how to set this up.
That's how XFCE makes it: Greet.desktop.
[Desktop Entry]
Encoding=UTF-8
Version=0.9.4
Type=Application
Name=Greet
Comment=
Exec=/home/lukas/scripts/startup.sh
OnlyShowIn=XFCE;
StartupNotify=false
Terminal=false
Hidden=false
startup.sh:
#!/bin/bash
RAND=$[ $RANDOM % 10 ]
mplayer /home/lukas/scripts/startup/startup_$RAND.wav >/dev/null 2>&1 &
Shutdown
But now for the shutdown sound, we need to create a service. All other possibilities that I tried didn't work.
Either did the system already hang out my drive or the sound drivers aren't working anymore, or the system does not wait for the sound to finish playing. That was probably the hardest sound to get playing. [ein bischen weiter ausführen]
But I got it working now, and it's really awesome.
systemctl daemon-reload [??warum???]
/etc/systemd/system/shutdown.service:
[Unit]
Description=Shutdown Sound
[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=/bin/true
ExecStop=/home/lukas/scripts/shutdown.sh
[Install]
WantedBy=multi-user.target
shutdown.sh:
#!/bin/bash
RAND=$[ $RANDOM % 16 ]
mplayer /home/lukas/scripts/shutdown/shutdown_$RAND.wav >/dev/null 2>&1
Pay attention that we don't want to put an & at the end, otherwise GNU Linux won't wait with shutting down until the sound is completely played
Then enable and start the service with:
systemctl start shutdown.service
Shell sounds
The rest of the sounds I want to play, will all be played within the shell.
The scrips are all the same and we also call them the same way.
install.sh:
#!/bin/bash
RAND=$[ $RANDOM % 2 ]
mplayer /home/lukas/scripts/install/install_$RAND.wav >/dev/null 2>&1 &
whoami.sh:
#!/bin/bash
RAND=$[ $RANDOM % 3 ]
mplayer /home/lukas/scripts/whoami/whoami_$RAND.wav >/dev/null 2>&1 &
locate.sh:
#!/bin/bash
RAND=$[ $RANDOM % 2 ]
mplayer /home/lukas/scripts/locate/locate_$RAND.wav >/dev/null 2>&1 &
unrar.sh:
#!/bin/bash
RAND=$[ $RANDOM % 1 ]
mplayer /home/lukas/scripts/unrar/unrar_$RAND.wav >/dev/null 2>&1 &
To call the scripts, we write us an alias in our .bashrc or .zshrc which then calls a function:
whoami_sound(){
/home/lukas/scripts/whoami.sh
/usr/bin/whoami
}
alias whoami=whoami_sound
locate_sound(){
/home/lukas/scripts/locate.sh
/usr/bin/locate "$@"
}
alias "locate"=locate_sound
find_sound(){
/home/lukas/scripts/locate.sh
/usr/bin/find "$@"
}
alias "find"=find_sound
install_sound(){
/home/lukas/scripts/install.sh
sudo /usr/bin/pacman "$@"
}
alias pacman=install_sound
unrar_sound(){
/home/lukas/scripts/unrar.sh
sudo /usr/bin/unrar "$@"
}
alias unrar=unrar_sound
unzip_sound(){
/home/lukas/scripts/unrar.sh
sudo /usr/bin/unzip "$@"
}
alias unzip=unzip_sound
Every time we call for example whoami, we instead call our function which calls our script and then the command we were calling in the first place. But pay attention: don't just write the command down, write the full path. Otherwise we create and endless loop.
On commands which take parameters we have to give them to our real call too.
Conclusions
So that's it. You may want to add a flag to regulate the volume or the output device. I was thinking about making it a real program, like Iron Mans J.A.R.V.I.S for example, but I haven't had an idea about how I can get all those different events into a single program. Maybe you got a idea. Just hit me up.
Did I make you interessted in this topic for even more or do you want to talk about it or do you have any suggestions or improvments?
Just tell me, I am always open for critique :)
I love to hear from you 😉