当前位置: 首页 > 面试题库 >

数据转为音频并返回。带有源代码的调制/解调

金嘉言
2023-03-14
问题内容

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

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

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

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

我找到了一种执行此操作的特殊方法- 频移键控。问题是我找不到任何源代码。

您能指出我用任何一种语言实现的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
    } 
  }
}

解码器稍微复杂一点,但这是一个概述:

  1. 可选地,对11Khz附近的采样信号进行带通滤波。这将在嘈杂的环境中提高性能。FIR过滤器非常简单,并且有一些在线设计小程序可以为您生成过滤器。
  2. 信号阈值。大于1/2最大幅度的每个值都是1,小于1/2最大幅度的每个值都是0。这假设您已经采样了整个信号。如果这是实时的,则可以选择固定的阈值或执行某种自动增益控制,以在一段时间内跟踪最大信号电平。
  3. 扫描点或破折号的开始。您可能希望在点周期内看到至少一定数量的1,以将样本视为点。然后继续扫描以查看是否是破折号。不要指望一个完美的信号-您会在1的中间看到几个0,在0的中间看到几个1。如果噪声很小,则将“接通”周期与“断开”周期区分开应该很容易。
  4. 然后逆转以上过程。如果看到破折号,则将1推至缓冲区,如果破折号,则将其推至零。


 类似资料:
  • 当我使用MVC控制器时,我使用“返回OK(对象)”或“返回BadRequest(ErrorMessage)”等。 我怎样才能实现这是剃刀页? 我尝试返回新的JSON result(object);这在状态代码为200时有效。但是如果我想返回带有JSON错误消息的状态代码400呢?

  • 我正在尝试播放从MPEG2传输(. ts)文件中获取的一些音频。我通过MediaExtractor获取音频流,使用readSampleData将样本复制到MediaCodec输入缓冲区,然后将MediaCodec输出缓冲区写入AudioTrack。MediaExtractor将音频MIME类型指示为“音频/mp4a-latm”。 所有这些都可以在Nexus 7 2013平板电脑上完美运行。 但它在

  • 我有一个YouTube用户,其中不包含YouTube频道。但是,如果我使用“mine=true”属性查询youtube.channels.list API方法,YouTube数据API确实会返回一个频道,如下面的示例(1)所示。 例: (1) 返回一个通道(mine=true;通过使用OAuth): https://developers.google.com/apis-explorer/#p/yo

  • 问题内容: 我正在从PHP调用python脚本。 python程序必须根据传递给它的参数返回一些值。 这是一个示例python程序,它将为您提供我目前正在做什么的基本概念: 从上面的代码中可以看到,我的基本目标是 以便python程序根据参数返回一些值(0、1、4、8等)。 然后,调用PHP的程序访问这些返回的值并执行适当的操作。 目前,我已经为此目的使用了“ sys.exit(n)”。 我使用s

  • 这是我的问题,有人给了我一个函数,如果我理解得很好,它会把一些声音样本放入数组列表中。 我想创建一个。wav文件的音频曲目,我真的不知道如何做到这一点。 这是代码,因为也许我根本不懂。。。 下面是示例,在上面的代码中导入。

  • 我想发送这个示例数组 作为二进制数据发送到我的websocket服务器。在服务器端,我希望将二进制数据解码回数组,进行更改并将二进制数据发送回客户端。最后在客户端,如何将二进制数据解码回数组? 示例截图我的意思是: 这是我的实际代码: 服务器端代码: 我现在可以将消息作为二进制帧发送到websocket服务器。我找到了将字符串转换为二进制类型并将其发送到ws-server的函数。 现在我有问题了。

  • 问题内容: Scipy最小化功能(仅作为示例使用)可以选择在每个步骤中添加一个回调函数。所以我可以做类似的事情, 有没有办法使用回调函数来创建fmin的生成器版本,以便我可以做, 似乎可以将收益和收益进行某种组合,但我可以想到任何事情。 问题答案: 如注释中所指出的,您可以使用在新线程中进行操作。缺点是您仍然需要某种方法来访问最终结果(最后返回什么)。下面的示例使用一个可选的回调函数来做一些事情(

  • 我正在使用spark-sql-2.4。1v和Java 8。我有一个用例,如下所示, 我需要根据另一个数据集的条目进行一些操作。 我需要并行处理所有代码。做同样的事情,我正在尝试如下: 如何在集群上并行工作?