数据到音频和返回。使用源代码进行调制/解调

时间:2011-02-12 03:54:05

标签: algorithm encoding audio signal-processing

我有一个二进制数据流,想要将其转换为原始波形声音数据,我可以将其发送给扬声器。

这就是老式调制解调器为了通过电话线传输二进制数据所做的事情(产生典型的现代声音)。它被称为调制。

然后我需要一个反向过程 - 从原始波形样本中,我想获得精确的二进制数据。这称为解调。

  • 任何比特率都可以起作用。
  • 使用计算机扬声器播放声音并使用麦克风采样。
  • 带宽非常低(低质量麦克风)。
  • 有一些背景噪音但不多。

我找到了一种特殊方法 - Frequency shift keying。问题是我找不到任何源代码。

您能否指点我用任何语言实施FSK? 或者提供具有可用源代码的任何替代编码二进制< - >声音?

2 个答案:

答案 0 :(得分:20)

最简单的调制方案是amplitude modulation(技术上对于数字领域,这将被称为幅移键控)。采用固定频率(比如说10Khz),你的“载波”,并使用二进制数据中的位来打开和关闭它。如果您的数据速率是每秒10位,您将以该速率打开和关闭10KHz信号。解调将是(可选的)10KHz滤波器,然后与阈值进行比较。这是一个相当简单的实现方案。通常,信号频率和可用带宽越高,开关信号的速度就越快。

这里非常酷/有趣的应用程序将编码/解码为莫尔斯代码,看看你能走多快。

FSK,在两个频率之间切换,带宽效率更高,对噪声的抵抗力更强,但由于需要区分这两个频率,因此会使解调器更加复杂。

Phase Shift Keying等高级调制方案可以很好地获得给定带宽和信噪比的最高比特率,但实现起来更复杂。模拟电话调制解调器需要处理某些带宽(例如低至3Khz)和噪声限制。如果您需要在给定带宽和噪声限制的情况下获得尽可能高的比特率,那么就可以了。

对于高级调制方案的实际代码示例,我将调查DSP供应商的应用笔记(例如TIAnalog Devices),因为这些是DSP的常见应用。

Implementing a PI/4 Shift D-QPSK Baseband Modem Using the TMS320C50

QPSK modulation demystified

V.34 Transmitter and Receiver Implementation on the TMS320C50 DSP

另一种非常简单且不那么有效的方法是使用DTMF。这些是由电话键盘产生的音调,其中每个符号是两个频率的组合。如果你谷歌,你会发现很多源代码。根据您的应用/要求,这可能是一个简单的解决方案。

让我们深入研究一些简单的方案实现细节,比如我之前提到的莫尔斯代码。我们可以使用“dot”代表0和“破折号”代表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
    } 
  }
}

解码器有点复杂,但这是一个大纲:

  1. 可选择对11Khz附近的采样信号进行带通滤波。这将改善嘈杂环境中的性能。 FIR过滤器非常简单,有一些在线设计小程序可以为您生成过滤器。
  2. 阈值信号。高于1/2最大振幅的每个值均为1,低于每个值为0.这假设您已对整个信号进行采样。如果这是实时的,您可以选择固定阈值或进行某种自动增益控制,在一段时间内跟踪最大信号电平。
  3. 扫描点或短划线的开始。您可能希望在点周期中至少看到一定数量的1来将样本视为点。然后继续扫描,看看这是否是破折号。不要指望一个完美的信号 - 你会在1的中间看到几个0,在0的中间看到几个1。如果噪音很小,那么将“开启”时段与“关闭”时段区分开来应该相当容易。
  4. 然后反转上述过程。如果你看到破折号向缓冲区推送1位,如果一个点推零。

答案 1 :(得分:2)

调制/解调的一个目的是适应信道特性。例如,频道可能无法通过DC。另一个目的是克服信道中给定数量和类型的噪声,同时仍然将数据传输到某个给定的错误率之上。

对于FSK,您只需要例程,而不是在发送端以两个不同的频率生成正弦波,并在接收端过滤和检测两个不同的频率。每段正弦波的长度,频率的分离和幅度将取决于您需要克服的数据速率和噪声量。

在最简单的情况下,零噪声,在连续的固定时间帧内简单地产生N或2N个正弦波。类似的东西:

x[i] = amplitude * sin( i * 2 * pi * (data[j] ? 1.0 : 2.0) * freq) / sampleRate )

在接收端,您可以在远高于最大频率两倍的位置对信号进行采样,并测量过零点之间的距离,并查看是否发现了一串短周期或长周期波形。在存在非零噪声的情况下,可以使用许多使用数字信号处理滤波器(IIR,FIR等)和各种统计检测器的更方便的方法。