【问题标题】:Delphi and Discrete Signals: Getting a fixed volume signal with fixed silenceDelphi 和离散信号:获得具有固定静音的固定音量信号
【发布时间】:2011-12-13 13:31:50
【问题描述】:

这是对我已经在这里问过的另一个问题的跟进How can I play a single tone or custom wave with Delphi?

长话短说,我使用 MMSystem 的 waveOutWrite() 创建了一个离散信号,但它似乎要么没有按我的预期工作,要么我没有得到它。

我用固定空格将两个离散信号写入缓冲区,例如Samples[i] := round(vol*sin(2*Pi*AFreq*t));

信号的音量为 1000,而“空格”是一些 0 音量的信号。

这就是它的样子,而我期待的东西会像 |...|

是我把 Signal 的东西弄错了,还是我没有正确使用 WinAPI?我希望每个固定信号在图表上看起来都一样

P.S:我使用的是 Windows 的 Stereo Mix,所以没有干扰 P.P.S:这是将二进制转换为信号的代码段

function CreateBinaryTone(BinaryString: String): TWaveformSamples;
var
  I: Integer;
  omega,
  dt: double;
  vol: double;
begin
  omega := 2*Pi*AFreq;
  dt := 1/Format.nSamplesPerSec;

  SetLength(Samples, Length(BinaryString));

  for I := 1 to Length(BinaryString) do
  begin
    { Discrete Time }
    Vol := StrToInt(BinaryString[I]) * 1000;
    Samples[I] := vol * sin(omega * dt * I);
  end;

  Result := Samples;
end;

【问题讨论】:

  • 请出示您的代码,sin() 表达式中的值应与i 不同,以获得具有相同幅度的纯频率。
  • 已将代码添加到问题中,谢谢。
  • 我刚刚测试了您在上一个问题中提到的Andreas sample。效果很好,你也测试过他的代码吗?
  • 不,我没有。但我要的是幅度调制,而不是频率。他也是一个连续时间,我想要离散时间,所以这不是我需要这样做的方式。我的想法可能有点偏离,我可以只有 0 / 任何幅度吗?我猜测通过麦克风的幅度会改变,所以我不想依赖某个阈值并希望它尽可能简单。这甚至是一种选择吗?
  • 我不确定您在寻找什么。您正在添加两个频率并改变幅度二进制(0 或 1000)。您是否检查过 Samples 向量是否在您想要的位置实际包含零?

标签: delphi signal-processing


【解决方案1】:

您的比特时间需要比您的载波(正弦)频率的周期长得多,并且采样率也需要超过载波频率的 2 倍。因此,您的样本生成循环可能需要更长的时间。

【讨论】:

  • 从外观上看,是的,要长得多。
  • 我的采样率是 2x,但你说的比特时间是什么意思?传输1000的时间?
  • 另外,我只使用 0/1000 安培的波,因此,我认为,我没有载波,或者这只是意味着我的“载波”总是在0?
  • 位时间 - 您为位向量中的每个 1 或 0 生成多少个样本?此外,采样率必须大于 AFreq 载波频率的 2 倍,而不仅仅是 2 倍。
  • 1 个样本小于正弦波周期的 1/2。根据您的操作,您可能需要为每个位使用多个周期的正弦波。
【解决方案2】:

我可能在此线程上发帖较晚,但是,如果重点是创建一个函数,该函数可以在不考虑时间的情况下生成离散但连续的正弦/余弦信号(例如,没有可预测时间限制的实时操作),那么很容易,但有效的方法来做到这一点!

首先是数学:

我们的朋友欧拉为我们提供了一种使用复数来描述正弦和余弦的方法。

Euler - Sin/Cos

通过在这个方程中添加一个时间变量,我们可以使它描述一个正弦波和一个余弦波

Euler - Sin/Cos Wave

在这个 ecuatoin 中,我们的余弦波由这个复数的实部表示 以及虚部的余弦波:

