Custom Audio Driver (Android)

Overview

The OpenTok Android SDK lets you set up a custom audio driver for publishers and subscribers. You
can use a custom audio driver to customize the audio sent to a publisher's stream. You can also
customize the playback of subscribed streams' audio.

Step 1: Adding a custom audio driver

The code for this section is in the audio-driver.step-1 branch of the learning-opentok-android
repo, so if you haven't already, you'll need to clone the repo into a local directory — this can be done using the command line:

git clone https://github.com/opentok/learning-opentok-android.git

Then check out the branch:

git checkout audio-driver.step-1

Open the project in Android Studio to follow along.

This branch shows
you how to implement a custom audio driver and use a simple audio capturer for audio used by
the stream published by the app.

This sample application uses the custom audio driver to publish white noise (a random audio signal)
to its audio stream. It also uses the custom audio driver to capture the audio from subscribed
streams and save it to a file.

Setting up the audio device and the audio bus

In using a custom audio driver, you define a custom audio driver and an audio bus to be
used by the app.

The BasicAudioDevice class defines a basic audio device interface to be used by the app.
It extends the BaseAudioDevice class, defined by the OpenTok Android SDK. To use a custom
audio driver, call the AudioDeviceManager.setAudioDevice(device) method. This sample sets
the audio device to an instance of the BasicAudioDevice class:

AudioDeviceManager.setAudioDevice(new BasicAudioDevice(this));

Use the AudioSettings class, defined in the OpenTok Android SDK, to define the audio format used
by the custom audio driver. The BasicAudioDevice() constructor instantiates two AudioSettings
instances -- one for the custom audio capturer and one for the custom audio renderer. It sets
the sample rate and number of channels for each:

The constructor also sets up some local properties that report whether the device is capturing
or rendering. It also sets a Handler instance to process the mCapturer Runnable object.

The BasicAudioDevice getAudioBus() method gets the AudioBus instance that this audio device uses,
defined by the BasicAudioDevice.AudioBus class. Use the AudioBus instance to send and receive audio
samples to and from a session. The publisher will access the
AudioBus object to obtain the audio samples. And subscribers will send audio samples (from
subscribed streams) to the AudioBus object.

Capturing audio to be used by a publisher

The BaseAudioDevice startCapturer() method is called when the audio device should start capturing
audio to be published. The BasicAudioDevice implementation of this method starts the mCapturer
thread to be run in the queue after 1 second:

The mCapturer thread produces a buffer containing samples of random data (white noise). It then
calls the writeCaptureData(data, numberOfSamples) method of the AudioBus object, which sends the
samples to the audio bus. The publisher in the application uses the samples sent to the audio bus to
transmit as audio in the published stream. Then if a capture is still in progress (if
the app is publishing), the mCapturer thread is run again after another second:

See Step 2 below for a simple implementation of a custom
audio renderer.

Other notes on the audio driver API

The AudioDevice class includes other methods that are implemented by the BasicAudioDevice class.
However, this sample does not do anything interesting in these methods, so they are not included
in this discussion.

Step 2: Adding a custom audio renderer

This branch shows you how to implement simple audio renderer for subscribed streams' audio.

The BasicAudioDevice() constructor method sets up a file to save the incoming audio to a file.
This is done simply to illustrate a use of the custom audio driver's audio renderer.
The app requires the following permissions, defined in the AndroidManifest.xml file:

The BaseAudioDevice initRenderer() method is called when the app initializes the audio renderer.
The BasicAudioDevice implementation of this method instantiates a new File object, to which
the the app will write audio data:

The BaseAudioDevice.startRendering() method is called when the audio device should start rendering
(playing back) audio from subscribed streams. The BasicAudioDevice implementation of this method
starts the mCapturer thread to be run in the queue after 1 second:

The mRenderer thread gets 1 second worth of audio from the audio bus by calling the
readRenderData(buffer, numberOfSamples) method of the AudioBus object. It then writes the audio
data to the file (for sample purposes). And, if the audio device is still being used to render audio
samples, it sets a timer to run the mRendererHandler thread again after 0.1 seconds:

This example is intentionally simple for instructional purposes -- it simply writes the audio data
to a file. In a more practical use of a custom audio driver, you could use the custom audio driver
to play back audio to a Bluetooth device or to process audio before playing it back.

Congratulations! You've finished the Custom Audio Driver Tutorial for Android.You can continue to play with and adjust the code you've developed here, or check out the Next Steps below.

Next steps

When you're finished here, continue building and enhancing your OpenTok application with these helpful resources: