Delphi和离散信号:获得具有固定静音的固定音量信号
这是我在这里已经问过的另一个问题的后续 长话短说,我使用MMSystem的waveOutWrite()创建了一个离散信号,但它要么没有像我预期的那样工作,要么我没有得到它 我用固定的空格向缓冲区写入了两个离散的信号,就像这样Delphi和离散信号:获得具有固定静音的固定音量信号,delphi,signal-processing,Delphi,Signal Processing,这是我在这里已经问过的另一个问题的后续 长话短说,我使用MMSystem的waveOutWrite()创建了一个离散信号,但它要么没有像我预期的那样工作,要么我没有得到它 我用固定的空格向缓冲区写入了两个离散的信号,就像这样Samples[I]:=round(vol*sin(2*Pi*AFreq*t)) 其中,一个信号的音量为1000,而“空格”是一些0音量的信号 这就是它看起来的样子,而我预期的东西会像|| 我是不是把信号都弄错了,还是我没有正确使用WinAPI?我希望每个固定信号在图表上看
Samples[I]:=round(vol*sin(2*Pi*AFreq*t))代码>
其中,一个信号的音量为1000,而“空格”是一些0音量的信号
这就是它看起来的样子,而我预期的东西会像||
我是不是把信号都弄错了,还是我没有正确使用WinAPI?我希望每个固定信号在图表上看起来都一样
我用的是Windows的立体声混音,所以没有干扰
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;
位时间需要比载波(正弦)频率的周期长得多,采样率也需要大于载波频率的2倍。因此,您的示例生成循环可能需要更长的时间。我可能会延迟发布此线程,但是,如果点正在创建一个函数,该函数在不考虑时间的情况下生成离散但连续的正弦/余弦信号(例如,没有可预测时间限制的实时操作),则有一个简单的,但这是一个有效的方法
首先,数学:
我们的朋友Euler为我们提供了一种用复数描述正弦和余弦的方法
通过在这个方程中加入一个时间变量,我们可以使它描述正弦波和余弦波
在这个例子中,我们的余弦波由这个复数的实部表示
以及余弦波的虚部:
现在,每次我们增加这个时间变量,就像我们增加一个小角度的旋转(我们称之为θ或θ)。这意味着我们的下一步,应该有上一步的角度加上这个小θ角
请记住,我们将ωt作为最后一步的角度,θ作为需要添加到最后一步角度的角度,以便生成新的步骤
如果我们发展这个等式的左边,我们会看到有趣的事情:
我们知道,如果一个复数z等于另一个复数w,这意味着它们的实部和虚部也相等
考虑到这一点,我们看到了一个非常有趣的结论:
如果我们已经计算了上一步的正弦和余弦,我们可以通过添加这个θ角,使用下面的余弦和正弦方程,轻松计算下一步:
最后,,
编码部分
现在,为了创建这样的函数,您需要一些东西:
2个全局双变量,表示最后一步的正反方向
一个初始化函数,用于设置初始“零步”及其初始阶段
在调用下一步时计算并得出结果的函数
首先,声明:
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调用之前)和每次您想要重置wave时,都应该调用Init过程
编辑:更正图像+添加遗忘的初始化过程请显示您的代码,sin()
表达式中的值应随i
变化,以获得具有相同振幅的纯频率。将代码添加到问题中,谢谢。我刚刚测试了您在上一个问题中提到的值。工作正常,你也测试过他的代码吗?没有。但我要的是振幅调制,不是频率。而且他的时间是连续的,我想要离散的时间,所以这不是我需要的方式。我的想法可能有点偏离,我能得到0/任何振幅吗?我猜通过麦克风的振幅会发生变化,所以我不想依赖于某个阈值,希望它尽可能简单。这是一个选择吗?我不知道你在找什么。您正在添加两个频率并改变二进制振幅(0或1000)。你有没有检查过样本向量在你想要的地方实际包含零?从外观上看,是的,更长。我的样本率是2x,但你所说的位时间是什么意思?传输时间1000?而且,我只使用了0/1000安培的波,因此,我认为,我没有载波,或者这只是意味着我的“载波”总是在0位时间-你为位向量中的每个1或0生成了多少个样本?此外,采样率必须大于AFreq载波频率的2倍,而不仅仅是2倍。1采样率小于正弦波周期的1/2。根据您所做的工作,每个位可能需要多个正弦波周期。