标签:
Skype是免费的语音通话软件,不但可以点对点用电脑进行免费的语音通话,而且只需花费低廉的费用就可以直接呼叫固定电话或手机,Skype以优秀的通话质量而赢得了全世界不少用户的亲睐,我就是Skype的忠实用户,下图就是我的Skype截图:
我常常使用Skype和台湾同胞还有国外的朋友进行联系,有时因为业务需要需要将语音通话录音并保留下来,在我有这个想法的那个时候(2006年)Skype官方并没有提供录音功能,咱们是做程序的嘛,没有的功能可以自己来添加啊,这也是为什么我酷爱编程的原因。
应广大网友的要求,现将该程序的编程思路和源代码贡献出来与大家共勉,希望能给对音频编程有兴趣的朋友提供一点点帮助,那我就心满意足了。
刚开始编写这个程序的时候,我试着用常规的录音方式对声卡进行录音,既然是通话录音,我们希望能将自己的声音和对方的声音同时纪录下来。首先,我们要将对方的声音录下来,那就只能选取“立体声混音”通道进行录音,但此时“麦克风”通道的声音将被丢弃,也就是说在Skype里对方将听不到我说话了;其次,如果我们还要将我自己的声音录下来,就得开启“麦克风”通道录音,但是在Skype通话过程中,“麦克风”通道已经被Skype占用了,我们的程序无法再次进行录音,看来常规的录音方式行不通。
于是,我想到了Windows音频的底层处理机制,任何语音软件的音频数据处理到最后都离不开 Windows 的底层音频 Win32 API 函数,查一下MSDN 库就能得知,这些函数都在 MultiMed.chm 帮助文件中:
Waveform Functions The following functions are used with waveform audio. auxGetDevCaps auxGetNumDevs auxGetVolume auxOutMessage auxSetVolume PlaySound sndPlaySound waveInAddBuffer waveInClose waveInGetDevCaps waveInGetErrorText waveInGetID waveInGetNumDevs waveInGetPosition waveInMessage waveInOpen waveInPrepareHeader waveInProc waveInReset waveInStart waveInStop waveInUnprepareHeader waveOutBreakLoop waveOutClose waveOutGetDevCaps waveOutGetErrorText waveOutGetID waveOutGetNumDevs waveOutGetPitch waveOutGetPlaybackRate waveOutGetPosition waveOutGetVolume waveOutMessage waveOutOpen waveOutPause waveOutPrepareHeader waveOutProc waveOutReset waveOutRestart waveOutSetPitch waveOutSetPlaybackRate waveOutSetVolume waveOutUnprepareHeader waveOutWrite
有了这些函数,我就想到了一个办法,那就是用系统钩子改变这些函数的原地址,在Skype调用这些Win32 API函数之前先进入我的程序,我将Skype的音频数据“偷偷地”拷贝一份传递给我自己的应用程序,再还给Skype,这样就可以神不知鬼不觉地将通话中的语音数据取出来,再加上自己的mp3压缩保存到硬盘文件即可。
以上便是整个Skype录音的全部思路,现在开始介绍代码。
由于我们的程序需要嵌入到Skype程序中,所以我们只能使用dll的形式来编写这个程序,我现在需要写一个修改Win32 API函数地址的类,在这里我直接引用了《Windows 核心编程》随书代码中的 CAPIHook 类,我提供的源代码里就有这个类,这个类可以修改Win32 API函数的地址,当我们修改好API函数地址以后,Skype调用前面所说的6个函数时系统会自动调用我们的函数,请看代码:
// // 定义函数变量 // typedef MMRESULT (WINAPI *PFN_waveInOpen) ( LPHWAVEIN phwi, UINT uDeviceID, LPWAVEFORMATEX pwfx, DWORD dwCallback, DWORD dwCallbackInstance, DWORD fdwOpen ); typedef MMRESULT (WINAPI *PFN_waveInClose) ( HWAVEIN hwi ); typedef MMRESULT (WINAPI *PFN_waveOutOpen) ( LPHWAVEOUT phwo, UINT uDeviceID, LPWAVEFORMATEX pwfx, DWORD dwCallback, DWORD dwCallbackInstance, DWORD fdwOpen ); typedef MMRESULT (WINAPI *PFN_waveOutClose) ( HWAVEOUT hwo ); typedef MMRESULT (WINAPI *PFN_waveInPrepareHeader) ( HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh ); typedef MMRESULT (WINAPI *PFN_waveOutWrite) ( HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh ); // // 修改Win32 API函数地址 // CAPIHook g_waveInOpen("winmm.dll", "waveInOpen", (PROC) Hook_waveInOpen, TRUE); CAPIHook g_waveInClose("winmm.dll", "waveInClose", (PROC) Hook_waveInClose, TRUE); CAPIHook g_waveOutOpen("winmm.dll", "waveOutOpen", (PROC) Hook_waveOutOpen, TRUE); CAPIHook g_waveOutClose("winmm.dll", "waveOutClose", (PROC) Hook_waveOutClose, TRUE); CAPIHook g_waveInPrepareHeader("winmm.dll", "waveInPrepareHeader", (PROC) Hook_waveInPrepareHeader, TRUE); CAPIHook g_waveOutWrite("winmm.dll", "waveOutWrite", (PROC) Hook_waveOutWrite, TRUE);
至此,Skype通话过程中音频输入和输出的数据(即对方讲话和我自己讲话的声音)已经全部“偷取”到了,接下来只要压缩成mp3格式即可,mp3压缩代码网上很多,随便下载一个来用就可以了,我用的是“hw_mp3_enc.dll library”,效果一般,但用做电话录音足亦。
一个有趣的功能:我们录音后的mp3文件播放时,我可以让对方的声音在左声道,我自己的声音在右声道,好像两个人面对面在对话一样。其实做起来并不难,从上面的代码我们知道,其实输入和输出的音频数据是独立获取的,我们在合并到mp3文件时,将输入的数据存为左声道,输出的数据存为右声道即可。
既然叫“Skype答录机”,除了有录音功能外,还应该有自动应答功能,要实现这个功能有两个办法:
a) 当来电震铃超过规定的次数时自动提机,将录音通道切换到“立体声混音”,然后播放之前准备好的一个语音文件(如:您好,我现在不在电脑旁,有事请留言),本软件使用的就是这种方式;
b) 当来电震铃超过规定的次数时自动提机,然后播放之前准备好的一个语音文件(如:您好,我现在不在电脑旁,有事请留言)数据直接传递至上面的.dll文件相关函数中,然后 waveInPrepareHeader 函数中将系统从麦克风中录制的声音替换掉,这种方式比较难控制,但可以发现很多奇怪的效果,比如通话变声等。
需要注意的地方:该程序是通过钩子方式截取Skype的音频数据,所以程序的执行效率要求很高,对于慢速处理的操作(如:压缩mp3数据、数据存盘等)最好是放到其他线程中处理,否则会影响Skype通话质量,造成通话断断续续的感觉,录音数据也可能会丢失。
标签:
原文地址:http://www.cnblogs.com/XnialBlog/p/4350055.html