この音はとてもシンプルなので、なぜ答えが見つからないのか理解できません笑
サウンドプールクラスが機能しています(チュートリアルといくつかの調整のおかげで)、正常に機能します。
今の問題は、バックグラウンドミュージックをランダムに変更できるようにしたいということです。(ループ内に常に同じ音楽があるとは限りませんが、2つまたは3つあり、1つが終了すると、他の2つのうちの1つを再生します)。
問題は、音楽の再生が終了したことを通知する方法が見つからないことです。
何か案は ?
ジェイソン
これが私がすることです:
起動時に、 MediaPlayerを使用して各サウンドクリックの長さを取得します。
private long getSoundDuration(int rawId){
MediaPlayer player = MediaPlayer.create(context, rawId);
int duration = player.getDuration();
return duration;
}
サウンドと継続時間を一緒に(DTOタイプのオブジェクトに)保存します。
私の知る限り、SoundPoolでは実行できません。
完了通知を提供できることがわかっている唯一のオーディオ「プレーヤー」は MediaPlayerです。これは、SoundPoolよりも複雑な獣ですが、再生が完了したときに通知されるようにOnCompletionListenerを設定できます。
私は100以上の短いサウンドクリップを持っており、SoundPoolが私の最良の選択肢です。別のクリップの再生が終了した直後に、あるクリップを再生したい。onCompletionListener()に相当するものがないことがわかったので、ランナブルを実装することを選択しました。最初のサウンドの長さが1〜2秒で、実行可能な期間が2000に設定されているので、これは私にとってはうまくいきます。多くの可能性があるので、このクラスで機能することを願っています。
MediaPlayerはSoundPoolに比べて重くて遅いですが、SoundPoolにはsetOnCompletionListenerがありません。この問題に対処するために、setOnCompletionListenerを使用してSoundPoolからカスタムクラスを実装しました。
使用法:MediaPlayerに似ています
SoundPoolPlayer mPlayer = SoundPoolPlayer.create(context, resId);
mPlayer.setOnCompletionListener(
new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) { //mp will be null here
Log.d("debug", "completed");
}
};
);
mPlayer.play();
mPlayer.pause();
mPlayer.stop();
mPlayer.resume();
mPlayer.isPlaying();
プルリクエストは大歓迎です。ここで必要なものだけを実装しました。
私は同様の問題に直面し、再生するサウンドをキューに入れ、各サウンドの再生が完了すると通知するSoundPoolキューを作成しました。Kotlinにありますが、Javaに簡単に翻訳できるはずです。
class SoundPoolQueue(private val context: Context, maxStreams: Int) {
companion object {
private const val LOG_TAG = "SoundPoolQueue"
private const val SOUND_POOL_HANDLER_THREAD_NAME = "SoundPoolQueueThread"
private const val ACTION_PLAY_SOUND = 1
@JvmStatic
fun getSoundDuration(context: Context, soundResId: Int) : Long {
val assetsFileDescriptor = context.resources.openRawResourceFd(soundResId)
val mediaMetadataRetriever = MediaMetadataRetriever()
mediaMetadataRetriever.setDataSource(
assetsFileDescriptor.fileDescriptor,
assetsFileDescriptor.startOffset,
assetsFileDescriptor.length)
val durationString = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)
val duration = durationString.toLong()
logDebug("SoundPoolQueue::getSoundDuration(): Sound duration millis: $durationString")
assetsFileDescriptor.close()
return duration
}
@JvmStatic
private fun logDebug(message: String) {
if(!BuildConfig.DEBUG) {
return
}
Log.d(LOG_TAG, message)
}
}
private var soundPool = if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val attrs = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION_EVENT)
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.build()
SoundPool.Builder()
.setMaxStreams(maxStreams)
.setAudioAttributes(attrs)
.build()
}
else {
@Suppress("DEPRECATION")
SoundPool(maxStreams, AudioManager.STREAM_NOTIFICATION, 0)
}
var soundPoolQueueListener : SoundPoolQueueListener? = null
private val soundPoolHandlerThread = SoundPoolQueueThread().apply { start() }
private val soundPoolSoundsSparseArray = SparseArray<SoundPoolSound>()
private val soundPoolSoundsQueue = LinkedList<SoundPoolSound>()
fun addSound(soundResId: Int, leftVolume: Float, rightVolume: Float, priority: Int, loop: Boolean, rate: Float) {
val durationMillis = getSoundDuration(context = context, soundResId = soundResId)
val soundId = soundPool.load(context, soundResId, priority)
soundPoolSoundsSparseArray.put(soundResId,
SoundPoolSound(durationMillis, soundResId, soundId, leftVolume, rightVolume, priority, loop, rate))
}
fun playSound(soundResId: Int) {
logDebug("SoundPoolQueue::playSound()")
soundPoolSoundsQueue.add(soundPoolSoundsSparseArray[soundResId])
soundPoolHandlerThread.handler?.sendEmptyMessage(ACTION_PLAY_SOUND)
}
inner class SoundPoolQueueThread : HandlerThread(SOUND_POOL_HANDLER_THREAD_NAME) {
var handler: Handler? = null
override fun onLooperPrepared() {
super.onLooperPrepared()
handler = object : Handler(looper) {
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
if(msg.what == ACTION_PLAY_SOUND && handler!!.hasMessages(ACTION_PLAY_SOUND)) {
return
}
if(soundPoolSoundsQueue.isEmpty()) {
logDebug("SoundPoolHandlerThread: queue is empty.")
handler!!.removeMessages(ACTION_PLAY_SOUND)
return
}
logDebug("SoundPoolHandlerThread: Playing sound!")
logDebug("SoundPoolHandlerThread: ${soundPoolSoundsQueue.size} sounds left for playing.")
val soundPoolSound = soundPoolSoundsQueue.pop()
soundPool.play(soundPoolSound.soundPoolSoundId,
soundPoolSound.leftVolume,
soundPoolSound.rightVolume,
soundPoolSound.priority,
if(soundPoolSound.loop) { 1 } else { 0 },
soundPoolSound.rate)
try {
Thread.sleep(soundPoolSound.duration)
}
catch (ex: InterruptedException) { }
//soundPoolQueueListener?.onSoundPlaybackCompleted(soundPoolSound.soundResId)
sendEmptyMessage(0)
}
}
}
}
interface SoundPoolQueueListener {
fun onSoundPlaybackCompleted(soundResId: Int)
}
}
付随するデータクラス
data class SoundPoolSound(val duration: Long,
val soundResId: Int,
val soundPoolSoundId: Int,
val leftVolume: Float,
val rightVolume: Float,
val priority: Int,
val loop: Boolean,
val rate: Float)
でサウンドの再生が完了すると通知が届きます
onSoundPlaybackCompleted(soundResId: Int)
完成した再生音のリソースIDを使用します。
使用例:
private class SoundPoolRunnable implements Runnable {
@Override
public void run() {
LogUtils.debug(SerializableNames.LOG_TAG, "SoundPoolRunnable:run(): Initializing sounds!");
m_soundPoolPlayer = new SoundPoolQueue(GSMSignalMonitorApp.this, 1);
m_soundPoolPlayer.setSoundPoolQueueListener(new SoundPoolQueue.SoundPoolQueueListener() {
@Override
public void onSoundPlaybackCompleted(int soundResId)
{
LogUtils.debug(SerializableNames.LOG_TAG, "onSoundPlaybackCompleted() " + soundResId);
}
});
m_soundPoolPlayer.addSound(R.raw.gsm_signal_lost, 0.2f, 0.2f, 1, false, 1.0f);
m_soundPoolPlayer.addSound(R.raw.gsm_signal_restored, 0.2f, 0.2f, 1, false, 1.0f);
m_soundPoolPlayer.addSound(R.raw.gsm_signal_low, 0.2f, 0.2f, 1, false, 1.0f);
m_soundPoolPlayer.addSound(R.raw.gsm_signal_lost_ru, 0.2f, 0.2f, 1, false, 1.0f);
m_soundPoolPlayer.addSound(R.raw.gsm_signal_restored_ru, 0.2f, 0.2f, 1, false, 1.0f);
m_soundPoolPlayer.addSound(R.raw.gsm_signal_low_ru, 0.2f, 0.2f, 1, false, 1.0f);
}
}
それが役に立てば幸い :)