Euler - Actual Sin/Cos Presentation

现在,每次我们增加这个时间变量,就好像我们增加了一个小角度的旋转(我们称之为 theta 或 θ)。这意味着我们的下一步,应该有我们上一步的角度加上这个小 θ 角

请记住,我们会将 ωt 作为我们的 最后一步 的角度,将 θ 作为需要添加的角度到我们最后一步的角度,以便生成新的步骤

Euler - Next Step (1/2)

如果我们开发这个等式的左边,我们会看到那里发生了一些有趣的事情:

Euler - Next Step (2/2)

我们知道,如果一个复数z与另一个复数w相等,这意味着它们的实部和虚部也相等。

Complex Numbers Equality

考虑到这一点,我们看到了一个非常有趣的结论: 如果我们已经计算了上一步的 Sine 和 Cosine,我们可以通过添加这个 θ 角轻松计算下一步,相应地使用以下等式计算余弦和正弦:

Euler - Next Step Calculation Equations

最后, 编码部分

现在,为了创建这样的功能,你需要一些东西:

  1. 2个全局,Double变量,呈现上一步的sin和cos
  2. 一个初始化函数,用于设置初始“零步”及其初始阶段
  3. 当被调用时计算并产生下一步的函数。

首先,声明:

private
    [...]
    sine_last_cos: double;
    sine_last_sin: double;
    cosine_last_cos: double;
    cosine_last_sin: double;

    procedure SineInit(starting_phase: double);
    procedure CosineInit(starting_phase: double);
    function SineGen(amplitutde: double; sampling_rate: Integer; Freq: Double): double;
    function CosineGen(amplitutde: double; sampling_rate: Integer; Freq: Double): double;

接下来是实现:

procedure Tmain.SineInit(starting_phase: double);
begin
    sine_last_sin := sin(starting_phase*pi/180);   //given phase is in deg, not rad
    sine_last_cos := cos(starting_phase*pi/180);
end;

procedure Tmain.CosineInit(starting_phase: double);
begin
    cosine_last_sin := sin(starting_phase*pi/180);
    cosine_last_cos := cos(starting_phase*pi/180);
end;

function Tmain.SineGen(amplitutde: double; sampling_rate: Integer; Freq: Double): double;
var sin_theta: double;
    cos_theta: double;
    new_sin_step, new_cos_step: double;
begin
    sin_theta :=  sin(2*pi*Freq/sampling_rate);    //theta depend on sampling freq
    cos_theta :=  cos(2*pi*Freq/sampling_rate);    //as well as desirable freq
    new_cos_step:= sine_last_cos*cos_theta - sine_last_sin*sin_theta;
    new_sin_step:= sine_last_cos*sin_theta + sine_last_sin*cos_theta;
    sine_last_sin:= new_cos_step;
    sine_last_cos:= new_sin_step;
    result := amplitutde *new_sin_step;
end;

function Tmain.CosineGen(amplitutde: double; sampling_rate: Integer; Freq: Double): double;
var sin_theta: double;
    cos_theta: double;
    new_sin_step, new_cos_step: double;
begin
    sin_theta :=  sin(2*pi*Freq/sampling_rate);    //theta depend on sampling freq
    cos_theta :=  cos(2*pi*Freq/sampling_rate);    //as well as desirable freq
    new_cos_step:= cosine_last_cos*cos_theta - cosine_last_sin*sin_theta;
    new_sin_step:= cosine_last_cos*sin_theta + cosine_last_sin*cos_theta;
    cosine_last_sin:= new_cos_step;
    cosine_last_cos:= new_sin_step;
    result := amplitutde *new_sin_step;
end;

在本例中,每次启动程序(也就是在第一次 SineGen/CosineGen 调用之前)以及每次您想要重置波形时都应调用 Init 过程。

编辑:更正图像 + 添加被遗忘的初始化程序

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多