xref: /aosp_15_r20/external/oboe/docs/OpenSLESMigration.md (revision 05767d913155b055644481607e6fa1e35e2fe72c)
1*05767d91SRobert WuOpenSLES Migration Guide
2*05767d91SRobert Wu===
3*05767d91SRobert Wu
4*05767d91SRobert Wu# Introduction
5*05767d91SRobert Wu
6*05767d91SRobert WuThis guide will show you how to migrate your code from [OpenSL ES for Android](https://developer.android.com/ndk/guides/audio/opensl/opensl-for-android) (just OpenSL from now on) to Oboe.
7*05767d91SRobert Wu
8*05767d91SRobert WuTo familiarise yourself with Oboe, please read the [Getting Started guide](https://github.com/google/oboe/blob/main/docs/GettingStarted.md) and ensure that Oboe has been added as a dependency in your project.
9*05767d91SRobert Wu
10*05767d91SRobert Wu
11*05767d91SRobert Wu# Concepts
12*05767d91SRobert Wu
13*05767d91SRobert WuAt a high level, OpenSL and Oboe have some similarities. They both create objects which communicate with an audio device capable of playing or recording audio samples. They also use a callback mechanism to read data from or write data to that audio device.
14*05767d91SRobert Wu
15*05767d91SRobert WuThis is where the similarities end.
16*05767d91SRobert Wu
17*05767d91SRobert WuOboe has been designed to be a simpler, easier to use API than OpenSL. It aims to reduce the amount of boilerplate code and guesswork associated with recording and playing audio.
18*05767d91SRobert Wu
19*05767d91SRobert Wu
20*05767d91SRobert Wu# Key differences
21*05767d91SRobert Wu
22*05767d91SRobert Wu
23*05767d91SRobert Wu## Object mappings
24*05767d91SRobert Wu
25*05767d91SRobert WuOpenSL uses an audio engine object, created using `slCreateEngine`, to create other objects. Oboe's equivalent object is `AudioStreamBuilder`, although it will only create an `AudioStream`.
26*05767d91SRobert Wu
27*05767d91SRobert WuOpenSL uses audio player and audio recorder objects to communicate with audio devices. In Oboe an `AudioStream` is used.
28*05767d91SRobert Wu
29*05767d91SRobert WuIn OpenSL the audio callback mechanism is a user-defined function which is called each time a buffer is enqueued. In Oboe you construct an `AudioStreamDataCallback` object, and its `onAudioReady` method is called each time audio data is ready to be read or written.
30*05767d91SRobert Wu
31*05767d91SRobert WuHere's a table which summarizes the object mappings:
32*05767d91SRobert Wu
33*05767d91SRobert Wu
34*05767d91SRobert Wu<table>
35*05767d91SRobert Wu  <tr>
36*05767d91SRobert Wu   <td><strong>OpenSL</strong>
37*05767d91SRobert Wu   </td>
38*05767d91SRobert Wu   <td><strong>Oboe </strong>(all classes are in the <code>oboe</code> namespace)
39*05767d91SRobert Wu   </td>
40*05767d91SRobert Wu  </tr>
41*05767d91SRobert Wu  <tr>
42*05767d91SRobert Wu   <td>Audio engine (an <code>SLObjectItf</code>)
43*05767d91SRobert Wu   </td>
44*05767d91SRobert Wu   <td><code>AudioStreamBuilder</code>
45*05767d91SRobert Wu   </td>
46*05767d91SRobert Wu  </tr>
47*05767d91SRobert Wu  <tr>
48*05767d91SRobert Wu   <td>Audio player
49*05767d91SRobert Wu   </td>
50*05767d91SRobert Wu   <td><code>AudioStream</code> configured for output
51*05767d91SRobert Wu   </td>
52*05767d91SRobert Wu  </tr>
53*05767d91SRobert Wu  <tr>
54*05767d91SRobert Wu   <td>Audio recorder
55*05767d91SRobert Wu   </td>
56*05767d91SRobert Wu   <td><code>AudioStream</code> configured for input
57*05767d91SRobert Wu   </td>
58*05767d91SRobert Wu  </tr>
59*05767d91SRobert Wu  <tr>
60*05767d91SRobert Wu   <td>Callback function
61*05767d91SRobert Wu   </td>
62*05767d91SRobert Wu   <td><code>AudioStreamDataCallback::onAudioReady</code>
63*05767d91SRobert Wu   </td>
64*05767d91SRobert Wu  </tr>
65*05767d91SRobert Wu</table>
66*05767d91SRobert Wu
67*05767d91SRobert Wu
68*05767d91SRobert Wu
69*05767d91SRobert Wu## Buffers and callbacks
70*05767d91SRobert Wu
71*05767d91SRobert WuIn OpenSL your app must create and manage a queue of buffers. Each time a buffer is dequeued, the callback function is called and your app must enqueue a new buffer.
72*05767d91SRobert Wu
73*05767d91SRobert WuIn Oboe, rather than owning and enqueuing buffers, you are given direct access to the `AudioStream`'s buffer through the `audioData` parameter of `onAudioReady`.
74*05767d91SRobert Wu
75*05767d91SRobert WuThis is a container array which you can read audio data from when recording, or write data into when playing. The `numFrames` parameter tells you how many frames to read/write. Here's the method signature of `onAudioReady`:
76*05767d91SRobert Wu
77*05767d91SRobert Wu
78*05767d91SRobert Wu```
79*05767d91SRobert WuDataCallbackResult onAudioReady(
80*05767d91SRobert Wu    AudioStream *oboeStream,
81*05767d91SRobert Wu    void *audioData,
82*05767d91SRobert Wu    int32_t numFrames
83*05767d91SRobert Wu)
84*05767d91SRobert Wu```
85*05767d91SRobert Wu
86*05767d91SRobert Wu
87*05767d91SRobert WuYou supply your implementation of `onAudioReady` when building the audio stream by constructing an `AudioStreamDataCallback` object. [Here's an example.](https://github.com/google/oboe/blob/main/docs/GettingStarted.md#creating-an-audio-stream)
88*05767d91SRobert Wu
89*05767d91SRobert Wu
90*05767d91SRobert Wu### Buffer sizes
91*05767d91SRobert Wu
92*05767d91SRobert WuIn OpenSL you cannot specify the size of the internal buffers of the audio player/recorder because your app is supplying them so they can have arbitrary size. You can only specify the _number of buffers_ through the `SLDataLocator_AndroidSimpleBufferQueue.numBuffers` field.
93*05767d91SRobert Wu
94*05767d91SRobert WuBy contrast, Oboe will use the information it has about the current audio device to configure its buffer size. It will determine the optimal number of audio frames which should be read/written in a single callback. This is known as a _burst_, and usually represents the minimum possible buffer size. Typical values are 96, 128, 192 and 240 frames.
95*05767d91SRobert Wu
96*05767d91SRobert WuAn audio stream's burst size, given by `AudioStream::getFramesPerBurst()`, is important because it is used when configuring the buffer size. Here's an example which uses two bursts for the buffer size, which usually represents a good tradeoff between latency and glitch protection:
97*05767d91SRobert Wu
98*05767d91SRobert Wu
99*05767d91SRobert Wu```
100*05767d91SRobert WuaudioStream.setBufferSizeInFrames(audioStream.getFramesPerBurst() * 2);
101*05767d91SRobert Wu```
102*05767d91SRobert Wu
103*05767d91SRobert Wu
104*05767d91SRobert Wu**Note:** because Oboe uses OpenSL under-the-hood on older devices which does not provide the same information about audio devices, it still needs to know [sensible default values for the burst to be used with OpenSL](https://github.com/google/oboe/blob/main/docs/GettingStarted.md#obtaining-optimal-latency).
105*05767d91SRobert Wu
106*05767d91SRobert Wu
107*05767d91SRobert Wu## Audio stream properties
108*05767d91SRobert Wu
109*05767d91SRobert WuIn OpenSL you must explicitly specify various properties, including the sample rate and audio format, when opening an audio player or audio recorder.
110*05767d91SRobert Wu
111*05767d91SRobert WuIn Oboe, you do not need to specify any properties to open a stream. For example, this will open a valid output `AudioStream` with sensible default values.
112*05767d91SRobert Wu
113*05767d91SRobert Wu
114*05767d91SRobert Wu```
115*05767d91SRobert WuAudioStreamBuilder builder;
116*05767d91SRobert Wubuilder.openStream(myStream);
117*05767d91SRobert Wu```
118*05767d91SRobert Wu
119*05767d91SRobert Wu
120*05767d91SRobert WuHowever, you may want to specify some properties. These are set using the `AudioStreamBuilder` ([example](https://github.com/google/oboe/blob/main/docs/FullGuide.md#set-the-audio-stream-configuration-using-an-audiostreambuilder)).
121*05767d91SRobert Wu
122*05767d91SRobert Wu
123*05767d91SRobert Wu## Stream disconnection
124*05767d91SRobert Wu
125*05767d91SRobert WuOpenSL has no mechanism, other than stopping callbacks, to indicate that an audio device has been disconnected - for example, when headphones are unplugged.
126*05767d91SRobert Wu
127*05767d91SRobert WuIn Oboe, you can be notified of stream disconnection by overriding one of the `onError` methods in `AudioStreamErrorCallback`. This allows you to clean up any resources associated with the audio stream and create a new stream with optimal properties for the current audio device ([more info](https://github.com/google/oboe/blob/main/docs/FullGuide.md#disconnected-audio-stream)).
128*05767d91SRobert Wu
129*05767d91SRobert Wu
130*05767d91SRobert Wu# Unsupported features
131*05767d91SRobert Wu
132*05767d91SRobert Wu
133*05767d91SRobert Wu## Formats
134*05767d91SRobert Wu
135*05767d91SRobert WuOboe audio streams only accept [PCM](https://en.wikipedia.org/wiki/Pulse-code_modulation) data in float or signed 16-bit ints. Additional formats including 8-bit unsigned, 24-bit packed, 8.24 and 32-bit are not supported.
136*05767d91SRobert Wu
137*05767d91SRobert WuCompressed audio, such as MP3, is not supported for a number of reasons but chiefly:
138*05767d91SRobert Wu
139*05767d91SRobert Wu
140*05767d91SRobert Wu
141*05767d91SRobert Wu*   The OpenSL ES implementation has performance and reliability issues.
142*05767d91SRobert Wu*   It keeps the Oboe API and the underlying implementation simple.
143*05767d91SRobert Wu
144*05767d91SRobert WuExtraction and decoding can be done either through the NDK [Media APIs](https://developer.android.com/ndk/reference/group/media) or by using a third party library like [FFmpeg](https://ffmpeg.org/). An example of both these approaches can be seen in the [RhythmGame sample](https://github.com/google/oboe/tree/main/samples/RhythmGame).
145*05767d91SRobert Wu
146*05767d91SRobert Wu
147*05767d91SRobert Wu## Miscellaneous features
148*05767d91SRobert Wu
149*05767d91SRobert WuOboe does **not** support the following features:
150*05767d91SRobert Wu
151*05767d91SRobert Wu
152*05767d91SRobert Wu
153*05767d91SRobert Wu*   Channel masks - only [indexed channel masks](https://developer.android.com/reference/kotlin/android/media/AudioFormat#channel-index-masks) are supported.
154*05767d91SRobert Wu*   Playing audio content from a file pathname or [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier).
155*05767d91SRobert Wu*   Notification callbacks for position updates.
156*05767d91SRobert Wu*   Platform output effects on API 27 and below. [They are supported from API 28 and above.](https://github.com/google/oboe/wiki/TechNote_Effects)
157*05767d91SRobert Wu
158*05767d91SRobert Wu
159*05767d91SRobert Wu# Summary
160*05767d91SRobert Wu
161*05767d91SRobert Wu
162*05767d91SRobert Wu
163*05767d91SRobert Wu*   Replace your audio player or recorder with an `AudioStream` created using an `AudioStreamBuilder`.
164*05767d91SRobert Wu*   Use your value for `numBuffers` to set the audio stream's buffer size as a multiple of the burst size. For example: `audioStream.setBufferSizeInFrames(audioStream.getFramesPerBurst * numBuffers)`.
165*05767d91SRobert Wu*   Create an `AudioStreamDataCallback` object and move your OpenSL callback code inside the `onAudioReady` method.
166*05767d91SRobert Wu*   Handle stream disconnect events by creating an `AudioStreamErrorCallback` object and overriding one of its `onError` methods.
167*05767d91SRobert Wu*   Pass sensible default sample rate and buffer size values to Oboe from `AudioManager` [using this method](https://github.com/google/oboe/blob/main/docs/GettingStarted.md#obtaining-optimal-latency) so that your app is still performant on older devices.
168*05767d91SRobert Wu
169*05767d91SRobert WuFor more information please read the [Full Guide to Oboe](https://github.com/google/oboe/blob/main/docs/FullGuide.md).
170