我有一个二进制数据流,想要将其转换为原始波形声音数据,然后可以将其发送给扬声器。
这就是老式调制解调器为了通过电话线传输二进制数据(产生典型的现代声音)所做的事情。这称为调制。
然后,我需要一个逆过程-从原始波形样本中,我想获得确切的二进制数据。这称为解调。
我找到了一种执行此操作的特殊方法- 频移键控。问题是我找不到任何源代码。
您能指出我用任何一种语言实现的FSK吗? 还是提供带有可用源代码的任何其他编码二进制<->声音?
最简单的调制方案是振幅调制(在数字领域中,技术上称为振幅移位键控)。采取固定的频率(假设为10Khz),即“载波”,并使用二进制数据中的位将其打开和关闭。如果您的数据速率为每秒10位,则将以该速率打开和关闭10KHz信号。解调将是一个(可选)10KHz滤波器,然后与阈值进行比较。这是一个相当简单的实现方案。通常,信号频率和可用带宽越高,打开和关闭信号的速度就越快。
这里一个很酷/很有趣的应用程序是将代码编码/解码为莫尔斯电码,并查看您可以走多快。
FSK,在两个频率之间切换在带宽上更有效,并且更不受噪声影响,但是由于需要区分两个频率,解调器将变得更加复杂。
诸如相移键控之类的高级调制方案擅长在给定的带宽和信噪比下获得最高的比特率,但实现起来较为复杂。模拟电话调制解调器需要处理某些带宽(例如,低至3Khz)和噪声限制。如果您需要在给定的带宽和噪声限制下获得尽可能高的比特率,那么这就是要走的路。
对于高级调制方案的实际代码示例,我将研究DSP厂商(例如TI和Analog Devices)的应用笔记,因为它们是DSP的常见应用。
使用TMS320C50实现PI / 4移位D- QPSK基带调制解调器
QPSK调制解开神秘面纱
V.34发送器和接收器在TMS320C50 DSP上的实现
另一种非常简单但效率不高的方法是使用DTMF。这些是电话键盘产生的音调,其中每个符号是两个频率的组合。如果您使用Google,则会发现很多源代码。根据您的应用程序/要求,这可能是一个简单的解决方案。
让我们深入了解一些简单的方案实现细节,例如我之前提到的莫尔斯电码。我们可以将“ dot”用于0,将“ dash”用于1。摩尔斯式方案的一个优点是它还解决了成帧问题,因为您可以在每个空间之后重新同步采样。为简单起见,我们选择“载波”频率以11KHz并假设您的波输出为44Khz,16位单声道。我们还将使用方波来产生谐波,但我们并不在意。如果11KHz超出了麦克风的频率响应,则将所有频率除以2例如,我们将选择任意级别10000,因此我们的“打开”波形如下所示:
{10000, 10000, 0, 0, 10000, 10000, 0, 0, 10000, 0, 0, ...} // 4 samples = 11Khz period
我们的“关闭”波形全为零。我把这部分的编码留给读者看。
所以我们有这样的东西:
const int dot_samples = 400; // ~10ms - speed up later const int space_samples = 400; // ~10ms const int dash_samples = 800; // ~20ms void encode( uint8_t* source, int length, int16_t* target ) // assumes enough room in target { for(int i=0; i<length; i++) { for(int j=0; j<8; j++) { if((source[i]>>j) & 1) // If data bit is 1 we'll encode a dot { generate_on(&target, dash_samples); // Generate ON wave for n samples and update target ptr } else // otherwise a dash { generate_on(&target, dot_samples); // Generate ON wave for n samples and update target ptr } generate_off(&target, space_samples); // Generate zeros } } }
解码器稍微复杂一点,但这是一个概述: