aboutsummaryrefslogtreecommitdiff
path: root/docs/OpenSLESMigration.md
blob: 3792f96a04b2a72e88f364934fdf59586aa88fa3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
OpenSLES Migration Guide
===

# Introduction

This 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. 

To familiarise yourself with Oboe, please read the [Getting Started guide](https://github.com/google/oboe/blob/master/docs/GettingStarted.md) and ensure that Oboe has been added as a dependency in your project.


# Concepts

At 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.

This is where the similarities end.

Oboe 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.


# Key differences


## Object mappings

OpenSL 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`.

OpenSL uses audio player and audio recorder objects to communicate with audio devices. In Oboe an `AudioStream` is used.

In OpenSL the audio callback mechanism is a user-defined function which is called each time a buffer is enqueued. In Oboe you construct an `AudioStreamCallback` object, and its `onAudioReady` method is called each time audio data is ready to be read or written.  

Here's a table which summarizes the object mappings:


<table>
  <tr>
   <td><strong>OpenSL</strong>
   </td>
   <td><strong>Oboe </strong>(all classes are in the <code>oboe</code> namespace)
   </td>
  </tr>
  <tr>
   <td>Audio engine (an <code>SLObjectItf</code>)
   </td>
   <td><code>AudioStreamBuilder</code>
   </td>
  </tr>
  <tr>
   <td>Audio player
   </td>
   <td><code>AudioStream</code> configured for output
   </td>
  </tr>
  <tr>
   <td>Audio recorder
   </td>
   <td><code>AudioStream</code> configured for input
   </td>
  </tr>
  <tr>
   <td>Callback function
   </td>
   <td><code>AudioStreamCallback::onAudioReady</code>
   </td>
  </tr>
</table>



## Buffers and callbacks

In 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.

In Oboe, rather than owning and enqueuing buffers, you are given direct access to the `AudioStream`'s buffer through the `audioData` parameter of `onAudioReady`.

This 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`:


```
DataCallbackResult onAudioReady(
    AudioStream *oboeStream,
    void *audioData,
    int32_t numFrames
)
```


You supply your implementation of `onAudioReady` when building the audio stream by constructing an `AudioStreamCallback` object. [Here's an example.](https://github.com/google/oboe/blob/master/docs/GettingStarted.md#creating-an-audio-stream)


### Buffer sizes

In 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.

By 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.  

An 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:


```
audioStream.setBufferSizeInFrames(audioStream.getFramesPerBurst() * 2);
```


**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/master/docs/GettingStarted.md#obtaining-optimal-latency).


## Audio stream properties

In OpenSL you must explicitly specify various properties, including the sample rate and audio format, when opening an audio player or audio recorder.

In 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.


```
AudioStreamBuilder builder;
builder.openStream(myStream);
```


However, you may want to specify some properties. These are set using the `AudioStreamBuilder` ([example](https://github.com/google/oboe/blob/master/docs/FullGuide.md#set-the-audio-stream-configuration-using-an-audiostreambuilder)).


## Stream disconnection

OpenSL has no mechanism, other than stopping callbacks, to indicate that an audio device has been disconnected - for example, when headphones are unplugged.

In Oboe, you can be notified of stream disconnection by overriding one of the `onError` methods in `AudioStreamCallback`. 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/master/docs/FullGuide.md#disconnected-audio-stream)).


# Unsupported features


## Formats

Oboe 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.

Compressed audio, such as MP3, is not supported for a number of reasons but chiefly:



*   The OpenSL ES implementation has performance and reliability issues.
*   It keeps the Oboe API and the underlying implementation simple.

Extraction 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/master/samples/RhythmGame).


## Miscellaneous features

Oboe does **not** support the following features:



*   Channel masks - only [indexed channel masks](https://developer.android.com/reference/kotlin/android/media/AudioFormat#channel-index-masks) are supported.
*   Playing audio content from a file pathname or [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier).
*   Notification callbacks for position updates.
*   Platform output effects on API 27 and below. [They are supported from API 28 and above.](https://github.com/google/oboe/blob/master/docs/notes/effects.md)


# Summary



*   Replace your audio player or recorder with an `AudioStream` created using an `AudioStreamBuilder`.
*   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)`.
*   Create an `AudioStreamCallback` object and move your OpenSL callback code inside the `onAudioReady` method.
*   Handle stream disconnect events by overriding one of the `AudioStreamCallback::onError` methods.
*   Pass sensible default sample rate and buffer size values to Oboe from `AudioManager` [using this method](https://github.com/google/oboe/blob/master/docs/GettingStarted.md#obtaining-optimal-latency) so that your app is still performant on older devices.

For more information please read the [Full Guide to Oboe](https://github.com/google/oboe/blob/master/docs/FullGuide.md).