我已经建立了一个简单的音乐排序Android应用程序,可以播放多个音频文件。
最初,我使用SoundPool播放mp3文件,它在2.3.4上与一个老HTC完美地工作在一起,难以置信。然后我在运行4.3的Galaxy上进行了测试,结果非常糟糕。音频定时到处都是,出现了故障/点击/pops。
所以我花了几天时间用AudioTrack制作了一个播放器,其中包括一个mp3解码器,并使它在Galaxy和宏达电上都能很好地工作。现在,我刚刚在Nexus 4(运行4.3)上测试了它,性能非常糟糕--时间到处都是。SoundPool甚至在这个设备上提供了更好的性能。
我真的很沮丧,不知道该怎么做才能完成我的应用程序,所以如果有人能帮助我,我会非常感激的。我在下面放了一些音频播放器的代码样本。我已经尝试了我能想到的一切,包括改变缓冲区大小,使用AudioTrack.MODE_STATIC等。新的谷歌设备有低延迟音频,所以这是非常奇怪的工作方式,一切如何更好地在我的旧机器人!
提前感谢
/**
* Play note
*/
public void playNote(String note, float vol)
{
PlayThread oldThread = threadMap.get(note);
if(oldThread != null) {
//Cancel timer
if(oldThread.timer != null) {
oldThread.timer.cancel();
oldThread.timer.purge();
oldThread.timer = null;
}
//Stop
oldThread.requestStop();
threadMap.remove(note);
}
//Play if within Polyphony
if(threadMap.size() < POLYPHONY) {
PlayThread thread = new PlayThread(note, vol);
thread.start();
threadMap.put(note, thread);
}
}
/**
* Stop note
*/
public void stopNote(String note, int fadeDurationInMs)
{
PlayThread thread = threadMap.get(note);
if(thread != null) {
thread.fadeOut(fadeDurationInMs);
threadMap.remove(note);
}
}
/**
* Stop all
*/
public void stopAllPlaying(int fadeDurationInMs)
{
for(PlayThread thread : threadMap.values()) {
if(thread != null) {
thread.fadeOut(fadeDurationInMs);
}
}
threadMap.clear();
}
/**
* PlayThread
*/
private class PlayThread extends Thread
{
String note;
float vol;
float fadeVol;
boolean stop;
AudioTrack audioTrack;
Timer timer;
/**
* Constructor
*/
public PlayThread(String note, float vol)
{
super();
this.note = note;
this.vol = vol;
this.fadeVol = vol;
}
/**
* Run
*/
public void run()
{
try {
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
//Create buffer
int bufferSize = AudioTrack.getMinBufferSize(SAMPLE_RATE, CHANNELS, AudioFormat.ENCODING_PCM_16BIT);
Log.v(Constants.TAG, "min buffersize = " + bufferSize);
bufferSize = bufferSize * 2;
byte[] buffer = new byte[bufferSize];
//AudioTrack
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, SAMPLE_RATE, CHANNELS, AudioFormat.ENCODING_PCM_16BIT, bufferSize, AudioTrack.MODE_STREAM);
audioTrack.setStereoVolume(vol, vol);
audioTrack.play();
//Get byte data
byte[] byteData = sampleMap.get(note);
//Convert to input stream
InputStream input = new ByteArrayInputStream(byteData);
//Write to audioTrack
int bytesRead = 0;
while(!stop && (bytesRead = input.read(buffer)) != -1) {
audioTrack.write(buffer, 0, bytesRead);
}
//When finished...
audioTrack.stop();
audioTrack.release();
input.close();
killThread(this);
}
catch(Exception e) {}
}
/**
* Set volume
*/
private synchronized void setVol(float newVol)
{
audioTrack.setStereoVolume(newVol, newVol);
}
/**
* Update volume
*/
private synchronized void lowerVol()
{
fadeVol -= 0.01;
if(fadeVol < 0) vol = 0;
audioTrack.setStereoVolume(fadeVol, fadeVol);
}
/**
* Fade out
*/
public synchronized void fadeOut(int fadeDurationInMs)
{
//Start decreasing volume
if(fadeDurationInMs > 0) {
timer = new Timer(true);
TimerTask timerTask = new TimerTask()
{
@Override
public void run()
{
//If thread killed while running
try {
//Lower volume
lowerVol();
}
catch (Exception e) {}
//Stop when volume reaches 0
if(fadeVol <= 0) {
if(timer != null) {
timer.cancel();
timer.purge();
}
stop = true;
}
}
};
//Calculate delay, set to 1 if zero
int delay = (int) (fadeDurationInMs / (vol * 100));
if(delay == 0) delay = 1;
timer.schedule(timerTask, delay, delay);
}
}
/**
* Request stop
*/
public synchronized void requestStop()
{
//Stop click/pop when stopping sample
setVol(0.01f);
setVol(0.005f);
stop = true;
}
/**
* Kill Thread
*/
private synchronized void killThread(Thread theThread)
{
if(theThread != null) {
theThread = null;
}
}
}发布于 2013-09-30 20:58:09
正如用户harikris建议的那样,我强烈建议您使用OpenSL ES库将所有音频回放和处理代码移到Android,以获得最佳性能。
据我所知,AudioTrack API构建在OpenSL ES缓冲队列音频播放器之上。因此,您可能可以通过直接使用NDK来提高性能,编写从Java/Android层调用的C代码来处理声音。
上面提到的本机音频示例包含代码,这些代码将向您展示如何从URI直接播放声音文件。在我的经验中,这种方法的结果优于静态模式下的AudioTrack。
声音池通常是为可以从内存播放的非常短的声音而保留的,它对于您的排序器来说不是一个可伸缩的解决方案,特别是当您引入大型文件时。
以下是一些帮助我完成我的应用程序的链接:-General Information on OpenSL ES for Android:http://mobilepearls.com/labs/native-android-api/opensles/
-An Android音频博客有一些很好的示例代码:http://audioprograming.wordpress.com
编辑:旧的移动珍珠链接似乎已经关闭。下面是一个有用的例子:http://mobilepearls.com/labs/native-android-api/ndk/docs/opensles/index.html
发布于 2013-12-12 05:36:24
在2013年谷歌I/O的高性能音频会议上(由Ian介绍,他对最初的帖子进行了评论)。我很惊讶他没有注意到这一点),他们谈论Nexus 4。在演示的视频中,跳转到27:25。
与使用本机44.1kHz采样率的许多其他设备不同,Nexus 4使用48 the。如果你在44.1kHz上提交数据,它将必须通过位于慢路径上的重放器。另一个不同之处是,Nexus 4上的缓冲区大小是240帧,这会导致回调中的规则抖动。这在链接视频中都有解释。
发布于 2013-09-18 16:28:08
我建议您在本机代码中执行所有音频捕获和时间敏感处理,并使用JNI在SDK和NDK组件之间进行交互。
您可以在Android发行版中找到本机音频项目示例代码,向您展示如何使用C/C++进行音频操作。
https://stackoverflow.com/questions/18794965
复制相似问题