从陀螺仪监听的 AudioTrack 信号发生器
AudioTrack signal generator listening from gyroscope
我正在尝试创建频率由 gyroscope 传感器数据控制的信号发生器(通过移动 phone)。问题是,我在 onSensorChanged
中调用 AudioTrack
并且输出中有 "clicks",因为每次更新 senzor 时,我都会调用
stopSinus();
setSinus(Frequency);
startSinus();
是这样定义的
siAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, SAMPLE_RATE,
AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT,
buffsize, AudioTrack.MODE_STATIC);
public void stopSinus() {
int checkPlay = siAudioTrack.getPlayState();
if (checkPlay == 3) { //
siaudioTrack.stop();
}
}
public void startSinus() {
siAudioTrack.reloadStaticData();
siAudioTrack.setLoopPoints(0, sampleCount, -1);
siAudioTrack.play();
}
public void setSinus(int frequency) {
sampleCount = (int) ((float) SAMPLE_RATE / frequency);
short sample[] = new short[sampleCount];
int amplitude = 32767;
double twoPi = 2 * Math.PI;;
double phase = 0.0;
for (int i = 0; i < sampleCount; i++) {
sample[i] = (short) (amplitude * Math.sin(phase));
phase += twoPi * frequency / SAMPLE_RATE;
}
siAudioTrack.write(sample, 0, sampleCount);
}
onSensorChanged
每秒变化几次,它会在波中间停止发电机 - 那就是 "clicks" 的来源。
谁能告诉我如何在一整波后停止发电机?如何找出 phase
os 何时传递零?或者一些不同的解决方案?
没有实用的方法来确定应用于扬声器的相位何时超过 0。最好放弃。
这是一个 "different" 解决方案,我认为您会发现它的效果更好:
- 切换到
MODE_STREAM
。为获得最低延迟,请使用最小的可用缓冲区大小 (getMinBufferSize()
)。
- 使用专用线程向
AudioTrack
提供波形。该线程有一项工作:根据当前频率将小块波形写入 AudioTrack
。 (你也可以使用主线程,并不断地用非阻塞写入来填充 AudioTrack
,但在我看来使用一个单独的线程更优雅。线程将花费大部分时间卡在阻塞 write()
调用,这意味着缓冲区保持最大填充状态,工作最少。)
- 删除
startSinus()
和 stopSinus()
。从一开始就开始播放音频,直到离开应用程序才停止。
- 更改
setSinus()
以便它仅将当前频率值传递给步骤 2 中描述的线程。使用任何标准的线程间通信机制。
- 当需要离开应用程序时,使用标准的线程间通信机制通知线程它应该终止,然后
join()
它,清理等等
其他详细信息:
- 让你的
write()s
变小。一次可能只有 100 个样本。这有助于最大限度地减少延迟,同时仍保持理想的阻塞行为,并且无需担心 AudioTrack
. 中有多少可用 space
- 在工作线程中,使用
float
来跟踪阶段(就像您现在所做的一样)。根据频率增加相位,但不要让它变得太大!通过让它回绕使其保持在 0 和 2*PI 之间(否则,由于量化,大的相位误差会逐渐出现,并且频率会意外改变)。显然,在填充每个微小的 100 样本缓冲区后,您不会重置 phase
。你只要让它不停地滚来滚去。
我正在尝试创建频率由 gyroscope 传感器数据控制的信号发生器(通过移动 phone)。问题是,我在 onSensorChanged
中调用 AudioTrack
并且输出中有 "clicks",因为每次更新 senzor 时,我都会调用
stopSinus();
setSinus(Frequency);
startSinus();
是这样定义的
siAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, SAMPLE_RATE,
AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT,
buffsize, AudioTrack.MODE_STATIC);
public void stopSinus() {
int checkPlay = siAudioTrack.getPlayState();
if (checkPlay == 3) { //
siaudioTrack.stop();
}
}
public void startSinus() {
siAudioTrack.reloadStaticData();
siAudioTrack.setLoopPoints(0, sampleCount, -1);
siAudioTrack.play();
}
public void setSinus(int frequency) {
sampleCount = (int) ((float) SAMPLE_RATE / frequency);
short sample[] = new short[sampleCount];
int amplitude = 32767;
double twoPi = 2 * Math.PI;;
double phase = 0.0;
for (int i = 0; i < sampleCount; i++) {
sample[i] = (short) (amplitude * Math.sin(phase));
phase += twoPi * frequency / SAMPLE_RATE;
}
siAudioTrack.write(sample, 0, sampleCount);
}
onSensorChanged
每秒变化几次,它会在波中间停止发电机 - 那就是 "clicks" 的来源。
谁能告诉我如何在一整波后停止发电机?如何找出 phase
os 何时传递零?或者一些不同的解决方案?
没有实用的方法来确定应用于扬声器的相位何时超过 0。最好放弃。
这是一个 "different" 解决方案,我认为您会发现它的效果更好:
- 切换到
MODE_STREAM
。为获得最低延迟,请使用最小的可用缓冲区大小 (getMinBufferSize()
)。 - 使用专用线程向
AudioTrack
提供波形。该线程有一项工作:根据当前频率将小块波形写入AudioTrack
。 (你也可以使用主线程,并不断地用非阻塞写入来填充AudioTrack
,但在我看来使用一个单独的线程更优雅。线程将花费大部分时间卡在阻塞write()
调用,这意味着缓冲区保持最大填充状态,工作最少。) - 删除
startSinus()
和stopSinus()
。从一开始就开始播放音频,直到离开应用程序才停止。 - 更改
setSinus()
以便它仅将当前频率值传递给步骤 2 中描述的线程。使用任何标准的线程间通信机制。 - 当需要离开应用程序时,使用标准的线程间通信机制通知线程它应该终止,然后
join()
它,清理等等
其他详细信息:
- 让你的
write()s
变小。一次可能只有 100 个样本。这有助于最大限度地减少延迟,同时仍保持理想的阻塞行为,并且无需担心AudioTrack
. 中有多少可用 space
- 在工作线程中,使用
float
来跟踪阶段(就像您现在所做的一样)。根据频率增加相位,但不要让它变得太大!通过让它回绕使其保持在 0 和 2*PI 之间(否则,由于量化,大的相位误差会逐渐出现,并且频率会意外改变)。显然,在填充每个微小的 100 样本缓冲区后,您不会重置phase
。你只要让它不停地滚来滚去。