第一次海报在这里。 我平时喜欢找自己的答案(无论是通过研究或试验和错误),但我在这里难住了。
我想要做的事:我建立一个简单的Android音频合成。 现在,我只是打在实时正弦基调,在为用户调整它改变音调的频率UI的滑块。
我是如何构建它:基本上,我有两个线程-一个工作线程和输出线程。 工作线程简单地填充与每一个其蜱()方法被调用时的正弦波数据的缓冲器。 一旦缓冲区被填满,它就会提醒输出线,该数据已准备好被写入音轨。 我使用两个线程的原因是audiotrack.write()块,我想工作线程能够开始尽快处理其数据(而不是等待音轨写完)。 UI上的滑块只是改变了工作线程的变量,使得任何改变的频率(通过滑块)将由工作线程的蜱()方法来读取。
什么工作:几乎所有的东西; 螺纹沟通好,有似乎并不在播放任何间隙或点击。 尽管大的缓冲区大小(感谢机器人),响应是OK。 频率可变确实变化,因为这样做在蜱()方法中的缓冲液的计算过程中使用的中间值(由Log.i验证())。
什么不起作用:出于某种原因,我似乎无法获得可听频率连续变化。 当我调整滑块,频率变化步骤,经常一样宽四分之三或五分之四。 从理论上讲,我应该听变化分钟为1Hz的,但我不是。 奇怪的是,它好像更改滑块造成正弦波通过在调和级数间隔发挥; 不过,我可以验证频率变量不是捕捉到默认频率的整数倍。
我的音轨被设置为这样的:
_buffSize = AudioTrack.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);
_audioTrackOut = new AudioTrack(AudioManager.STREAM_MUSIC, _sampleRate, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT, _buffSize, AudioTrack.MODE_STREAM);
工作线程的缓冲区被填充(通过蜱())这样:
public short[] tick()
{
short[] outBuff = new short[_outBuffSize/2]; // (buffer size in Bytes) / 2
for (int i = 0; i < _outBuffSize/2; i++)
{
outBuff[i] = (short) (Short.MAX_VALUE * ((float) Math.sin(_currentAngle)));
//Update angleIncrement, as the frequency may have changed by now
_angleIncrement = (float) (2.0f * Math.PI) * _freq / _sampleRate;
_currentAngle = _currentAngle + _angleIncrement;
}
return outBuff;
}
音频数据被这样写的:
_audioTrackOut.write(fromWorker, 0, fromWorker.length);
任何帮助将不胜感激。 我怎样才能得到更多的频率逐渐变化? 我很有信心,我的蜱逻辑()是合理的,因为Log.i()验证变量angleIncrement和currentAngle中被正确地更新。
谢谢!
更新:
我在这里发现了一个类似的问题: Android的AudioTrack缓冲问题的解决提出了一个必须能够生产样品足够快的audioTrack,这是有道理的。 我垂下采样率22050Hz的,并进行了一些经验测试 - 我可以填补我缓冲液(通过蜱())在最坏的情况下大约为6ms。 这是绰绰有余。 在22050Hz的,所述audioTrack给我的2048个样本(或4096个字节)的缓冲大小。 因此,每个填充的缓冲器持续音频,这是比需要创建的数据(1〜6毫秒)更长的〜0.0928秒。 所以,我知道,我没有产生样本足够快的任何问题。
我也应该注意到,有关应用程序生命周期的前3秒,它工作正常 - 滑块的顺利横扫产生的音频输出平稳横扫。 在此之后,它开始变得真正波涛汹涌(音大约每100Mhz的唯一变化),之后,将停止响应滑块输入的。
我也是固定的一个错误,但我不认为它有一个作用。 AudioTrack.getMinBufferSize()返回在BYTES允许的最小缓冲器大小以及我使用这个号码作为缓冲器的蜱的长度() - 我现在用这一半数量(每个样品2个字节)。