Nook in the Lunar Mare

Netwhale开发日志

Jan 1, 2019

原理

pcap

用来开发tcpdump的库,支持bpf语法,调用简单,其原理是从网卡那边复制了一份包的数据,而非截获,因此不能用libpcap实现注入,如果要这么做的话可以使用libnet(不过我记得api和文档不是很友好。)不过,这次的软件并不需要注入,只需要完成抓包的任务。

音高

音高(英语:pitch)在音乐领域里指的是人类心理对音符基频之感受。 –Wiki

参考的是以下2个说法

  1. 人类能分辨的两次声音之间需间隔0.1s以上
  2. 人耳的音高差别阈限(频率辨别阈或音调辨别阈),即辨别两个音的最小频率差需满足一定条件,1000Hz以下时为1~2Hz,在1000Hz以上时约为频率的0.1~0.2%。

据此,设计时需考虑音响设备支持的最高最低频率,以及人耳能够辨别的极限。以下为开发时参考的音高表。()

音名↓八度->0123456789
C16.352(−48)32.703(−36)65.406(−24)130.81(−12)261.63(0)523.25(+12)1046.5(+24)2093.0(+36)4186.0(+48)8372.0(+60)
C♯/D♭17.324(−47)34.648(−35)69.296(−23)138.59(−11)277.18(+1)554.37(+13)1108.7(+25)2217.5(+37)4434.9(+49)8869.8(+61)
D18.354(−46)36.708(−34)73.416(−22)146.83(−10)293.66(+2)587.33(+14)1174.7(+26)2349.3(+38)4698.6(+50)9397.3(+62)
D♯/E♭19.445(−45)38.891(−33)77.782(−21)155.56(−9)311.13(+3)622.25(+15)1244.5(+27)2489.0(+39)4978.0(+51)9956.1(+63)
E20.602(−44)41.203(−32)82.407(−20)164.81(−8)329.63(+4)659.26(+16)1318.5(+28)2637.0(+40)5274.0(+52)10548(+64)
F21.827(−43)43.654(−31)87.307(−19)174.61(−7)349.23(+5)698.46(+17)1396.9(+29)2793.8(+41)5587.7(+53)11175(+65)
F♯/G♭23.125(−42)46.249(−30)92.499(−18)185.00(−6)369.99(+6)739.99(+18)1480.0(+30)2960.0(+42)5919.9(+54)11840(+66)
G24.500(−41)48.999(−29)97.999(−17)196.00(−5)392.00(+7)783.99(+19)1568.0(+31)3136.0(+43)6271.9(+55)12544(+67)
G♯/A♭25.957(−40)51.913(−28)103.83(−16)207.65(−4)415.30(+8)830.61(+20)1661.2(+32)3322.4(+44)6644.9(+56)13290(+68)
A27.500(−39)55.000(−27)110.00(−15)220.00(−3)440.00(+9)880.00(+21)1760.0(+33)3520.0(+45)7040.0(+57)14080(+69)
A♯/B♭29.135(−38)58.270(−26)116.54(−14)233.08(−2)466.16(+10)932.33(+22)1864.7(+34)3729.3(+46)7458.6(+58)14917(+70)
B30.868(−37)61.735(−25)123.47(−13)246.94(−1)493.88(+11)987.77(+23)1975.5(+35)3951.1(+47)7902.1(+59)15804(+71)

结构

一图胜千言 arch.jpg

代码和示例

代码放在github(https://github.com/Heersin/netwhale)上了,这边是一个小栗子,ping主机后获得的结果如下。

![[ping_AAC.mp4]] ## 开发遇到的问题 最主要的一个问题是开发过程中曾经出现播放5个单音后,产生segment fault,用gdb调试后发现问题出现在播放单音内的一个数组操作上,当时思考了如下原因:

  1. 数组读写时产生读写互斥的情况。之所以会考虑这个是由于前期调研中推荐libao的网友提到过libao在多线程程序中会有些奇奇怪怪的问题。由于libpcap中对包的处理采用异步操作,而通过查看libao的playaudio代码,其中device这个结构中存在一个swap空间,全局只有一个device,因此可以认为是一个全局静态变量,这个函数是不可重入的,推测是因为中断然后重入,造成数组的读写问题。
  • 这边我在代码中加入了一个时延,留足libao调用设备播放的时间,看segment fault何时出现。结果依旧是5个单音后出现,gdb调试显示同样的结果。
  1. 数组用尽,堆增长过大?因为播放单音时,我在函数中使用的时calloc分配堆空间,首先检查自己是不是内存泄露了,发现在播放后并未进行free,尝试后爆出double free的错误,看来libao在播放中就释放了内存。接着用gdb查看map,在开辟buff的地方下断点,一步步跟踪,发现在第6个音时,需求的数组超过了堆标识。修改播放时间,能播的音变多了。
  • 比对正常运行的程序,即使播放几十个音也不会产生该错误,百思不得其解。
  1. 数组越界?在进行第二步检查的时候发现,程序以16bit位的标准进行,比特位却设置为8,二者处理方式不一致导致数组发生越界。

后记

想着想着,最需要的功能应该是过滤一些层的声音,比如说网络层的东西就可以不要听,单听传输层,不然网络层声音过长了。此外声音有点尖锐,我想的一个解决方案是把数据映射到CC#DD#EFF#GG#AA#BB#这样一个范围内,或者其附件,具体的操作是每4位作为一个单音播放。每4位的好处在于可以以更细的粒度去用声音展现数据,以及数据范围在0-15,可以维持在正常的音高范围内。伪代码如下

// Define in header
#define A 440
#define A# 466
...(etc)

void playHalfByteA(u_char byte)
{
    u_char half_byte = (byte>>4) & 0x0F;
    playByte(half_byte);
}

void playHalfByteB(u_char byte)
{
    u_char half_byte = byte & 0x0F;
    playByte(half_byte);
}

这样分别播放前半字节和后半字节 ## 参考

  1. https://www.xiph.org/ao/
  2. https://www.tcpdump.org/
  3. https://www.tcpdump.org/pcap.html