我喜欢Android Soundpool类的简单性,它适用于我在我的应用程序中使用的标准音频文件。现在我希望用户可以通过在SD卡上指定音频文件来指定某些声音。不幸的是我遇到Soundpool的限制,当声音文件太大我得到了
AudioFlinger无法创建曲目。状态:-12
响应。在进入MediaPlayer的复杂性之前我似乎还要切换到MediaPlayer我想问一下是否有适用于android的音频库
非常感谢。
马丁
答案 0 :(得分:2)
现在我想出了一个非常简单的AudioPool类,它随后使用MediaPlayer类播放添加到其中的音频。这个实现肯定还不成熟,但我只是想分享它,因为它至少可以让人知道如何轻松地解决这个问题。如果您发现此课程有任何问题,请告诉我们。
用法:
AudioPool ap = new AudioPool();
File root = Environment.getExternalStorageDirectory() ;
int id1 = ap.addAudio(root + "/gong1.mp3");
int id2 = ap.addAudio(root + "/gong2.mp3");
int id3 = ap.addAudio(root + "/gong3.mp3");
ap.playAudio(id1);
ap.playAudio(id3);
ap.playAudio(id3);
ap.playAudio(id2);
将播放gong1 - > gong3 - > gong3 - > gong1随后。因为这基本上是我需要的,所以我把它放在这里......
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.util.Log;
public class AudioPool {
static String TAG = "AudioPool";
MediaPlayer mPlayer;
int mAudioCounter;
int mCurrentId;
HashMap<Integer, String> mAudioMap;
LinkedList<Integer> mAudioQueue;
public AudioPool() {
mAudioMap = new HashMap<Integer, String>();
mAudioQueue = new LinkedList<Integer>();
mAudioCounter = 0;
}
public int addAudio(String path) {
Log.d(TAG, "adding audio " + path + " to the pool");
if (mAudioMap.containsValue(path)) {
return getAudioKey(path);
}
mAudioCounter++;
mAudioMap.put(mAudioCounter, path);
return mAudioCounter;
}
public boolean playAudio(int id) {
if (mAudioMap.containsKey(id) == false) {
return false;
}
if (mPlayer == null) {
setupPlayer();
}
if (mPlayer.isPlaying() == false) {
return prepareAndPlayAudioNow(id);
} else {
Log.d(TAG, "adding audio " + id + " to the audio queue");
mAudioQueue.add(id);
}
return true;
}
public Integer[] getAudioIds() {
return (Integer[]) mAudioMap.keySet().toArray(
new Integer[mAudioMap.keySet().size()]);
}
public void releaseAudioPlayer() {
if (mPlayer != null) {
mPlayer.release();
mPlayer = null;
}
}
private boolean prepareAndPlayAudioNow(int id) {
mCurrentId = id;
try {
Log.d(TAG, "playing audio " + id + " now");
mPlayer.reset();
mPlayer.setDataSource(mAudioMap.get(id));
mPlayer.prepare();
mPlayer.start();
return true;
} catch (Exception e) {
Log.d(TAG, "problems playing audio " + e.getMessage());
return false;
}
}
private boolean playAudioAgainNow() {
try {
mPlayer.seekTo(0);
mPlayer.start();
return true;
} catch (Exception e) {
Log.d(TAG, "problems playing audio");
return false;
}
}
private void setupPlayer() {
mPlayer = new MediaPlayer();
mPlayer.setOnCompletionListener(new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
audioDone();
}
});
}
private void audioDone() {
if (mAudioQueue.size() > 0) {
Log.d(TAG, mAudioQueue.size() + " audios in queue");
int nextId = mAudioQueue.removeFirst();
if (mCurrentId == nextId) {
playAudioAgainNow();
} else {
prepareAndPlayAudioNow(nextId);
}
} else {
releaseAudioPlayer();
}
}
private int getAudioKey(String path) {
for (Map.Entry<Integer, String> map : mAudioMap.entrySet()) {
if (map.getValue().compareTo(path) == 0) {
return map.getKey();
}
}
return -1;
}
}
答案 1 :(得分:0)
感谢dorjeduck提供解决方案,但是他的课程基于MediaPlayer
,它具有巨大的延迟。
这是什么意思?这意味着当您调用这些时:
mPlayer.prepare();
mPlayer.start();
并且实际上听到声音的延迟非常明显。例如,当您需要播放一首曲目并立即播放另一首曲目时,即使在高端硬件上,您也会听到延迟。
一种解决方案,可以在播放之前将所有字节加载到内存中,并使用AudioTrack
播放声音字节。
我写了SoundPoolCompat
,它在内部使用了AudioTrack
。您可以传递自定义bufferSize
,并且该缓冲区内的所有数据都将被加载到内存中,并且像SoundPool一样具有较小的延迟播放。超出bufferSize
的所有数据将按需加载(这会增加延迟,类似于MediaPlayer
)。 Api与SoundPool
非常相似,还添加了一项功能,可从Uri加载声音(例如gdrive)。并且有playOnce
方法,在播放文件后将卸载所有资源。
implementation 'com.olekdia:sound-pool:3.0.2'