10
votes

Some special circumstances force me to do that perverted thing.

Is it possible to play 2 different audio streams on different channels. Imagine a headset and I need to play 1st song at left speaker and 2nd song on right speaker simultaneously.

After some research I found that it is possible to play at some single channel. Its possible even to close one of them. But I didn't find any information how to play 2 audio streams simultaneously.

Is it even possible? And how? Some code examples and links appreciated!


Research results.

1)AudioTrack able to play on different channels

// only play sound on left
for(int i = 0; i < count; i += 2){
    short sample = (short)(Math.sin(2 * Math.PI * i / (44100.0 / freqHz)) * 0x7FFF);
    samples[i + 0] = sample;
    samples[i + 1] = 0;
}
// only play sound on right
for(int i = 0; i < count; i += 2){
    short sample = (short)(Math.sin(2 * Math.PI * i / (44100.0 / freqHz)) * 0x7FFF);
    samples[i + 0] = 0;
    samples[i + 1] = sample;
}

2) SoundPool able to set volume for left and right channels separately. So technically it's possible to start 2 streams and set for one 0 left volume and 100 for right volume and vice versa for 2nd stream. Right?

setVolume(int streamID, float leftVolume, float rightVolume)

sample:

    SoundPool pool = new SoundPool(1, AudioManager.STREAM_NOTIFICATION, 0);
pool.play(
        pool.load(this, R.raw.my_sound_effect, 1), // The sound effect to play
        0, // The volume for the left channel (0.0 - 1.0)
        1.0f, // The volume for the right channel (0.0 - 1.0)
        0, // Default priority 
        0, // Do not loop
        1.0f); // Playback rate (1.0f == normal)

Perhabs there's such solution using MediaPlayer so that would be much preferable!


SOLUTION: Seems like the easiest solution is to use SoundPool. The way I tested it.

//SDK Version
public CustomSoundPool() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        AudioAttributes attributes = new AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_GAME)
                .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                .build();

        soundPool = new SoundPool.Builder()
                .setAudioAttributes(attributes)
                .build();
    } else {
        soundPool = new SoundPool(10, AudioManager.STREAM_MUSIC, 0);
    }
    soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
        @Override
        public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
            soundPool.play(sampleId, 1.0f, 0, 0, 0, 1.0f); //left channel
            //soundPool.play(sampleId, 0, 1.0f, 0, 0, 1.0f); //right channel
        }
    });
}

public void playSound(String path) {
    if (soundPool != null) {
        soundPool.load(path, 1);
    }
}

public void release(){
    if (soundPool != null) {
        soundPool.release();
    }
}

Although there's lack of features like MediaPlayer.OnCompletionListener(). Now sure how to implement this but anyway.. Problem solved.

2

2 Answers

3
votes

Either approaches 1) 2) should work as you said.

Regarding approach 1), if you want to play different stream simultaneously you could write both channels with different data in the same loop, without using two loops. In order to set the volume per channel, you can use the AudioTrack's method setStereoVolume(float leftGain, float rightGain).

Since MediaPlayer is an high-level API it doesn't offer this level of specificity; a workaround could be to save the audio data to a file (muting the left/right channel) and play it as a regular media element using MediaPlayer.


UPDATE:

save the audio data to a file (muting the left/right channel)

To do this, assuming that you have your audio data in the AudioTrack-compatible format (PCM), you just need to process the data as you said (muting one channel by resetting part of the array) and then save the array/buffer to a file as WAV/PCM. Take a look here for more detail and code about how to save WAV files.

1
votes

I used 2 instances of mediaplayer and tried to play two seperate music tracks on earphones and it worked like a charm. it played one track on the left and an another on the right.

MediaPlayer mediaPlayer1 = MediaPlayer.create(this, R.raw.lenka);
MediaPlayer mediaPlayer2 = MediaPlayer.create(this, R.raw.faded);
final int sessionIdA = mediaPlayer1.getAudioSessionId();
final int sessionIdB = mediaPlayer2.getAudioSessionId();
mediaPlayer1.setLooping(true);
mediaPlayer2.setLooping(true);
mediaPlayer1.setVolume(0, 1);
mediaPlayer2.setVolume(1, 0);
mediaPlayer1.start();
mediaPlayer2.start();