1

I want to play multiple speakers at the same time.

In my apllication I'm getting audio from network, decode from C#, decode by opus and then want to play bytes. But now I can play only one speaker.

My AudioPLayer.class:

public class Player {
    private static final String TAG = Player.class.getName();
    private AudioTrack audioTrack;
    private boolean isWorking;

    public Player() {
       try  {
           audioTrack = new AudioTrack(
               AudioManager.STREAM_MUSIC,
               AudioConsts.SAMPLERATE,
               AudioConsts.NUM_CHANNELS == 1 ? AudioConsts.CHANNEL_OUT_MONO : AudioConsts.CHANNEL_OUT_STEREO,
               AudioConsts.ENCODING_PCM_16BIT,
               AudioConsts.GetPlayerBufferSize(),
               AudioTrack.MODE_STREAM);
       } catch (Exception e){
           Log.e(TAG, e.toString());
       }
    }

    public void play() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                isWorking = true;

                try {
                    audioTrack.play();
                } catch (Exception e) {
                    Log.d(e.toString(), "AUDIO EXCEPTION");
                    return;
                }

                int bufferSize = AudioConsts.GetPlayerBufferSize();
                while (isWorking){
                    int cursor = audioTrack.getPlaybackHeadPosition();
                    if (cursor > bufferSize){
                        cursor %= bufferSize;

                        audioTrack.flush();
                        audioTrack.setPlaybackHeadPosition(cursor);
                    }
                }
            }
        }).start();
    }

    public void stopReading(){
        if (!isWorking)
            return;

        audioTrack.release();
        isWorking = false;
    }

    public void appendForPlayback(byte[] audioMessage, int size) {
        if (size != 0){
            int writen = audioTrack.write(audioMessage, 0, size);
            if (writen != size) {
                //audioTrack.release();
                Log.d(TAG, "WTF");
            }
        }
    }
}

Also attach my AudioPlayer's initialization:

 @Override
public void onCreate() {
    super.onCreate();

    ...

    player = new Player();
    player.play();

    IntentFilter filter = new IntentFilter();
    filter.addAction(ON_UNITY_AUDIO_MESSAGE_RECEIVED);
    filter.addAction(AudioConsts.START_RECORDER);
    filter.addAction(AudioConsts.STOP_RECORDER);

    broadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(ON_UNITY_AUDIO_MESSAGE_RECEIVED)) {
                byte[] decryptedBytes = intent.getByteArrayExtra(UNITY_AUDIO_MESSAGE);

                onUnityAudioReceivedFromNetwork(decryptedBytes);
            } else if (intent.getAction().equals(AudioConsts.START_RECORDER)) {
                incrementSessionCount();
                recorder.startRecording();
            } else if (intent.getAction().equals(AudioConsts.STOP_RECORDER)) {
                recorder.stopRecording();
            }
        }
    };

    registerReceiver(broadcastReceiver, filter);

    decodeMsg = new byte[AudioConsts.FRAME_SIZE * AudioConsts.ENCODING_PCM_16BIT];
    opusDecoder = new OpusDecoder();
    opusDecoder.init(AudioConsts.SAMPLERATE, AudioConsts.NUM_CHANNELS);
}

...

private void onUnityAudioReceivedFromNetwork(byte[] decryptedBytes) {
    UnityAudioMessage audioMessage = UnityAudioMessage.fromBytesSharp(decryptedBytes);
    if (audioMessage != null) {
        try {
            opusDecoder.decode(audioMessage.unityAudioMessage, decodeMsg, AudioConsts.FRAME_SIZE);
        } catch (OpusError e) {
            e.printStackTrace();
            return;
        }

        player.appendForPlayback(decodeMsg, decodeMsg.length);
    }
}

...

Can I release simultaneos playback of multiple speakers?

Also I tried release it with HaspMap of my players. But it works only like 1 audio track.

Phantômaxx
  • 37,901
  • 21
  • 84
  • 115
  • Possible duplicate of [Playing 2 musics through 2 different sound cards at same time](https://stackoverflow.com/questions/52138934/playing-2-musics-through-2-different-sound-cards-at-same-time) – Martin Zeitler Oct 29 '18 at 07:38
  • So in my implemetation I use AudioTrack, not MediaPlayer. I played my audio in real-time – Vlad Doroshko Oct 29 '18 at 07:51
  • `STREAM_MUSIC` had been recently deprecated. while I don't think one has any chance to define multiple outputs there (one cannot even select a single output device) - except possibly through some kind of virtual audio cable, in order to use it as audio source for the `MediaPlayer`. – Martin Zeitler Oct 30 '18 at 01:51
  • For audio, there's a hard limit of 32 active AudioTrack objects per _device_ (not per app: you need to share those 32 with rest of the system), and AudioTrack is used internally beneath SoundPool, ToneGenerator, MediaPlayer, native audio based on OpenSL ES, etc. But the actual AudioTrack limit is < 32; it depends more on soft factors such as memory, CPU load, etc. I receive audio packets over the network and want to play them simultaneosly, and there are several speakers. – Vlad Doroshko Oct 30 '18 at 05:56
  • and where those audio tracks are routed to? the track itself does not have any such properties. unless one can connect two outputs to a mixer device - and maybe even multiplex several inputs towards a single stereo channel, it might always be the default audio device where the output is being routed to. any open-source audio mixer software might provide clues. there even are low-latency real-time audio drivers, but (afaik) no software which uses them. – Martin Zeitler Oct 30 '18 at 06:37

1 Answers1

0

I tried a lot of things, but my solution use AsyncTask.class

Attach Player.class

public class Player {
private static final String TAG = Player.class.getName();
private AudioTrack audioTrack;
private boolean isWorking;

public Player() {
    try {
        audioTrack = new AudioTrack(
                new AudioAttributes.Builder()
                        .setUsage(AudioAttributes.USAGE_MEDIA)
                        .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
                        .setLegacyStreamType(AudioManager.STREAM_MUSIC)
                        .build(),

                new AudioFormat.Builder()
                        .setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
                        .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
                        .setSampleRate(AudioConsts.SAMPLERATE)
                        .build(),

                AudioConsts.GetPlayerBufferSize(),
                AudioTrack.MODE_STREAM,
                AudioManager.AUDIO_SESSION_ID_GENERATE);

    } catch (Exception e) {
        Log.e(TAG, e.toString());
    }
}

public void play() {
    audioTrack.play();
}

public void stopReading() {
    if (!isWorking)
        return;

    audioTrack.release();
    isWorking = false;
}

public void appendForPlayback(byte[] audioMessage, int size) {
    new Executor().doInBackground(audioMessage);
}

private class Executor extends AsyncTask<byte[], Void, Void> {
    @Override
    protected Void doInBackground(byte[]... bytes) {
        for (byte[] audioMessage : bytes) {
            if (audioMessage.length != 0) {
                int writen = audioTrack.write(audioMessage, 0, audioMessage.length);
                if (writen != audioMessage.length) {
                    Log.d(TAG, "WTF");
                }
            }

        }

        return null;
    }
}}