<p>有两个问题。在</p>
<h3>混叠</h3>
<p>以<em>f</em><sub>S</sub>速率采样的信号,只有在不包含频率高于<em>f</em><sub>S</sub>/2的分量时,才能正确重建。当从样本重构信号时(例如,通过声卡),任何频率在区间[0,<em>f</em><sub>S</sub>/2]的信号分量都会被折叠到该区间。在</p>
<p>这被称为<a href="https://en.wikipedia.org/wiki/Aliasing#Sampling_sinusoidal_functions" rel="nofollow noreferrer"><em>aliasing</em></a>,可以通过在采样前对信号进行低通滤波或使采样率足够高来避免。在</p>
<p>在您的例子中,如果您想要采样频率为28160赫兹的正弦波,采样率必须至少为56320赫兹。在</p>
<h3>相位计算错误</h3>
<blockquote>
<pre><code>def get_sample(time):
frequency = a10 / 2.0 ** time
return sin(pi2 * frequency * time)
</code></pre>
</blockquote>
<p>phase</em>是<code>sin</code>函数的参数。它对时间的导数是<a href="https://en.wikipedia.org/wiki/Instantaneous_phase#Instantaneous_frequency" rel="nofollow noreferrer"><em>instantaneous frequency</em></a>,这是我们听到的音调的音调。在</p>
<p>在本例中,如果我们将<code>frequency = a10 / 2.0 ** time</code>插入<code>pi2 * frequency * time</code>,则阶段是</p>
<pre><code>pi2 * (a10 / 2.0 ** time) * time
</code></pre>
<p>或者用符号表示法:</p>
<p>φ=2π·A10·2<sup>—<em>t</em></sup>·<em>t</em></p>
<p>那么<a href="https://www.wolframalpha.com/input/?i=derivative+of+2+pi+*+A+*+2%5E(-t)+*+t+by+t" rel="nofollow noreferrer">derivative of this</a>是</p>
<p><em>f</em>=2π·A10·2<sup>-<em>t</em></sup>·(1−ln 2·<em>t</em>)</p>
<p>而不是2π·A10·2<sup>—<em>t</em></sup>。在</p>
<p>这是使用您的方法得到的实际频率扫描图(考虑到混叠,请注意曲线在0 Hz和24000 Hz线路上是如何反射的),与您的预期相比:</p>
<p><img src="https://i.stack.imgur.com/XAwxB.png" alt="frequency sweep (linear scale)"/></p>
<p>这是同样的对数频率标度图,这就是我们如何将频率视为音调:</p>
<p><img src="https://i.stack.imgur.com/kfaii.png" alt="frequency sweep (logarithmic scale)"/></p>
<h3>解决方案</h3>
<p>通过进行以下更改,可以获得正确的结果:</p>
<ol>
<li><p>为<code>SAMPLE_RATE</code>使用足够高的值。</p></li>
<li><p>不要从给定的时间直接计算样本,而是通过替换保持以与预期频率成比例的速率递增的相位值(将其包装为2π,这样它就不会超出范围)</p>
<blockquote>
<pre><code>def get_sample(time):
frequency = a10 / 2.0 ** time
return sin(pi2 * frequency * time)
[…]
for i in range(SAMPLE_RATE * DURATION):
time = i / SAMPLE_RATE
sample = round(get_sample(time) * MAX_SAMPLE_VALUE)
</code></pre>
</blockquote>
<p>通过</p>
<pre><code>def get_frequency(time):
frequency = a10 / 2.0 ** time
return frequency
[…]
phase = 0
for i in range(SAMPLE_RATE * DURATION):
time = i / SAMPLE_RATE
f = get_frequency(time)
phase = (phase + pi2 * f / SAMPLE_RATE) % pi2
sample = round(sin(phase) * MAX_SAMPLE_VALUE)
</code></pre></li>
</ol>