/keysound

keysound is keyboard sound software for Linux

Primary LanguageC++MIT LicenseMIT

Keysound

pic

感谢 SenlinOS 提供的图标

English README 感谢@MiraculousMoon提供英文版

一个linux下的按键音效程序

Motivation

当我使用vim编程的时侯,我找到了一个有趣的插件skywind3000/vim-keysound,这个插件会在你进行输入的时候,发出类似机械键盘敲击的声音,我觉得非常有趣,不过,该插件只能在vim中使用,不能在其他软件中使用,也就是无法全局使用,而且该插件不支持混音,当你连续按下两个按键的时候,第二个按键的声效会终止第一个按键的声效,体验不是太好。

我之前写过一次keysound,当时只写了全局按键音效,依赖SDL2播放音频,存在很多很多的问题,例如cpu占用高,无法检测键盘的热插拔,没有混音等,体验感极差。我一直想完善一下该项目,正好最近学习c++,可以通过完善该项目练习c++。

该项目虽然很简单,但是我从中学到了很多,如音频方面,我了解了wav格式如何解析,理解了采样率,通道数,比特率,混音等概念,会计算一段数据的播放时长;系统编程方面,学习了多线程,设备热插拔的监控,命令行参数的解析,信号等等;该项目甚至让我学会了弹钢琴,哈哈哈。

这里有一些视频演示

Advatage

  1. 全局按键音效,无论用户在哪个程序下敲击键盘,都可以发声。
  2. 自定义,用户可以为每个按键自定义音效,支持json和目录+文件名的方式。
  3. 混音,支持混音,同时按下多个按键,多个按键的音效会同时播放。
  4. 热插拔,支持动态监控键盘的插入与拔出。
  5. 多个音频播放后端,音频播放可以选择使用alsa(存在问题),pulse,sdl2进行音频的播放,默认使用pulse

Build

Makefile文件是找的模板,写的不是太好,后续需要更改一下。

depends

默认使用的是pulse,所以我们需要安装libpulse,如果使用SDL2,那么就需要安装SDL2

ubuntu:

# 如果使用pulse
sudo apt install libpulse-dev
# 如果使用sdl2
sudo apt install libsdl2-dev

fedora:

# 这个我还没有尝试

arch:

# 如果使用pulse
sudo pacman -S libpulse
# 如果使用sdl2
sudo pacman -S sdl2

make

git clone https://github.com/fgheng/keysound

cd keysound

# 默认使用pulse
make
# 指定使用pulse
make CFLAG=pulse
# 指定使用sdl2
make CFLAG=sdl
# 指定使用alsa,存在问题,不要使用这个
make CFLAG=alsa

默认使用的是pulse播放,执行make之后会在Makefile文件所在的目录下生成一个可执行文件keysound。

add to group

编译完成后,我们需要将用户加入到input用户组才可以使用keysound

sudo usermod -a -G input $USER

重新登录以使附加组生效。

或者不想重新登录,直接使用的话,那么可以在终端中执行如下命令:

newgrp input

该命令会新开一个shell,在该shell中用户属于input组。

Basic Usage

本程序支持单独的音频文件,支持json配置文件,支持目录

  -f, --file=WAV_FILE        要播放的音频
  -j, --json=JSON_FILE       json配置文件
  -d, --dir=DIR              目录
  -l, --log=LOG_FILE         日志输出文件(未完成)
  -D, --daemon               后台运行
  -k, --kill                 结束进程
  -?, -h, --help             帮助

例如:

./keysound -f ./audio/typewriter-key.wav

所有的按键都会使用typewriter-key.wav这个音效。

./keysound -d ./audio/dir

程序会去./audio/dir目录下寻找合适的音频,比如a.wav表示按键a的音效,lshift.wav是左shift的音效,;.wav是按键;的音效等等。

./keysound -j ./audio/piano.json

该命令会让程序读取./audio/piano.json配置文件,下面是json文件的一个例子:

{
    "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"必须要有,他表示的是音频文件所在的目录,接下来的是各个按键对应的音频,如果存在default关键字,那么所有没有设置的按键以及音频读取失败的按键都将使用default所配置的音频。

./keysound -j ./audio/piano.json -D

让程序在后台运行,如果已经有程序在运行了,那么新进程会向旧进程发送中断信号结束旧进程。

./keysound -k

结束运行中的进程,包括前台和后台的进程。

Principle

本程序的原理很简单,通过读取每个键盘的event文件来检测按键事件,当按键事件发生的时候将音频添加到混音器中进行混音,音频播放器从混音器中读取数据进行播放。

程序有这么几个线程,其一是检测键盘热插拔的线程,该线程通过netlink实现键盘热插拔的检测。当有键盘插入的时候,自动启动一个新的键盘监控线程,键盘监控线程检测是否有按键按下,如果有按键按下,那么就会从Audio中获取该按键对应的音频加入到混音器中;当有键盘拔出的时候,键盘热插拔检测程序会通知对应的键盘监控线程安全退出。

其二是音频播放线程,该线程可以选择使用alsa(有问题)pulse以及sdl2中的一种作为后端进行音频的播放,当音频播放器缓存中的数据播放完毕后,音频播放线程会去混音器中获取新的数据,直到用户给定停止播放的信号。

混音部分是通过一个循环buffer实现的。

混音器主要有两个功能,一个功能是存数据,一个功能是取数据。当检测到按键事件的时候,按键检测线程会将音频数据存入到混音器的buffer中,并设定当前数据的起始地址pos,当播放器从混音器取数据的时候会从pos处取走固定大小的数据,取走数据后,混音器应该将buffer中取走的部分置为0,同时pos要移动到还未读取的数据部分。如果此时又有按键按下,那么新的音频应该从pos处开始存储,同时要与buffer中已经存在的还未取走的数据进行合并,这就是混音。我使用的方案是A+B-A*B>>XX,溢出之后使用最大值,我使用for循环实现,我认为这样实现效率比较低,这里可以继续进行优化。

Example

audio目录下有一个piano.json配置文件,该配置文件是一个按键与钢琴音效对应的配置文件,该配置只使用了三个八度的音,我们可以根据自己的情况配置更多的音,使用该配置后,我们的键盘就变成了一个简单的钢琴,执行下面的命令:

./keysound -j ./audio/piano.json

此时我们可以尝试自己弹奏钢琴曲了,我不会钢琴,下面的我都是按照简谱敲的,有可能敲错了,后面的是音乐的视频链接,首先要知道怎么唱才能听出弹的是什么歌,哈哈哈:

  1. 青花瓷 youtubebilibili

    ;;kjkxjk;kj  ;;kjkcjk;jn  njk;';k;kkjj  njnjnnk;k   ;;kjkcjk;kj   ;;kjkxjk;jn  njk;';k;kkjj  xkjnn
    
  2. 菊花台 youtubebilibili

    kkjkk;kjk  nnjk;kjjnj  k;k’;’;;k;  ;kjk;kjjjnj  kkjkk;kjknnjk;kjjnjk;k’;’;;k;kjk;kjjn
    njkk;’’iuyy’;   ‘;kjncnjjnjnjkk;’’iuyyuy  ;;k/ynjkjn
    
  3. 好久不见youtubebilibili

    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
    
  4. 斯卡布罗集市youtubebilibili

    调1:
    ''iii/y/'  ip[pioui  [[[piiuy/;   'iuy/';'
    调2:
    cckkkvnvc  k;';kljk   ''';kkjnx   ckjnvcxc    ccckkkvnvc    k;';kljk   ''';kkjnvc   kjnvcxc
    
  5. 像我这样的人youtube bilibili

    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
    
  6. 滚滚红尘youtubebilibili

     k’;lkjk l;lknjkc  jjjjnjk’;lkllljklk   ‘’’/k/’;kjk’; jjjkckjjjcncv  k’;lkjk  l;lknjkc  jjjjnjk’;lkljknjxc  jjjjnjk’;lkljknjxc
    
  7. 丁香花 youtube bilibili

    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
    

TODO

  • 重新整理进程退出这部分的逻辑
  • 重新编写Makefile,可以选择使用alsa,pulse还是sdl2,检测依赖是否缺失
  • 增加日志输出
  • 优化混音部分的计算:1. 使用更好的方式实现混音算法 2. 设计一个更好的buffer
  • 混音部分增加float支持
  • 混音部分增加大小端判断
  • 增加不使用混音的选项
  • 增加音量调节
  • 增加一个界面,可以在任务栏显示
  • 修复alsa无法播放的bug
  • 添加到各种系统的软件仓库中?
  • 增加按键抬起,重复等音效的自定义
  • 增加鼠标操作音效
  • 蓝牙耳机存在较高延迟
  • 支持soundfont midi

Thanks

感谢@MiraculousMoon提供英文版

部分音频资源来自以下项目

skywind3000/vim-keysound

mattogodoy/hacker-sounds

timmyreilly/TypewriterNoises-VSCode

License

MIT