11

I need help with playing multiple audio tracks at the same time in Android.

I am supposed to play three audio tracks at the exact same time using Android.Media.MediaPlayer.

Yesterday I managed to make it work doing so :

MediaPlayer track1 = MediaPlayer.create(this, R.raw.track1);
MediaPlayer track2 = MediaPlayer.create(this, R.raw.track2);
MediaPlayer track3 = MediaPlayer.create(this, R.raw.track3);

As you can see, I have three distincts instances of MediaPlayer here.

Because I am asked to, I need to have these MediaPlayer being played in a background thread.

Here is the part where I start the MediaPlayers:

//let's suppose this code snippet is in a run() method inside a Thread (which is the case)
track1.start();
track2.start();
track3.start();

If I say yesterday, it's because the next day, it did not work as supposed to.

Indeed, starting a MediaPlayer seems to stop any previous playing MediaPlayer.

I tested in debugger mode : clearly, track2.start() stops track1's MediaPlayer and following the same pattern, track3.start() stops tack2's MediaPlayer.

So at the end, only track3 is being played, I cannot hear any of the previous tracks, whatever the volume settings are whereas I could cleary hear all of them before : it is supposed to create some sort an ambiance.

Of course, changing the order in which the tracks are started does not change anything : only the last track will be heard.

If I say "heard" it is because in debugger mode, checking the property MediaPlayer.isPlaying is returning true : all three players are saying they are playing but only one can be heard... !

Why the change ? Why did it work once to stop working afterwards ?

NOTE:

  • the audio files have all the same duration which is approximately about 15 minutes
  • the audio files were .mp3 files compressed into .acc files (from 320kbps to 144kbps)
  • I am working under Xamarin Studio in C# but for clarety purpouse I will stick to small Java code snippets.

EDIT 1

According to this Play two mp3 song at the same time my solution should be working, right ?

Community
  • 1
  • 1
Mackovich
  • 3,319
  • 6
  • 35
  • 73

5 Answers5

8

I achieved what you're looking for using a CyclicBarrier instance and an inner class implementation.

Example:

public enum MP_COMMAND {
    START,
    STOP,
    PAUSE
}

/**
 * Uses threads to execute synced commands for the current video media player and 
 * background music player in tandem.
 */
public void syncedCommand(MediaPlayer player1, MediaPlayer player2, MP_COMMAND command) {
    final CyclicBarrier commandBarrier = new CyclicBarrier(2);
    new Thread(new SyncedCommandService(commandBarrier, player1, command)).start();
    new Thread(new SyncedCommandService(commandBarrier, player2, command)).start();
}

/**
 * Inner class that starts a given media player synchronously
 * with other threads utilizing SyncedStartService
 */
private class SyncedCommandService implements Runnable {
    private final CyclicBarrier              mCommandBarrier;
    private       MediaPlayerTest.MP_COMMAND mCommand;
    private       MediaPlayer                mMediaPlayer;

    public SyncedCommandService(CyclicBarrier barrier, MediaPlayer player, MediaPlayerTest.MP_COMMAND command) {
        mCommandBarrier = barrier;
        mMediaPlayer = player;
        mCommand = command;
    }

    @Override public void run() {
        try {
            mCommandBarrier.await();
        } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
        }

        switch (mCommand) {
            case START:
                mMediaPlayer.start();
                break;

            case STOP:
                mMediaPlayer.stop();
                break;

            case PAUSE:
                mMediaPlayer.pause();
                break;

            default:
                break;
        }
    }
}

You'd utilize it like so:

syncedCommand(mCurrentVideoPlayer, mBackgroundMusic, MP_COMMAND.START);

If you had the requirement that it be usable for any number of media players, you could easily implement it - my requirements just call for two.

I realize this question is old, but this page is where I found myself while searching for a solution, so I hope this helps anyone stuck on this issue in the future.

