thanks SenlinOS for the icon above
A key sound program on GNU/Linux
Thanks @MiraculousMoon for this English version.
I find an interesting plugin called skywind3000/vim-keysound when I program in VIM. It can give you immediate feedback using mechanical keyboard sound when you are typing. But it can't work globally and doesn't support mixing.
So I write it to make up its defect and practice cpp.
Although the project is very simple, I have learned a lot from it. For example, in terms of audio, I understood how to parse the wav format, understood the concepts of sampling rate, number of channels, bit rate, mixing, etc., and calculate the playback time of a piece of data; system In terms of programming, I learned about multi-threading, device hot-plug monitoring, command line parameter analysis, signals, etc.; the project even taught me to play the piano, lol.
Here are some video presentations
- Global key sound effects.
- Customization. Users can customize the sound effect for each button.
- Mixing. Support for mixing, press multiple buttons at the same time, the sound effects of multiple buttons will be played at the same time.
- Hot plugging. Supports dynamic monitoring of keyboard insertion and removal.
- Multiple audio playback backends. Audio playback can choose to use alsa (problems), pulse, sdl2 for audio playback.(Pulse is used by default)
Pulse is the default backend so libpulse
is needed.
If you use SDL2 so the sdl2
is needed.
ubuntu:
# Pulse
sudo apt install libpulse-dev
# SDL2
sudo apt install libsdl2-dev
arch:
# Pulse
sudo pacman -S libpulse
# SDL2
sudo pacman -S sdl2
git clone https://github.com/fgheng/keysound
cd keysound
# Just make,default backend is Pulse
make
# Pulse
make CFLAG=pulse
# SDL2
make CFLAG=sdl
# Alsa
make CFLAG=alsa
You will get a executable file called keysound
after make.
Add your username to input
group
sudo usermod -a -G input username
relogin or user this command
newgrp input
-f, --file=WAV_FILE Audio to play
-j, --json=JSON_FILE Json config file
-d, --dir=DIR Directory
-l, --log=LOG_FILE log(undone)
-D, --daemon Run as a daemon
-k, --kill Kill running process
-?, -h, --help Help
Examples:
./keysound -f ./audio/typewriter-key.wav
All key will use typewriter-key.wav
./keysound -d ./audio/dir
Program will search suitable audioes in ./audio/dir
. For example, a.wav
is the sound of KEY-a
. lshift.wav
is the sound of KEY-left_shift
. ;.wav
is the sound of KEY-;
.
./keysound -j ./audio/piano.json
Program will read ./audio/piano.json
to play sound.
Following is an example:
{
"dir": "./audio/piano",
"`": "28-C-小字组.wav",
"1": "30-D-小字组.wav",
"2": "32-E-小字组.wav",
"3": "33-F-小字组.wav",
"4": "35-G-小字组.wav",
"5": "37-A-小字组.wav",
"6": "39-B-小字组.wav",
"7": "40-C-小字1组.wav",
"8": "42-D -小字1组.wav",
"9": "44-E-小字1组.wav",
"0": "45-F-小字1组.wav",
"-": "47-G-小字1组.wav",
"=": "49-A-小字1组.wav",
"backspace": "51-B-小字1组.wav",
"f7": "52-C-小字2组.wav",
"f8": "54-D-小字2组.wav",
"f9": "56-E-小字2组.wav",
"f10": "57-F-小字2组.wav",
"f11": "59-G-小字2组.wav",
"f12": "61-A-小字2组.wav",
"insert": "63-B-小字2组.wav",
"h": "52-C-小字2组.wav",
"n": "40-C-小字1组.wav",
"j": "42-D -小字1组.wav",
"k": "44-E-小字1组.wav",
"l": "45-F-小字1组.wav",
";": "47-G-小字1组.wav",
"'": "49-A-小字1组.wav",
".": "51-B-小字1组.wav",
"slash": "51-B-小字1组.wav",
"a": "28-C-小字组.wav",
"s": "30-D-小字组.wav",
"d": "32-E-小字组.wav",
"f": "33-F-小字组.wav",
"x": "35-G-小字组.wav",
"c": "37-A-小字组.wav",
"v": "39-B-小字组.wav",
"y": "52-C-小字2组.wav",
"u": "54-D-小字2组.wav",
"i": "56-E-小字2组.wav",
"o": "57-F-小字2组.wav",
"p": "59-G-小字2组.wav",
"[": "61-A-小字2组.wav",
"]": "63-B-小字2组.wav"
}
"dir"
is required. It means directory of audio files.
Below is audio file's name corresponding each key.
If you use default
keyword, then all of keys you haven't set will use the default audio.
./keysound -j ./audio/piano.json -D
Make process running as a daemon.
./keysound -k
Kill all process running in the foreground and background.
The principle of this program is very simple. It detects key events by reading the event file of each keyboard. When a key event occurs, the audio is added to the mixer for mixing, and the audio player reads data from the mixer to play.
There are several threads in the program, one of which is to detect keyboard hot-plugging. This thread uses netlink to detect keyboard hot-plugging. When a keyboard is inserted, a new keyboard monitoring thread is automatically started. The keyboard monitoring thread detects whether a key is pressed. If a key is pressed, the audio corresponding to the key is obtained from Audio and added to the mixer ; When the keyboard is unplugged, the keyboard hot-plug detection program will notify the corresponding keyboard monitoring thread to exit safely.
The second is the audio playback thread. This thread can choose to use one of alsa (problem) pulse and sdl2 as the backend for audio playback. When the data in the audio player buffer is played, the audio playback thread will go to mix Acquire new data from the sounder until the user gives a signal to stop playing.
The mixing part is realized through a circular buffer.
The mixer has two main functions, one is to store data, and the other is to fetch data. When a key press event is detected, the key detection thread will store the audio data into the buffer of the mixer and set the starting address pos of the current data. When the player fetches data from the mixer, it will start from pos. Remove the fixed-size data, after removing the data, the mixer should set the removed part of the buffer to 0, and at the same time pos must move to the unread data part. If a button is pressed again at this time, the new audio should be stored from pos and merged with the data already in the buffer that has not been taken away. This is the mixing. The scheme I use is A+BA*B>>XX, use the maximum value after overflow. I use a for loop to achieve it. This method's efficiency maybe relatively low, so optimization can be continued here.
audio
directory has a piano.json
config file binding key to the piano sound effect. This file just uses three octaves. You can set more tone level. You can turn your keyboard to a simple piano using this config.
./keysound -j ./audio/piano.json
Here are some example key sequences.
-
;;kjkxjk;kj ;;kjkcjk;jn njk;';k;kkjj njnjnnk;k ;;kjkcjk;kj ;;kjkxjk;jn njk;';k;kkjj xkjnn
-
kkjkk;kjk nnjk;kjjnj k;k’;’;;k; ;kjk;kjjjnj kkjkk;kjknnjk;kjjnjk;k’;’;;k;kjk;kjjn njkk;’’iuyy’; ‘;kjncnjjnjnjkk;’’iuyyuy ;;k/ynjkjn
-
xlkdjvn n';kjnj clk;nnjn ccvlk;kj clkdjjn c';kjnj clkl;jjn cvnk'jn k;k;kjk;kjn '';k;kj njnjn' ';jk kjjnncj k;k;kjk;kjn '';;knj njnjnc ';jk kjnnc njk;kjn kcjn
-
调1: ''iii/y/' ip[pioui [[[piiuy/; 'iuy/';' 调2: cckkkvnvc k;';kljk ''';kkjnx ckjnvcxc ccckkkvnvc k;';kljk ''';kkjnvc kjnvcxc
-
jk;k;kjk jk;k;kjk xcncncxcn jkjkcnj jk;k;kjk jk;kj;k xcncncxcn jjnnjk kyy//’k; jnnnncnk kyy//’/’k; jnnnncn jk;k;kjk jk;k;jk xcncncxcn jjnnjk ‘/y/y’/i iuiuiiiuy y/y/njk kyy//’/’k; jnnnncn ‘/y/y’/ y’y’yyu/’; ‘;’;njk kyy//’/’k; jnnnncn kyy//’/’k;jnnnnjn
-
k’;lkjk l;lknjkc jjjjnjk’;lkllljklk ‘’’/k/’;kjk’; jjjkckjjjcncv k’;lkjk l;lknjkc jjjjnjk’;lkljknjxc jjjjnjk’;lkljknjxc
-
Dkjkjjkk njkcncncc cnncnjj lllnnjk dkjkjjkk dkjkjncc cnncnjj klllnjkk jnjnjkj nkkjjnncc dnnnnncn vvvvvcv 高潮 k’k;’’’’y’;’;kjk ckjk’’nk’’k’// k’k;’’’ ‘y’;’;kjk ckjkkkccvvvjnvc Ckjkjjkk njkjjnncc cnncnjj vvvjnvc
- Reorganize the logic of the process exiting this part.
- Rewrite Makefile, you can choose to use alsa, pulse or sdl2, and check whether the dependency is missing.
- Support log output.
- Optimize the calculation of the mixing part: 1. Use a better way to implement the mixing algorithm 2. Design a better buffer.
- Float support added in the mixing section.
- The mixing section adds the size end judgment.
- Added option not to use audio mixing.
- Support volume adjustment function.
- Add an interface that can be displayed in the taskbar
- Fix the bug that alsa cannot be played.
- Add to the software warehouse of various distros like AUR.
- Add the customization of sound effects such as button up and repeat.
- Support mouse operation sound effects.
- Reduce Bluetooth headsets' latency.
- support soundfont and midi
Thanks @MiraculousMoon for this English version.
some audio resources come from
timmyreilly/TypewriterNoises-VSCode
MIT