java - 等待声音结束后再继续

标签 java android for-loop audio android-mediaplayer

我正在创建一个迭代字符串的循环,并根据存在的角色播放不同的声音。不幸的是,它不会等到声音结束才继续,这会引发媒体播放器错误。我的循环如下所示:

for (char ch : morsechars) {
            if (Character.toString(ch).equals(".") ) {
                Log.d("play: ", "dot");
                try {
                    startPlayback();
                } catch (Exception e){
                    e.printStackTrace();
                }
            } else if (Character.toString(ch).equals("-")){
                Log.d("play: ", "dash");
                try {
                    startPlayback();
                } catch (Exception e){
                    e.printStackTrace();
                }
            } 
        }

我的播放器看起来像这样:

public void startPlayback() throws Exception{
        play = MediaPlayer.create(this, dotFILE);
        play.start();
        isplaying = true;
        while (isplaying == true){

        }


        play.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            public void onCompletion(MediaPlayer play) {
                isplaying = false;
                play.stop();
                play.reset();
                play.release();
            }
        });
    }

我不确定如何在继续循环中的下一个字符之前等待完成监听器。

最佳答案

1) MediaPlayer 不是适合这项工作的工具。看SoundPool 。它适用于重复播放的短声音。

2)对于你的情况,你想要的并不是真正的循环。声音将异步播放,因此您会遇到播放调用相互叠加的情况。这将导致 MediaPlayer 的 native 代码耗尽可用资源。

您想要的是一系列回调。 MediaPlayer 具有您已经在使用的完成回调,但是,虽然这似乎正是您所需要的,但它不足以满足您的用例。 MediaPlayer 的 native 代码将无法足够快地释放其资源以供后续每次调用播放,并且将发生相同的资源耗尽。对于 MediaPlayer 的工作方式来说,这只是一个不幸的现实。

SoundPool 没有那么方便的完成回调,但在这种情况下,您可能只有两个声音样本,并且您知道它们有多长。一个简单的定时器延迟可能就足够了。您可以使用 Handler.postDelayed 来实现此效果。在每个回调事件中,只需播放下一个声音,前进您用于跟踪当前声音的任何变量,然后再次发布相同的回调以播放任何剩余的声音。这是一种异步循环。

更新

为了帮助理解发生了什么,这里摘录了关于 Handler 的 Android 文档。 :

A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.

因此,如果我对您的代码结构的猜测接近正确,您将创建一个 Handler 对象作为 Activity 类的成员。这会将其绑定(bind)到 GUI 线程,这应该没问题,因为您不会在那里做太多工作。您还需要一个 Runnable 来充当发布的任务。也让它成为你类(class)的一员。并且您需要包含要成为成员的字符的String。您将需要一个 int 来跟踪您当前所在的角色。

private Handler playMorseHandler = new Handler();
private String morseChars = ".--...---"; // I don't know where you set this
private int charIndex = 0;
private Runnable playMorseTask = new Runnable() {
    if (morseChars == null ||
        charIndex < 0 ||
        charIndex >= morseChars.length()) {
        Log.w("whateverTag", "Basic assumptions failed");
        return;
    }
    char ch = morseChars.charAt(charIndex);
    long lengthOfClip = 0L;
    boolean postAgain = true;
    if (ch == '.') {
        Log.d("play: ", "dot");
        lengthOfClip = 300L; // I don't know how long your clips are
        try {
            startPlayback();
        } catch (Exception e){
            e.printStackTrace();
        }
    } else if (ch == '-') {
        Log.d("play: ", "dash");
        lengthOfClip = 500L; // I don't know how long your clips are
        try {
            startPlayback();
        } catch (Exception e){
            e.printStackTrace();
        }
    } else {
        Log.d("whateverTag", "unexpected input");
        postAgain = false;
    }
    ++charIndex;
    if (postAgain && charIndex < morseChars.length()) {
        playMorseHandler.postDelayed(this, lengthOfClip);
    }
};

无论您想从哪里开始播放声音,您只需调用:

charIndex = 0;
playMorseHandler.post(playMorseTask);

剩下的唯一一件事就是让您的 startPlayback() 方法做正确的事情,即使用 SoundPool 而不是 MediaPlayer 。这是因为,正如我之前提到的,MediaPlayer 将不可避免地导致系统在如此快速地播放这些短声音时耗尽资源。

关于java - 等待声音结束后再继续,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46899978/

相关文章:

Java ConcurrentHashMap 操作原子性

android - 从服务调用带有 FLAG_ACTIVITY_REORDER_TO_FRONT 的 Activity

java - 对于循环 1a ,2b, 3c... 在 java 中

java - 将 Nifi 属性值拆分为多个属性

java - Model 1 是 MVC 架构的子类吗?

android - 如何在 OpenGL ES 2.0 中将 float 组传递给着色器?

javascript - 如何使用 JavaScript 中的 for 循环列出数组中的项目?

ios - objective-c 中这两个for循环之间的区别

java - 使用 log4j 在没有日期和时间的控制台中打印错误

类似于 Youtube 应用的 Android VideoView 方向