我想播放短于1秒的生成声音。但是,AudioTrack
的minBufferSize似乎总是1秒或更长。在某些设备上,我可以将bufferSize设置为小于使用AudioTrack.getMinBufferSize
评估的值,但这在所有设备上都不可能。我想知道是否可以为AudioTrack生成更短的声音。我目前正在使用此代码(它包含一些平滑功能,因为我不断获得新的频率):
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();
double currentFrequency = getFrequency();
double smoothing = 300;
double deltaTime = buffSize / 500;
while (playing && PreferenceManager.getDefaultSharedPreferences(
MainActivity.this).getBoolean("effect_switch", true))
{
double newFrequency = getFrequency();
for (int i = 0; i < buffSize; i++)
{
currentFrequency += deltaTime * (newFrequency - currentFrequency) / smoothing;
samples[i] = (short) (amp * Math.sin(phase));
phase += twopi * currentFrequency / sampleRate;
}
audioTrack.write(samples, 0, buffSize);
}
audioTrack.stop();
audioTrack.release();
事实上,我希望更频繁地更新声音,这就是我需要更短样本的原因。
答案 0 :(得分:1)
我想我有一个解决方案。由于我的最小缓冲区似乎远小于1秒,我通过加载一个5秒数据的缓冲区模拟你的问题,但只播放0.5秒,然后是另一个频率。这个音调我也创造了5秒的数据,但只播放了0.5秒&amp;重复这几个音调。这一切都适合我。
另外,由于我把它塞进了我正在处理的当前项目中,因此我很难剪切和粘贴代码。虽然我已经测试了我的解决方案,但我在此处发布的内容并未完全按照书面形式进行测试。其中一些是切割&amp;粘贴,一些伪代码。
关键功能是使用OnPlaybackPositionUpdateListener。
private AudioTrack.OnPlaybackPositionUpdateListener audioTrackListener = new AudioTrack.OnPlaybackPositionUpdateListener() {
@Override
public void onMarkerReached(AudioTrack audioTrack) {
int marker = audioTrack.getNotificationMarkerPosition();
// I just used 8 tones of 0.5 sec each to determine when to stop but you could make
// the condition based on a button click or whatever is best for you
if(marker < MAX_FRAME_POSITION) {
audioTrack.pause();
newSamples();
audioTrack.play();
} else {
audioTrack.stop();
}
audioTrack.setNotificationMarkerPosition(marker + FRAME_MARKER);
Log.d(TAG, "MarkerReached");
}
@Override
public void onPeriodicNotification(AudioTrack audioTrack) {
int position = audioTrack.getPlaybackHeadPosition();
if(position < MAX_FRAME_POSITION) {
audioTrack.pause();
newSamples();
audioTrack.play();
} else {
audioTrack.stop();
}
Log.d(TAG, "PeriodNotification");
}
};
然后
audioTrack.setPlaybackPositionUpdateListener(AudioTrackListener);
我使用了标记(必须重复重新初始化)才能进行测试......
audioTrack.setNotificationMarkerPosition(MARKER_FRAMES);
但您也应该能够使用定期通知。
audioTrack.setPositionNotificationPeriod(PERIODIC_FRAMES);
从侦听器调用newSamples()
方法
public void newSamples() {
/*
* generate buffer, I'm doing similar to you, without the smoothing
*/
// AudioTrack write is a blocking operation so I've moved it off to it's own Thread.
// Could also be done with an AsyncTask.
Thread thread = new Thread(writeSamples);
thread.start();
}
private Runnable writeSamples = new Runnable() {
@Override
public void run() {
audioTrack.write(samples, 0, buffSize);
}
};