【问题标题】:Android SoundPool: get notified when end of playedAndroid SoundPool:播放结束时收到通知
【发布时间】:2011-05-25 01:51:28
【问题描述】:

这听起来很简单,以至于我无法弄清楚为什么我找不到答案哈哈

我有一个可用的声音池类(感谢教程和我所做的一些调整),它运行良好。

现在的问题是我希望能够随机更改我的背景音乐。 (并不总是循环播放相同的音乐,但有 2 或 3 个,当一个完成时,我播放另外 2 个中的一个)。

问题是我找不到通知音乐已播放完毕的方法。

有什么想法吗?

杰森

【问题讨论】:

    标签: android soundpool


    【解决方案1】:

    这就是我的工作:

    启动时,我使用 MediaPlayer 获取每次声音点击的长度:

    private long getSoundDuration(int rawId){
       MediaPlayer player = MediaPlayer.create(context, rawId);
       int duration = player.getDuration();
       return duration;
    }
    

    并将声音和持续时间一起存储(在 DTO 类型的对象中)。

    【讨论】:

    • 谢谢; +1。请注意,您还可以在返回之前调用 player.release()。
    • 谢谢!像魅力一样工作!
    • 也许通过一些离线工具(例如 ffmpeg)导出音频的持续时间并在应用启动时创建一个查找表是一个好主意。 AFAIK 创建 MediaPlayer 将涉及意外开销。
    【解决方案2】:

    据我所知,SoundPool 无法做到这一点。

    我所知道的唯一可以提供完成通知的音频“播放器”是 MediaPlayer - 它比 SoundPool 更复杂,但允许设置 OnCompletionListener 在播放完成时收到通知。

    【讨论】:

    • 这是我得出的结论,但我想确定一下。我肯定必须使用媒体播放器,因为 SoundPool 无法处理大声音(我在某处读到超过 100ko)谢谢
    • ko 是什么意思?
    • @DanielG.R. :我怀疑 Jason Rogers 的意思只是 100k(如 100 KB),但键盘上的 o 键在 k 附近,他可能不小心碰到了它。
    • @JasonRogers SoundPool 可以处理未压缩流最大为 1 MB 或 1 MiB 的文件。
    【解决方案3】:

    我有 100 多个短声音片段,SoundPool 是我的最佳选择。我想在另一个剪辑完成播放后播放一个剪辑。在发现没有 onCompletionListener() 等效项后,我选择实现一个可运行的。这对我有用,因为第一个声音的长度在 1 到 2 秒之间,所以我将可运行的持续时间设置为 2000。希望他们在这门课上工作,因为它有很大的潜力!

    【讨论】:

    • 如果需要暂停但它的持续时间并不重要,这是一个不错的选择。
    【解决方案4】:

    MediaPlayer 与 SoundPool 相比笨重且缓慢,但 SoundPool 没有 setOnCompletionListener。为了解决这个问题,我使用 setOnCompletionListener 从 SoundPool 实现了一个 custom class

    用法:类似于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();
    

    欢迎任何拉取请求。我在这里只实现了我需要的。

    【讨论】:

    • 你会如何用这个循环播放音频?
    【解决方案5】:

    我遇到了类似的问题,并创建了一个 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);
            }
        }
    

    希望对你有帮助:)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-05
      相关资源
      最近更新 更多