Android AudioTrack | Playing Audio in Android

Play audio in Android using the AudioTrack

There are multiple ways to play audio on Android. One can use MediaPlayer,
Native API (oboe or opensl) and AudioTrack. This post shows you how to use
AudioTrack to play music.
To play audio from a media file we need MediaExtractor to extract the
audio track and an AudioDecoder to decode encoded samples. I'll use
Kotlin and Coroutines, so it will be good if you know
Kotlin Coroutines very well.

Contents

Android Audio Decoder | MediaCodec

I have created an audioDecoding that accepts some params inputChannel,
mediaFormat, and onMediaFormat lambda. onMediaFormat is a callback that
provides the output media format of the decoder that could be used to describe
the format of the decoded audio data, such as sample rate, channel config
(number of channels), PCM encoding, etc.

After configuring the decoder in synchronous mode, we start it and launch a
coroutine which Consumes encoded data. Each Encoded data is wrapped in a
BufferWrapper class with a buffer info object. Further in the collect block we
deque the input buffer from the decoder with timeout 'timeout' and write the
encoded sample to the buffer and queue the buffer to the decoder.

Further, we deque output buffer with val outBuffIx =
decoder.dequeueOutputBuffer(info, timeout).

If the outBuffIx >= 0, then we can get the output buffer by calling
decoder.getOutputBuffer(outBuffIx).

else we can check for the output format changed and invoke the onMediaFormat
callback. Decoder's output format becomes available as the first encoded
sample is provided to it.

When we have the decoder output buffer we'll copy the decoded sample into a
ByteArray. We wrapped bytes and buffer info in a bufferWrapper object and send
it to outputChannel.

BufferInfo has presentation time stamps and info about the buffer such as size
and offset. It is also important to stop and release the decoder when the
decoder inputChannel has closed that is why we are listening to the job
completion. When the job completes we stop and release the decoder.

Playing Decoded Audio | Android AudioTrack

AudioTrack class from the Android media API can be used to play raw PCM music.
Let's create an AudioPlayer class and define an audioPlayback function.
 

audioPlayback function required the inputChannel and mediaFormat describing
the raw audio data settings. First, we extract all the required attributes
from the mediaFormat to create an object of AudioTrack. Then we retrieve the
minimum buffer size required for audiotrack to have. We initialize the object
with stream type set to STREAM_MUSIC and mode to AudioTrack.MODE_STREAM.
Lastly, we start the audioTrack by calling
audioTrack.play() and launch a coroutine that writes the raw data to
audioTrack until the inputChannel is open.

PlayFrom file | MediaExtractor

MediaExtractor class is used to extract the audio video track and mediaFormat
of each track from a media file. Let's again create a function that extracts
the audio from a given file. 

In the code snippet above, firstly, we create an instance of MediaExtractor
and setDataSource to the path described by the given file. then we start a for
loop for each track and select the track that's mime starts with "audio". Note
that we are setting the current track using mediaExtractor.selectTrack().

We also need the audio format, we get the current audio format by calling
mediaExtractor.getTrackFormat(). We also create another function
'playEncodedSample' which internally further starts the decoder and provided
it inputChannel also this function invokes the audioPlayback and passes down
the decoder's output. 

Complete Android code

The Complete Android code is hosted on
GitHub
Share this Post

Leave a Reply

Your email address will not be published. Required fields are marked *