- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
好的,我有一个频率发生器,它使用 AudioTrack 将 PCM 数据发送到硬件。这是我为此使用的代码:
private class playSoundTask extends AsyncTask<Void, Void, Void> {
float frequency;
float increment;
float angle = 0;
short samples[] = new short[1024];
@Override
protected void onPreExecute() {
int minSize = AudioTrack.getMinBufferSize( 44100, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT );
track = new AudioTrack( AudioManager.STREAM_MUSIC, 44100,
AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT,
minSize, AudioTrack.MODE_STREAM);
track.play();
}
@Override
protected Void doInBackground(Void... params) {
while( Main.this.isPlaying)
{
for( int i = 0; i < samples.length; i++ )
{
frequency = (float)Main.this.slider.getProgress();
increment = (float)(2*Math.PI) * frequency / 44100;
samples[i] = (short)((float)Math.sin( angle )*Short.MAX_VALUE);
angle += increment;
}
track.write(samples, 0, samples.length);
}
return null;
}
}
频率与滑动条相关联,样本生成循环中报告了正确的值。当我启动应用程序时,一切都很好。当您沿着滑动条拖动手指时,您会听到美妙的扫过声音。但在玩了大约 10 秒后,音频开始变得不稳定。它不是平滑的扫描,而是交错的,并且仅每 1000 Hz 左右改变音调。关于可能导致此问题的任何想法?
如果问题出在其他地方,这里是所有代码:
public class Main extends Activity implements OnClickListener, OnSeekBarChangeListener {
AudioTrack track;
SeekBar slider;
ImageButton playButton;
TextView display;
boolean isPlaying=false;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
display = (TextView) findViewById(R.id.display);
display.setText("5000 Hz");
slider = (SeekBar) findViewById(R.id.slider);
slider.setMax(20000);
slider.setProgress(5000);
slider.setOnSeekBarChangeListener(this);
playButton = (ImageButton) findViewById(R.id.play);
playButton.setOnClickListener(this);
}
private class playSoundTask extends AsyncTask<Void, Void, Void> {
float frequency;
float increment;
float angle = 0;
short samples[] = new short[1024];
@Override
protected void onPreExecute() {
int minSize = AudioTrack.getMinBufferSize( 44100, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT );
track = new AudioTrack( AudioManager.STREAM_MUSIC, 44100,
AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT,
minSize, AudioTrack.MODE_STREAM);
track.play();
}
@Override
protected Void doInBackground(Void... params) {
while( Main.this.isPlaying)
{
for( int i = 0; i < samples.length; i++ )
{
frequency = (float)Main.this.slider.getProgress();
increment = (float)(2*Math.PI) * frequency / 44100;
samples[i] = (short)((float)Math.sin( angle )*Short.MAX_VALUE);
angle += increment;
}
track.write(samples, 0, samples.length);
}
return null;
}
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
display.setText(""+progress+" Hz");
}
public void onClick(View v) {
if (isPlaying) {
stop();
} else {
start();
}
}
public void stop() {
isPlaying=false;
playButton.setImageResource(R.drawable.play);
}
public void start() {
isPlaying=true;
playButton.setImageResource(R.drawable.stop);
new playSoundTask().execute();
}
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onStop() {
super.onStop();
//Store state
stop();
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
}
最佳答案
我找到原因了!
问题是 AudioTrack 缓冲区的大小。如果您不能足够快地生成样本,样本就会用完,播放会暂停,并在再次有足够的样本时继续。
唯一的解决方案是确保您可以足够快地生成样本 (44100/s)。作为快速修复,尝试将采样率降低到 22000(或增加缓冲区的大小)。
至少这对我来说是这样的——当我优化我的样本生成例程时,跳跃就消失了。
(增加缓冲区的大小会使声音稍后开始播放,因为等待一些保留 - 直到缓冲区填满。但是,如果您生成样本的速度不够快,样本最终会用完) .
哦,你应该把不变变量放在循环之外!并且可能使样本数组更大,以便循环运行更长的时间。
short samples[] = new short[4*1024];
...
frequency = (float)Main.this.slider.getProgress();
increment = (float)(2 * Math.PI) * frequency / 44100;
for(int i = 0; i < samples.length; i++)
{
samples[i] = (short)((float)Math.sin(angle) * Short.MAX_VALUE);
angle += increment;
}
您还可以预先计算频率为 200Hz-8000Hz 的所有正弦值,步长为 10Hz。或者按需执行此操作:当用户选择频率时,使用您的方法生成样本一段时间,并将它们保存到数组中。当您生成足够多的样本以获得一个完整的正弦波时,您可以继续在数组上循环(因为 sin 是一个周期函数)。实际上,声音中可能存在一些小的不一致,因为您从未准确地击中一个周期(因为您将角度增加 1,并且一个完整正弦波的长度是 sampleRate/(double)frequency
样本) .但取周期的正确倍数会使不一致变得不明显。
另请参阅 http://developer.android.com/guide/practices/design/performance.html
关于Android AudioTrack 缓冲问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3305598/
我正在使用 MediaPlayer 在我的应用程序中播放在线 mp3 文件中的一些声音。 但是,即使在播放完成后,我也会在控制台中收到一行又一行的回调和缓冲。 10-24 08:08:48.467
我有一个简单的多边形。 dfr p = st_polygon(list(as.matrix(dfr))) > pbuf = st_buffer(p, .4) > plot(pbuf) > plot(
这可能又是一些愚蠢的问题,也许这确实是我所缺少的东西,但我很难让 glMultiDrawArrays 在 OpenGL4 中工作。 我发现了很多这样的解释: for (int i = 0; i #i
这仅仅是根据网络速度调整预缓冲内容量的问题吗?你是否在一开始就为此调整一次,每秒......? 或者它更复杂 - 对您的网络速度记录历史进行采样并取平均值/中值并对其进行调整? 最佳答案 您的第二段总
嗨,我正在使用 FFmpeg Autogen C#。当我使用 mkv 输出作为文件并使用 h264 rtsp 流作为输入时,一切正常。编解码器是 libx264 ffmpeg.avio_open(
我需要多次遍历几个文本文件的行。目前这是通过多个 with open("file.txt") as f: for line in f: # do something 虽然性能还
昨天给同学们写了一个xinetd小练习:做一个反向回显程序。 为了学习新东西,我尝试实现一个 Haskell 解决方案。琐碎的main = forever $ interact reverse不起作用
我正在阅读《实时渲染第三版》中的遮挡剔除部分,但我无法理解它是如何工作的。一些问题: “Z 金字塔”有何贡献?为什么我们需要多种分辨率的 Z 缓冲区?在书中,它的显示如下(左侧): 八叉树结构与用于一
我通过串行端口与设备通信。 我已成功获取 InputStream 并读取设备发送的内容。 但问题是,我根本不知道何时停止阅读并继续执行另一项任务。 这是简化的代码: inputStream = ser
我有以下代码: func (q *Queue) GetStreams(qi *QueueInfo) { channel := make(chan error, len(qi.AudioChun
在我调用 -play 之前,有没有办法让 MPMusicPlayerController 缓冲内容?还是在您设置队列时默认执行此操作? AVAudioPlayer 有 -prepareToPlay 方
我正在编写一个数据库 备份函数,从System.Diagnostics.Process 对象 读取StandardOutput (StreamReader) 属性。我已成功写入普通文件。 //This
我有一个 wpf 应用程序,其中所有 viewModel 都继承自实现 INotifyPropertyChanged 的类 NotifyPropertyChangeClass(见下文)。 我想限制
我需要类似于 withLatestFrom 的东西,对应于下图: ---------A-----------------B-- -1-2-3------4------5-6-7-8---- -----
有没有办法缓冲 OutputStream,在返回之前修改它?这是我的代码片段: public ServletOutputStream getOutputStream() throws IOExcept
目前我们有实现服务器通信协议(protocol)缓冲的需求。如果有人对此有任何意见,他们可以向我提供任何意见吗。 最佳答案 请查看以下 Protocol Buffer 链接。 http://code.
所以我目前正在开发一个 Java 应用程序,该应用程序应该将特定事件记录到数据库中。我希望每分钟最多有 15 到 20 次插入,基本上我想知道我是否应该为每个插入语句建立一个新连接,或者只要应用程序正
请考虑以下代码,包括两个线程 buffering_thread(用一条消息填充缓冲区指针)和 sending_thread(清空缓冲区): #include "msg.cpp" msg * buffe
是否可以在线播放由两个或多个视频文件组成的视频? 由于我原来的帖子不够清楚,这里有扩展的解释和问题。 我的站点托管在 Linux/Apache/PHP 服务器上。我有 FLV/F4V 格式的视频文件。
这是我用于缓冲和转换传入事件的代码: public Publisher> logs(String eventId) { ConnectableObservable connectableObs
我是一名优秀的程序员,十分优秀!