[英]Android audio programming nightmare - soundpool, audiotrack arrghh?
我已經構建了一個簡單的音樂音序器Android應用程序,可以播放多個音頻文件。
最初,我是使用SoundPool播放mp3文件的,它在2.3.4下與舊的HTC Droid Incredible完美配合。 然后,我在運行4.3的Galaxy Nexus上進行了測試,其性能令人震驚。 整個地方都有音頻定時,並且有小故障/咔嗒聲/爆裂聲。
因此,我花了幾天的時間使用AudioTrack制作了一個包含mp3解碼器的播放器,並使其在Galaxy和HTC上都能正常工作。 現在,我剛剛在Nexus 4(運行4.3)上對其進行了測試,性能非常糟糕-時機到處都是。 SoundPool甚至在此設備上提供更好的性能。
我真的很沮喪,不知道該怎么做才能完成我的應用程序,因此,如果有人可以幫助我,我將非常感激。 我在下面放了一些音頻播放器的代碼示例。 我已經嘗試了所有可以想到的方法,包括更改緩沖區大小,使用AudioTrack.MODE_STATIC
等。新的Google設備具有低延遲音頻,因此,在我的舊機器人上一切正常的工作方式非常奇怪!
提前致謝
/**
* 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;
}
}
}
就像用戶harikris所建議的那樣,我強烈建議您使用OpenSL ES庫將所有音頻播放和處理代碼移至Android NDK,以獲得最佳性能。
據我了解,AudioTrack API建立在OpenSL ES緩沖區隊列音頻播放器之上。 因此,您可以直接與NDK合作,編寫從Java / Android層調用以處理聲音的C代碼,從而提高性能。
上面提到的本地音頻示例包含的代碼將向您展示如何直接從URI播放聲音文件。 以我的經驗,這種方法的結果要好於靜態模式下的AudioTrack。
通常,Soundpool保留用於可以從內存播放的非常短的聲音,它不是定序器的可伸縮解決方案,尤其是在引入大文件時。
以下是一些對我的應用程序有幫助的鏈接:-有關適用於Android的OpenSL ES的常規信息: http : //mobilepearls.com/labs/native-android-api/opensles/
-帶有一些出色示例代碼的Android音頻博客: http : //audioprograming.wordpress.com
編輯:舊的移動珍珠鏈接似乎已關閉。 這是一個有效的方法: http : //mobilepearls.com/labs/native-android-api/ndk/docs/opensles/index.html
在Google I / O 2013的“ 高性能音頻”會議上(由Ian Ni-Lewis介紹,他對原始帖子發表了評論。我很驚訝他沒有對此發表評論),他們談論了Nexus4。在視頻中演示文稿的內容,跳到27:25。
與許多其他使用44.1kHz原始采樣率的設備不同,Nexus 4使用48kHz。 如果您以44.1kHz的頻率提交數據,則必須通過慢速路徑上的重采樣器。 另一個區別是Nexus 4的緩沖區大小為240幀,這可能會導致回調中出現常規抖動。 鏈接的視頻中對此都有所有說明。
我建議您使用本機代碼進行所有音頻捕獲和對時間敏感的處理,並使用JNI在SDK和NDK組件之間進行交互。
您可以在Android NDK發行版中找到本機音頻項目示例代碼,向您展示如何在C / C ++中進行音頻處理
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.