Carter Hudson
  • 1,176
  • 1
  • 11
  • 23
  • Hi, how do you instance that class? – Billyjoker Dec 11 '17 at 20:24
  • @Billyjoker - I'm not sure what you mean; can you elaborate? The answer itself shows how to instantiate the `SyncedCommandService` runnable. – Carter Hudson Dec 11 '17 at 20:27
  • I mean when i instance the SyncedCommandService object. My issue is that i could not do it in the classic class instance way: SyncedCommandService syncedCommandService = new SyncedCommandService(); becasuse it has not defined an empty constructor, so, to get it work i had to add it in your class code this: public SyncedCommandService(){} – Billyjoker Dec 12 '17 at 11:43
  • Right, there is no default constructor in my example because it is specific to my use-case and was not required :] – Carter Hudson Mar 29 '18 at 18:11
2

Well, I believe I found what I could name "a temporary solution".

MediaPlayer track1 = MediaPlayer.create(this, R.raw.track1);
track1.start();
MediaPlayer track2 = MediaPlayer.create(this, R.raw.track2);
track2.start();
MediaPlayer track3 = MediaPlayer.create(this, R.raw.track3);
track3.start();

The problem with this is that the create() method creates a noticeable gap when the songs are being played.

So this is not it.

After a while, I tried the following :

MediaPlayer track1 = MediaPlayer.create(this, R.raw.track1);
track1.start();
track1.pause();
MediaPlayer track2 = MediaPlayer.create(this, R.raw.track2);
track2.start();
track2.pause();
MediaPlayer track3 = MediaPlayer.create(this, R.raw.track3);
track3.start();
track3.pause();

// resuming...
track1.start(); 
track2.start();
track3.start();

That whay, the songs are more synchronous. I can still hear a very small gap but it is way better. I found this solution quite strange as well as ugly.

Unless someone has another idea, I will stick with that solution.

Mackovich
  • 3,319
  • 6
  • 35
  • 73
  • 1
    There is no `play()` method - do you mean `start()`? – Karu Dec 23 '15 at 05:58
  • 1
    That is correct indeed. I'll update my answer immediately. – Mackovich Dec 23 '15 at 11:29
  • 1
    Even this solution isn't working for me - I can still only hear one of the tracks. Interestingly, it's not always the same one - it seems random. – Karu Dec 24 '15 at 10:21
  • @karu well recently I was working on another projet to test out Bluetooth audio streaming via the A2DP protocol with up to five tracks at the same time. Curiously, I did not stumble on this issue! I would also add that my original question took place in a C# Xamarin.Android project while my current project is pure Java Android ;) – Mackovich Dec 24 '15 at 20:51
  • 1
    this problems depends from device. i faced it on some devices while others working fine. – Andrey Nikishaev Sep 08 '16 at 08:37
1

As Andrey Nikishaev already wrote in a comment, it seems like some devices have problems using multiple MediaPlayers at the same time. While only one was playing on my Nexus 5, it worked on my Moto G4 (but still having short breaks during playback). Both running with Android 6.0.1.

I suggest to use the ExoPlayer for this use-case instead. It worked fine for me, at least on my two devices.

Community
  • 1
  • 1
Alex
  • 141
  • 1
  • 10
0

I converted my mp3 in ogg file audio (using Audacity) and I am been able to use 2 Mediaplayer istances and listen the audio mixed.

I didn't try is it is possible for more than 3.

Boris Karloff
  • 1,190
  • 12
  • 20
0

hi try below code it will work it the song wont stop you just need to select the song from spinner and click on start button, then select next song from spinner and click start it will work.

public class MainActivity extends AppCompatActivity {

    Spinner sp;
    Button bstart;
    MediaPlayer mp;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        sp=(Spinner)findViewById(R.id.sp);
        String [] mys={"demo","democheap","demorehana"};
        ArrayAdapter<String> data=new ArrayAdapter<String>(this,R.layout.support_simple_spinner_dropdown_item,mys);
        sp.setAdapter(data);

    }

    public void code(View v)
    {
        if(sp.getSelectedItem().toString().equals("demo"))
        {
            mp=MediaPlayer.create(this,R.raw.demo);
        }
        else if(sp.getSelectedItem().toString().equals("democheap"))
        {
            mp=MediaPlayer.create(this,R.raw.democheap);
        }
        else if(sp.getSelectedItem().toString().equals("demorehana"))
        {
            mp=MediaPlayer.create(this,R.raw.demorehana);
        }

        if(v.getId()==R.id.bstart)
        {
            mp.start();
        }
    }
}
Dan Lowe
  • 51,713
  • 20
  • 123
  • 112