Franco Franco - 4 months ago 7
Android Question

Play different sounds when different strings are displayed in textview

I have some code for a quiz-style game that randomly changes the text in a TextView when the correct button is pressed. Now, I would like to play a sound that says the word that is being displayed when the string in the TextView changes. Also, I want the previous sound to be repeated when an incorrect button is pressed. How would I go about doing this? Is there a way to associate variables so that they are displayed/sounded at the same time?

I have some .ogg audio files in my raw folder that I would like to use.

Many thanks!!!

import android.os.Bundle;
import android.content.res.Resources;
import android.view.View;
import android.widget.TextView;
import android.widget.Button;
import java.lang.String;
import java.util.Random;


public class MainActivity extends AppCompatActivity implements View.OnClickListener {

private static final Random r_generator = new Random();
String textViewString;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Resources res = getResources();
TextView tv = (TextView) findViewById(R.id.animal_text);
String[] myString = res.getStringArray(R.array.englishAnimalArray);
String q = myString[r_generator.nextInt(myString.length)];
tv.setText(q);
textViewString = tv.getText().toString();

}

@Override
public void onClick(View view) {

Button btn = (Button) view;
TextView tv = (TextView) findViewById(R.id.animal_text);
textViewString = tv.getText().toString();

if (btn.getText().equals(textViewString)) {

Resources res = getResources();
String[] myString = res.getStringArray(R.array.englishAnimalArray);
String q = myString[r_generator.nextInt(myString.length)];
tv.setText(q);
**// play sound that says the word in the string randomly generated**

} else {

**//repeat sound when incorrect button is pressed here**

}
}

}

Answer

I did a quiz feature with sounds for correct and incorrect answers. Here's how I did it.

Start by declaring references for the SoundPool and the IDs of the loaded sounds:

    private SoundPool mSoundPool;

    private int mSoundIdCorrect;

    private int mSoundIdIncorrect;

    private int mSoundIdStart;

    private int mSoundIdFinish;

Here are some useful static values:

    private static final int MAX_STREAMS = 2;

    private static final float VOLUME_LEFT = 1.0F;    // full volume

    private static final float VOLUME_RIGHT = 1.0F;   // full volume

    private int PLAYBACK_PRIORITY = 1;                // normal

    private int PLAYBACK_LOOP = 0;                    // no looping

    private float PLAYBACK_RATE = 1.0F;               // normal speed

The SoundPool constructor is now deprecated. If you are using Lollipop or greater, you should use the SoundPool.Builder.

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            AudioAttributes attrs = new AudioAttributes.Builder()
                    .setUsage(AudioAttributes.USAGE_GAME)
                    .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                    .build();
            mSoundPool = new SoundPool.Builder().setMaxStreams(MAX_STREAMS).setAudioAttributes(attrs).build();
         } else {
             mSoundPool = new SoundPool(MAX_STREAMS, AudioManager.STREAM_MUSIC, 0);
         }

Now on my app, I had a sound for Start Quiz. The start of quiz was when the quiz fragment view is created, but you can't play a sound until the sound has been loaded by the SoundPool. So in order to do something like this, you need an OnLoadCompleteListener. I am including this code in case you need to do something similar.

playSounds is a boolean that is set by the user's preference to play sounds.

restored is a boolean that is set when savedInstanceState is not null, meaning the fragment was recreated on a config change, for example.

          mSoundPool.setOnLoadCompleteListener(new OnLoadCompleteListener() {
              @Override
              public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
                  Log.d(TAG, "soundpool onLoadComplete, sampleId = " + sampleId + ", status = " + status);

                  if (sampleId == mSoundIdStart && playSounds && ! restored) {
                      mSoundPool.play(mSoundIdStart, VOLUME_LEFT, VOLUME_RIGHT, PLAYBACK_PRIORITY, PLAYBACK_LOOP, PLAYBACK_RATE);
                  }
              }
          });

You should always load sounds with SoundPool using a non-UI thread.

        // sound load should happen off the UI thread
        new Thread() {
            @Override
            public void run() {
                mSoundIdCorrect = mSoundPool.load(getActivity(), R.raw.quiz_correct, 1);
                mSoundIdIncorrect = mSoundPool.load(getActivity(), R.raw.quiz_incorrect, 1);
                mSoundIdFinish = mSoundPool.load(getActivity(), R.raw.quiz_finish, 1);
                mSoundIdStart = mSoundPool.load(getActivity(), R.raw.quiz_start, 1);
            }
        }.start();

IMPORTANT: Use .OGG format audio files as they are universally supported. Using .WAV or .MP3 files may not work on certain OS versions and/or devices.

When you play a sound, you should also do that off the UI thread:

        if (playSounds) {
            new Thread() {
                @Override
                public void run() {
                    mSoundPool.play(isCorrect ? mSoundIdCorrect : mSoundIdIncorrect, VOLUME_LEFT, VOLUME_RIGHT, PLAYBACK_PRIORITY, PLAYBACK_LOOP, PLAYBACK_RATE);
                }
            }.start();
        }

Don't forget to release the loaded sound assets when you are done.

    @Override
    public void onDestroy() {
        mSoundPool.release();
        mSoundPool = null;
        super.onDestroy();
    }