平滑的频率变化
Smooth frequency change
我正在尝试连续播放一个声音,其频率应该平滑地改变。例如,我的 AudioTrack 当前以 100 Hz 的频率播放,新的频率为 1000 Hz。然后我不希望频率跳跃,而是平滑地适应变化。我当前的代码如下所示:
int buffSize = AudioTrack.getMinBufferSize(sampleRate,
AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT);
AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT, buffSize,
AudioTrack.MODE_STREAM);
short samples[] = new short[buffSize];
int amp = 10000;
double twopi = 8. * Math.atan(1.);
double phase = 0.0;
audioTrack.play();
while (playing)
{
double newFrequency = getFrequency();
for (int i = 0; i < buffSize; i++)
{
samples[i] = (short) (amp * Math.sin(phase));
phase += twopi * newFrequency / sampleRate;
}
audioTrack.write(samples, 0, buffSize);
}
audioTrack.stop();
audioTrack.release();
这当然是在后台运行。我的采样率自动设置为可能的最低值:
private int getMinSampleRate()
{
for (int i = 1; i < 100000; i++)
if (AudioTrack.getMinBufferSize(i, AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT) > 0)
return i;
return -1;
}
您可以使用 low-pass filter to smooth the transition. Here's another example.
double currentFrequency = getFrequency();
double smoothing = 10; // calculate based on your sample rate and desired time delta for the change
while (playing)
{
double newFrequency = getFrequency();
for (int i = 0; i < buffSize; i++)
{
// because you are filling the buffer, deltaTime will be a constant: the time between samples
currentFrequency += deltaTime * ( newFrequency - currentFrequency ) / smoothing;
samples[i] = (short) (amp * Math.sin(phase));
phase += twopi * currentFrequency / sampleRate;
}
audioTrack.write(samples, 0, buffSize);
}
保留频率的历史记录,并根据增量使用增量步骤慢慢将其更改为您的新值。对于大增量,该值在开始时会变化得更快,然后在接近结束时变慢。 IIRC,这称为一阶过滤器,因为您无法控制变化的加速。二阶滤波器将使加速度平滑。
有很多不同的技术,请查看 Wikipedia 中的过滤器。
我正在尝试连续播放一个声音,其频率应该平滑地改变。例如,我的 AudioTrack 当前以 100 Hz 的频率播放,新的频率为 1000 Hz。然后我不希望频率跳跃,而是平滑地适应变化。我当前的代码如下所示:
int buffSize = AudioTrack.getMinBufferSize(sampleRate,
AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT);
AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT, buffSize,
AudioTrack.MODE_STREAM);
short samples[] = new short[buffSize];
int amp = 10000;
double twopi = 8. * Math.atan(1.);
double phase = 0.0;
audioTrack.play();
while (playing)
{
double newFrequency = getFrequency();
for (int i = 0; i < buffSize; i++)
{
samples[i] = (short) (amp * Math.sin(phase));
phase += twopi * newFrequency / sampleRate;
}
audioTrack.write(samples, 0, buffSize);
}
audioTrack.stop();
audioTrack.release();
这当然是在后台运行。我的采样率自动设置为可能的最低值:
private int getMinSampleRate()
{
for (int i = 1; i < 100000; i++)
if (AudioTrack.getMinBufferSize(i, AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT) > 0)
return i;
return -1;
}
您可以使用 low-pass filter to smooth the transition. Here's another example.
double currentFrequency = getFrequency();
double smoothing = 10; // calculate based on your sample rate and desired time delta for the change
while (playing)
{
double newFrequency = getFrequency();
for (int i = 0; i < buffSize; i++)
{
// because you are filling the buffer, deltaTime will be a constant: the time between samples
currentFrequency += deltaTime * ( newFrequency - currentFrequency ) / smoothing;
samples[i] = (short) (amp * Math.sin(phase));
phase += twopi * currentFrequency / sampleRate;
}
audioTrack.write(samples, 0, buffSize);
}
保留频率的历史记录,并根据增量使用增量步骤慢慢将其更改为您的新值。对于大增量,该值在开始时会变化得更快,然后在接近结束时变慢。 IIRC,这称为一阶过滤器,因为您无法控制变化的加速。二阶滤波器将使加速度平滑。
有很多不同的技术,请查看 Wikipedia 中的过滤器。