No history yet

Oboe Integration

Integrating Oboe into Your Project

Oboe is a C++ library that simplifies building high-performance audio apps on Android. It acts as a wrapper around Android's native audio APIs, like AAudio and OpenSL ES, choosing the best one for the device. This lets you write simple, reliable code that achieves the lowest possible audio latency without worrying about the specifics of different Android versions.

Oboe is a C++ library which makes it easy to build high-performance audio apps on Android.

Environment Setup

Before you can use Oboe, your development environment needs to be configured for native C++ development. This means ensuring you have the Android Native Development Kit (NDK) and CMake installed through Android Studio's SDK Manager.

Once that's set up, you can add Oboe to your project. The easiest way is to use Google's prefab package through Gradle. Add the following to your app's build.gradle file:

dependencies {
    // ... other dependencies
    implementation 'com.google.oboe:oboe:1.8.1'
}

Next, you need to tell your native build system, usually CMake, how to find and link against Oboe. You'll do this in your CMakeLists.txt file.

# Add Oboe from the prefab package
find_package(oboe REQUIRED CONFIG)

# Link Oboe to your native library
target_link_libraries(
    your-native-lib-name
    oboe::oboe
)

This configuration automatically handles the complexities of locating the Oboe library and linking it with your app's native code, making the setup process straightforward.

Initializing an Audio Stream

With Oboe added to your project, you can initialize an audio stream. This is done using the AudioStreamBuilder. This builder pattern lets you specify the characteristics of the stream you need, such as the audio direction (input or output), sample rate, and channel count.

At its core, stream management involves three main components:

  • AudioStreamBuilder: Configures and creates your stream.
  • AudioStream: The active object representing the audio stream.
  • AudioStreamCallback: A class you implement to receive or provide audio data.
#include <oboe/Oboe.h>

class MyAudioEngine : public oboe::AudioStreamCallback {
public:
    void start() {
        oboe::AudioStreamBuilder builder;
        builder.setDirection(oboe::Direction::Output)
               ->setPerformanceMode(oboe::PerformanceMode::LowLatency)
               ->setSharingMode(oboe::SharingMode::Exclusive)
               ->setFormat(oboe::AudioFormat::Float)
               ->setCallback(this)
               ->openStream(&stream);
        
        stream->requestStart();
    }

private:
    oboe::AudioStream *stream;

    // Callback methods will be defined here...
    oboe::DataCallbackResult onAudioReady(
        oboe::AudioStream *audioStream, 
        void *audioData, 
        int32_t numFrames) override;
};

In this example, we create a builder for a low-latency output stream. We set our current class (this) as the callback handler for audio data and then call openStream to create the stream. Finally, requestStart() activates it.

Handling Audio Data

The heart of your audio engine is the AudioStreamCallback. Oboe invokes your callback's onAudioReady method when it needs new audio data to play. Your job is to fill the buffer provided by Oboe with your audio samples.

The onAudioReady function gives you a pointer to a buffer (audioData) and the number of frames it needs (numFrames). You must fill this buffer completely. For stereo audio, the samples are interleaved, meaning one sample for the left channel, then one for the right, and so on.

// This is a method within our MyAudioEngine class.
oboe::DataCallbackResult MyAudioEngine::onAudioReady(
    oboe::AudioStream *audioStream, 
    void *audioData, 
    int32_t numFrames) {

    // We are using floating point samples.
    float *outputBuffer = static_cast<float*>(audioData);

    // For this example, just fill the buffer with silence.
    // A real app would generate sound here.
    for (int i = 0; i < numFrames * stream->getChannelCount(); ++i) {
        outputBuffer[i] = 0.0f;
    }

    return oboe::DataCallbackResult::Continue;
}

Returning oboe::DataCallbackResult::Continue tells Oboe to keep the stream running and to call you back when it needs more data. If you were finished, you could return oboe::DataCallbackResult::Stop.

It's also crucial to manage the stream's lifecycle with your app. You should start the stream when your app is in the foreground (e.g., onResume) and stop or close it when it goes to the background (onPause). Closing the stream releases the audio device for other apps to use.

void stop() {
    if (stream) {
        stream->stop();
        stream->close();
        stream = nullptr;
    }
}

With these pieces in place—setup, initialization, and a data callback—you have a basic, high-performance audio stream running in your Android application.

Quiz Questions 1/5

What is the primary purpose of the Oboe library?

Quiz Questions 2/5

Which two native Android APIs does Oboe abstract away, automatically choosing the best one for the device?