简体   繁体   中英

Play a .wav file and then Android Text To Speech in a loop

Requirement is to play a chime sound an phrase after that using Android Text to Speech.

for (final Integer orderId : voiceoverIds) {

    alertChimePlayer = MediaPlayer.create(getApplicationContext(), R.raw.orderalert);

    alertChimePlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
        public void onCompletion(MediaPlayer mp) {
            String orderSpeechText = "Number " + orderId;
            textToSpeech.speak(orderSpeechText, TextToSpeech.QUEUE_ADD, null, "ORDER_NO_" + orderId);
            textToSpeech.playSilentUtterance(2000, TextToSpeech.QUEUE_ADD, "PAUSE_NO_" + orderId);

            System.out.println(">>>>>>>>>>>>>>>>>>> orderSpeechText : " + orderSpeechText);
        }   
    });

    alertChimePlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
        @Override
        public void onPrepared(MediaPlayer mp) {
            alertChimePlayer.start();
        }
    });
}

But this only works one time. How to handle this properly?

Good question. Stayed up all night on this. The problem is that in the loop, those chimes just get rapidly sent to the media player all at the same time. Media Player cant really handle that properly. Here is my solution. I am using SoundPool to play the chime because it is better at playing short sounds in repetition. I am also using a timer thread to trigger the "Chime + spoken text-to-speech (tts)" sequences. The tts onUtteranceProgressListener is used to play the tts after the chime sound. Here is the tested code. What you will hear is: chime "number 1" (3 second delay) chime "number 2" (3 second delay) ... continues until terminated

import android.app.Activity;
import android.content.Context;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.SoundPool;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.speech.tts.UtteranceProgressListener;
import android.util.Log;

import java.util.Locale;

public class MainActivity extends Activity implements TextToSpeech.OnInitListener {

    AudioAttributes aa;
    SoundPool sp;
    private TextToSpeech tts;
    int MAX_STREAMS = 5;
    int REPEAT = 0;
    int DELAY = 3000;
    int orderId = 0;

    // Clock thread
    Thread m_clockThread;
    boolean m_bClockThreadStop;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.e("TTS", "Starting...");

        // Set up the sound pool sound
        AudioAttributes aa = new AudioAttributes.Builder()
                .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                .setUsage(AudioAttributes.USAGE_MEDIA)
                .build();

        sp = new SoundPool.Builder()
                .setMaxStreams(8)
                .setAudioAttributes(aa)
                .build();

        // Start the tts
        tts = new TextToSpeech(MainActivity.this,MainActivity.this);
        tts.setLanguage(Locale.US);
    }

    @Override
    public void onInit(int status) {
        Log.e("TTS", "Enter onInit...");
        if (status == TextToSpeech.SUCCESS) {
            int result = tts.setLanguage(Locale.US);
            if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {
                Log.e("TTS", "This Language is not supported");
            } else {
                Log.e("TTS", "onInit Success");
                // create and run clock thread
                createAndRunClockThread(this);
            }
        } else {
            Log.e("TTS", "onInit Fail");
        }
    }

    public void createAndRunClockThread(final Activity act) {
        m_bClockThreadStop=false;
        m_clockThread = new Thread(new Runnable() {
            public void run() {
                while(!m_bClockThreadStop) {
                    try {
                        act.runOnUiThread(new Runnable() {
                            public void run() {
                                playChime();
                            }
                        });
                        Thread.sleep(DELAY);
                    }
                    catch(InterruptedException e) {
                        Log.e("TTS", "ClockThread fail");
                    }
                }
            }
        });
        m_clockThread.start();
    }

    private void playChime() {
        Log.e("TTS", "Entering startChimes...");

        sp.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
            @Override
            public void onLoadComplete(final SoundPool soundPool, final int soundId, int status) {
                final int priority = 0;
                final int repeat = 0;
                final float rate = 1.f; // Frequency Rate can be from .5 to 2.0
                // Set volume
                AudioManager mgr = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
                float streamVolumeCurrent = mgr.getStreamVolume(AudioManager.STREAM_MUSIC);
                float streamVolumeMax = mgr.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
                final float volume = streamVolumeCurrent / streamVolumeMax;
                // Play a chime followed by the tts
                tts.speak("Number " + orderId, TextToSpeech.QUEUE_ADD, null, "ID" + orderId);
                tts.setOnUtteranceProgressListener(new UtteranceProgressListener() {
                    @Override
                    public void onStart(String utteranceId) {
                        // Speaking started.
                        sp.play(soundId, volume, volume, priority, repeat, rate);
                    }
                    @Override
                    public void onDone(String utteranceId) {
                        // Speaking stopped.
                        orderId = orderId + 1;
                    }
                    @Override
                    public void onError(String utteranceId) {
                        // There was an error.
                    }
                });
            }
        });
        sp.load(this, R.raw.beep, 1);
    }
}

Thanks for the answer @Mark W. But I was thinking of solution that doesn't involve explicit delays/sleeps.

So I was implementing this Service Class.

public class OrderNoticeService extends Service implements TextToSpeech.OnInitListener {
    private List<OrderSpeechAsyncTask> orderSpeechAsyncTasks = new ArrayList<>();
    private TextToSpeech textToSpeech;
    private Context context;

    public void addToOrderNoticeQueue(int orderId) {
        String orderSpeechText = String.format(getResources().getString(R.string.order_voice_over_default_text), Integer.toString(orderId));
        orderSpeechAsyncTasks.add(new OrderSpeechAsyncTask(getApplicationContext(), R.raw.orderalert, orderSpeechText, textToSpeech, new AsyncTaskCallback() {
            @Override
            public void onTaskCompleted(Object response) {
            }
        }));

        if (orderSpeechAsyncTasks.size() > 1) {
            final OrderSpeechAsyncTask orderSpeechAsyncTask = orderSpeechAsyncTasks.get(orderSpeechAsyncTasks.size() - 1);
            OrderSpeechAsyncTask orderSpeechAsyncTaskPrior = orderSpeechAsyncTasks.get(orderSpeechAsyncTasks.size() - 2);
            orderSpeechAsyncTaskPrior.setAsyncTaskCallback(new AsyncTaskCallback() {
                @Override
                public void onTaskCompleted(Object response) {
                    try {
                        orderSpeechAsyncTask.execute();
                        System.out.println("Execution!");
                    } catch (Exception e) {

                    }
                }
            });
        }
    }

    @Override
    public void onCreate() {
        textToSpeech = new TextToSpeech(this, this);
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        context = this;
        return Service.START_STICKY;
    }

    private static final String TAG = "OrderNoticeService";

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, "OrderNoticeService onBind");
        return mBinder;
    }

    @Override
    public void onDestroy() {
        if (textToSpeech != null) {
            textToSpeech.stop();
            textToSpeech.shutdown();
        }
        Log.i(TAG, "OrderNoticeService onDestroy");
    }

    @Override
    public void onInit(int status) {
        if (status == TextToSpeech.SUCCESS) {
            OrderNoticeVoiceOverThread orderNoticeVoiceOverThread = new OrderNoticeVoiceOverThread(context, orderSpeechAsyncTasks);
            orderNoticeVoiceOverThread.start();
        } else {
            System.out.println("Text To Speech not supported!");
        }
    }

    private class OrderNoticeVoiceOverThread extends Thread {
        private Context context;
        private List<OrderSpeechAsyncTask> orderSpeechAsyncTasks;
        private boolean anyTaskRunning = false;

        public OrderNoticeVoiceOverThread(Context context, List<OrderSpeechAsyncTask> orderSpeechAsyncTasks) {
            this.context = context;
            this.orderSpeechAsyncTasks = orderSpeechAsyncTasks;
        }

        public void run() {
            while (true) {

                for (OrderSpeechAsyncTask orderSpeechAsyncTask : new ArrayList<OrderSpeechAsyncTask>(orderSpeechAsyncTasks)) {
                    if (orderSpeechAsyncTask != null && orderSpeechAsyncTask.getStatus().equals(AsyncTask.Status.RUNNING)) {
                        anyTaskRunning = true;
                        break;
                    }
                }

                if (!anyTaskRunning) {
                    for (OrderSpeechAsyncTask orderSpeechAsyncTask : new ArrayList<OrderSpeechAsyncTask>(orderSpeechAsyncTasks)) {
                        if (orderSpeechAsyncTask != null && orderSpeechAsyncTask.getStatus().equals(AsyncTask.Status.PENDING)) {
                            orderSpeechAsyncTask.execute();
                            anyTaskRunning = false;
                            break;
                        }
                    }
                }
            }
        }
    }

    private final IBinder mBinder = new LocalBinder();

    public class LocalBinder extends Binder {
        public OrderNoticeService getService() {
            return OrderNoticeService.this;
        }
    }
}

And the OrderSpeechAsyncTask as follows.

public class OrderSpeechAsyncTask extends AsyncTask<Void, Void, Void> {
    private static final String LOG_TAG = OrderSpeechAsyncTask.class.getSimpleName();
    private MediaPlayer mediaPlayer;
    private int soundId;
    private Context context;
    private String orderSpeechText;
    private AsyncTaskCallback asyncTaskCallback;
    private TextToSpeech textToSpeech;

    public OrderSpeechAsyncTask(final Context context, int soundId, String orderSpeechText, TextToSpeech textToSpeech, AsyncTaskCallback asyncTaskCallback) {
        this.context = context;
        this.soundId = soundId;
        this.orderSpeechText = orderSpeechText;
        this.textToSpeech = textToSpeech;
        this.asyncTaskCallback = asyncTaskCallback;
    }

    public AsyncTaskCallback getAsyncTaskCallback() {
        return asyncTaskCallback;
    }

    public void setAsyncTaskCallback(AsyncTaskCallback asyncTaskCallback) {
        this.asyncTaskCallback = asyncTaskCallback;
    }

    @Override
    protected Void doInBackground(Void... params) {
        mediaPlayer = MediaPlayer.create(context, soundId);
        mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                mediaPlayer.release();
                textToSpeech.speak(orderSpeechText, TextToSpeech.QUEUE_ADD, null, "ORDER_NO_" + orderSpeechText);
                textToSpeech.playSilentUtterance(2000, TextToSpeech.QUEUE_ADD, "PAUSE_NO_" + orderSpeechText);

                textToSpeech.setOnUtteranceProgressListener(new UtteranceProgressListener() {
                    @Override
                    public void onStart(String utteranceId) {

                    }

                    @Override
                    public void onDone(String utteranceId) {
                        asyncTaskCallback.onTaskCompleted(null);
                    }

                    @Override
                    public void onError(String utteranceId) {

                    }
                });
            }
        });
        mediaPlayer.start();

        return null;
    }
}

This so far handles the following;

  • Playing the sound and the text in the list
  • Adding an item to the queue whilst the existing list is still being read

To do;

  • This doesn't handle any new item that gets added to the list once the existing items are done being read.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM