diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b82a0fda..89e684ec8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,32 +1,12 @@ cmake_minimum_required(VERSION 3.4.1) -set (oboe_sources - src/aaudio/AAudioLoader.cpp - src/aaudio/AudioStreamAAudio.cpp - src/common/LatencyTuner.cpp - src/common/AudioStream.cpp - src/common/AudioStreamBuilder.cpp - src/common/Utilities.cpp - src/fifo/FifoBuffer.cpp - src/fifo/FifoController.cpp - src/fifo/FifoControllerBase.cpp - src/fifo/FifoControllerIndirect.cpp - src/opensles/AudioInputStreamOpenSLES.cpp - src/opensles/AudioOutputStreamOpenSLES.cpp - src/opensles/AudioStreamBuffered.cpp - src/opensles/AudioStreamOpenSLES.cpp - src/opensles/EngineOpenSLES.cpp - src/opensles/OpenSLESUtilities.cpp - src/opensles/OutputMixerOpenSLES.cpp - ) - -add_library(oboe STATIC ${oboe_sources}) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -std=c++11") # Specify directories which the compiler should look for headers -target_include_directories(oboe PRIVATE src include) +include_directories( + include + src) -target_compile_options(oboe PRIVATE -std=c++11 - PRIVATE -Wall - PRIVATE "$<$:-Werror>") # Only include -Werror when building debug config +file(GLOB_RECURSE oboe_sources src/*) -target_link_libraries(oboe PRIVATE log OpenSLES) +add_library(oboe STATIC ${oboe_sources}) \ No newline at end of file diff --git a/FullGuide.md b/FullGuide.md index 014c903e5..7466502c4 100644 --- a/FullGuide.md +++ b/FullGuide.md @@ -3,7 +3,7 @@ Oboe is a C++ library which makes it easy to build high-performance audio apps o ## Audio streams -Oboe moves audio data between your app and the audio inputs and outputs on your Android device. Your app passes data in and out by reading from and writing to *audio streams*, represented by the class `AudioStream`. The read/write calls can be blocking or non-blocking. +Oboe moves audio data between your app and the audio inputs and outputs on your Android device. Your app passes data in and out by reading from and writing to *audio streams*, represented by the class `OboeStream`. The read/write calls can be blocking or non-blocking. A stream is defined by the following: @@ -28,16 +28,11 @@ The audio device attached to a stream determines whether the stream is for input A stream has a sharing mode: -* `SharingMode::Exclusive` (available on API 26+) means the stream has exclusive access to an endpoint on its audio device; the endpoint cannot be used by any other audio stream. If the exclusive endpoint is already in use, it might not be possible for the stream to obtain access to it. Exclusive streams provide the lowest possible latency by bypassing the mixer stage, but they are also more likely to get disconnected. You should close exclusive streams as soon as you no longer need them, so that other apps can access that endpoint. Not all audio devices provide exclusive endpoints. System sounds and sounds from other apps can still be heard when an exclusive stream is in use as they use a different endpoint. +* `OBOE_SHARING_MODE_EXCLUSIVE` (available on API 26+) means the stream has exclusive access to its audio device; the device cannot be used by any other audio stream. If the audio device is already in use, it might not be possible for the stream to have exclusive access. Exclusive streams provide the lowest possible latency, but they are also more likely to get disconnected. You should close exclusive streams as soon as you no longer need them, so that other apps can access the device. +* `OBOE_SHARING_MODE_SHARED` allows Oboe to mix audio. Oboe mixes all the shared streams assigned to the same device. -![Oboe exclusive sharing mode diagram](oboe-sharing-mode-exclusive.jpg) - -* `SharingMode::Shared` allows Oboe to mix audio. Oboe mixes all the shared streams assigned to the same endpoint on the audio device. - -![Oboe exclusive sharing mode diagram](oboe-sharing-mode-shared.jpg) - - -You can explicitly request the sharing mode when you create a stream, although you are not guaranteed to receive that mode. By default, the sharing mode is `Shared`. +You can explicitly request the sharing mode when you create a stream, although you are not guaranteed to receive that mode. By default, +the sharing mode is `SHARED`. ### Audio format @@ -49,26 +44,26 @@ The data passed through a stream has the usual digital audio attributes, which y Oboe permits these sample formats: -| AudioFormat | C data type | Notes | +| oboe_format_t | C data type | Notes | | :------------ | :---------- | :---- | -| I16 | int16_t | common 16-bit samples, [Q0.15 format](https://source.android.com/devices/audio/data_formats#androidFormats) | -| Float | float | -1.0 to +1.0 | +| OBOE_FORMAT_PCM_I16 | int16_t | common 16-bit samples, [Q0.15 format](https://source.android.com/devices/audio/data_formats#androidFormats) | +| OBOE_FORMAT_PCM_FLOAT | float | -1.0 to +1.0 | Oboe might perform sample conversion on its own. For example, if an app is writing FLOAT data but the HAL uses PCM_I16, Oboe might convert the samples automatically. Conversion can happen in either direction. If your app processes audio input, it is wise to verify the input format and be prepared to convert data if necessary, as in this example: - AudioFormat dataFormat = stream->getDataFormat(); + oboe_format_t dataFormat = stream->getDataFormat(); //... later - if (dataFormat == AudioFormat::I16) { + if (dataFormat == OBOE_FORMAT_PCM_I16) { convertFloatToPcm16(...) } ## Creating an audio stream -The Oboe library follows a [builder design pattern](https://en.wikipedia.org/wiki/Builder_pattern) and provides the class `AudioStreamBuilder`. +The Oboe library follows a [builder design pattern](https://en.wikipedia.org/wiki/Builder_pattern) and provides the class `OboeStreamBuilder`. - 1. Set the audio stream configuration using an AudioStreamBuilder. Use the builder functions that correspond to the stream parameters. These optional set functions are available: + 1. Set the audio stream configuration using an OboeStreamBuilder. Use the builder functions that correspond to the stream parameters. These optional set functions are available: - AudioStreamBuilder streamBuilder; + OboeStreamBuilder streamBuilder; streamBuilder.setDeviceId(deviceId); streamBuilder.setDirection(direction); @@ -86,25 +81,25 @@ whether Oboe will use AAudio or OpenSL ES as the audio engine for you app. Oboe will automatically select the best implementation available on your device. If you want to specifically select AAudio or OpenSL, set the APIIndex yourself. After a stream has been opened, you can verify that the API you specified was -chosen by calling `AudioStreamBuilder::getAPIIndex()`. The allowable indexes are -`AAudio` and `OpenSLES`. +chosen by calling `OboeStreamBuilder::getAPIIndex()`. The allowable indexes are +`API_AAUDIO` and `API_OPENSL_ES`. If you do not specify the deviceId, the default is the primary output device. If you do not specify the stream direction, the default is an output stream. For all other parameters, you can explicitly set a value, or let the system assign the optimal value by not specifying the parameter at all or setting -it to `kUnspecified`. +it to `OBOE_UNSPECIFIED`. To be safe, check the state of the audio stream after you create it, as explained in step 3, below. - 2. After you've configured the AudioStreamBuilder, call `openStream()` to open the stream: + 2. After you've configured the OboeStreamBuilder, call `openStream()` to open the stream: - Result result = streamBuilder.openStream(&stream_); - if (result != OK){ + oboe_result_t result = streamBuilder.openStream(&stream_); + if (result != OBOE_OK){ __android_log_print(ANDROID_LOG_ERROR, "AudioEngine", "Error opening stream %s", - convertToText(result)); + OboeConvert_ResultToText(result)); } @@ -118,7 +113,7 @@ To be safe, check the state of the audio stream after you create it, as explaine builder setting: -| AudioStreamBuilder set methods | AudioStream get methods | +| StreamBuilder set methods | Stream get methods | | :------------------------ | :----------------- | | `setDeviceId()` | `getDeviceId()` | | `setDirection()` | `getDirection()` | @@ -144,7 +139,7 @@ Data only flows through a stream when the stream is in the Started state. To move a stream between states, use one of the functions that request a state transition: - Result result; + oboe_result_t result; result = stream->requestStart(); result = stream->requestStop(); result = stream->requestPause(); @@ -171,7 +166,7 @@ Though it's not shown, you can call `close()` from any state Oboe doesn't provide callbacks to alert you to state changes. One special function, -`AudioStream::waitForStateChange()` can be used to wait for a state change. +`OboeStream::waitForStateChange()` can be used to wait for a state change. The function does not detect a state change on its own, and does not wait for a specific state. It waits until the current state @@ -183,9 +178,9 @@ Since you can't wait for the Paused state, use `waitForStateChange()` to wait fo other than Pausing*. Here's how that's done: ``` -StreamState inputState = StreamState::Pausing; -StreamState nextState = StreamState::Uninitialized; -int64_t timeoutNanos = 100 * kNanosPerMillisecond; +oboe_stream_state_t inputState = OBOE_STREAM_STATE_PAUSING; +oboe_stream_state_t nextState = OBOE_STREAM_STATE_UNINITIALIZED; +int64_t timeoutNanos = 100 * OBOE_NANOS_PER_MILLISECOND; result = stream->requestPause(); result = stream->waitForStateChange(inputState, &nextState, timeoutNanos); ``` @@ -199,16 +194,16 @@ stream. You can use this same technique after calling request start, stop, or flush, using the corresponding transient state as the inputState. Do not call -`waitForStateChange()` after calling `AudioStream::close()` since the stream +`waitForStateChange()` after calling `OboeStream::close()` since the stream will be deleted as soon as it closes. And do not call `close()` while `waitForStateChange()` is running in another thread. ### Reading and writing to an audio stream After the stream is started you can read or write to it using the methods -`AudioStream::read(buffer, numFrames, timeoutNanos)` +`OboeStream::read(buffer, numFrames, timeoutNanos)` and -`AudioStream::write(buffer, numFrames, timeoutNanos)`. +`OboeStream::write(buffer, numFrames, timeoutNanos)`. For a blocking read or write that transfers the specified number of frames, set timeoutNanos greater than zero. For a non-blocking call, set timeoutNanos to zero. In this case the result is the actual number of frames transferred. @@ -219,7 +214,7 @@ frames was read. If not, the buffer might contain unknown data that could cause audio glitch. You can pad the buffer with zeros to create a silent dropout: - Result result = + oboe_result_t result = stream.read(audioData, numFrames, timeout); if (result < 0) { // Error! @@ -253,7 +248,7 @@ An audio stream can become disconnected at any time if one of these events happe When a stream is disconnected, it has the state "Disconnected" and any attempts to execute write() or other functions return `OBOE_ERROR_DISCONNECTED`. When a stream is disconnected, all you can do is close it. If you need to be informed when an audio device is disconnected, write a class -which extends `AudioStreamCallback` and implements the `onError(stream, error)` +which extends `OboeStreamCallback` and implements the `onError(stream, error)` method. Register your class using `builder.setCallback(yourCallbackClass)`. The `onError()` method should check the state of the stream as shown in the following @@ -263,17 +258,17 @@ stream it might have different characteristics than the original stream (for example framesPerBurst): ``` -void PlayAudioEngine::onError(AudioStream *audioStream, Result error) { - if (error == Result::ErrorDisconnected) { +void PlayAudioEngine::onError(OboeStream *audioStream, oboe_result_t error) { + if (error == OBOE_ERROR_DISCONNECTED) { // Handle stream restart on a separate thread std::function restartStream = std::bind(&PlayAudioEngine::restartStream, this); mStreamRestartThread = new std::thread(restartStream); } - // See Definitions.h for other Result::Error* codes + // See OboeDefinitions.h for other OBOE_ERROR_* codes } ``` -You can also implement two other callback methods in the class `AudioStreamCallback`: +You can also implement two other callback methods in the class `OboeStreamCallback`: * `onAudioReady()` is used for a high-priority callback * `onExit()` is called when the callback thread exits. @@ -290,17 +285,17 @@ For applications that require low latency, an audio stream can use an asynchrono The callback runs in a high-priority thread that has better performance. Your code can access the callback mechanism by implementing the virtual class -`AudioStreamCallback`. The stream periodically executes `onAudioReady()` (the +`OboeStreamCallback`. The stream periodically executes `onAudioReady()` (the callback function) to acquire the data for its next burst. - class AudioEngine : AudioStreamCallback { + class AudioEngine : OboeStreamCallback { public: - DataCallbackResult AudioEngine::onAudioReady( - AudioStream *oboeStream, + oboe_data_callback_result_t AudioEngine::onAudioReady( + OboeStream *oboeStream, void *audioData, int32_t numFrames){ oscillator_->render(static_cast(audioData), numFrames); - return DataCallbackResult::Continue; + return OBOE_CALLBACK_RESULT_CONTINUE; } bool AudioEngine::start() { @@ -327,24 +322,24 @@ The callback does a non-blocking read from the input stream placing the data int (Note that Oboe version 1 does not support input streams, so this example cannot run.) - class AudioEngine : AudioStreamCallback { + class AudioEngine : OboeStreamCallback { public: oboe_data_callback_result_t AudioEngine::onAudioReady( - AudioStream *oboeStream, + OboeStream *oboeStream, void *audioData, int32_t numFrames){ - Result result = + oboe_result_t result = stream2.read(audioData, numFrames, timeout); if (result == numFrames) - return DataCallbackResult::Continue; + return OBOE_CALLBACK_RESULT_CONTINUE; if (result >= 0) { memset(static_cast(audioData) + result * samplesPerFrame, 0, sizeof(sample_type) * (numFrames - result) * samplesPerFrame); - return DataCallbackResult::Continue; + return OBOE_CALLBACK_RESULT_CONTINUE; } - return DataCallbackResult::Stop; + return OBOE_CALLBACK_RESULT_STOP; } bool AudioEngine::start() { @@ -352,12 +347,12 @@ The callback does a non-blocking read from the input stream placing the data int streamBuilder.setCallback(this); } - void setRecordingStream(AudioStream *stream) { + void setRecordingStream(OboeStream *stream) { recordingStream = stream; } private: - AudioStream *recordingStream; + OboeStream *recordingStream; } @@ -365,34 +360,34 @@ Note that in this example it is assumed the input and output streams have the sa ### Setting performance mode -Every AudioStream has a *performance mode* which has a large effect on your app's behavior. There are three modes: +Every OboeStream has a *performance mode* which has a large effect on your app's behavior. There are three modes: -* `PerformanceMode::None` is the default mode. It uses a basic stream that balances latency and power savings. -* `PerformanceMode::LowLatency` uses smaller buffers and an optimized data path for reduced latency. -* `PerformanceMode::PowerSaving` uses larger internal buffers and a data path that trades off latency for lower power. +* `OBOE_PERFORMANCE_MODE_NONE` is the default mode. It uses a basic stream that balances latency and power savings. +* `OBOE_PERFORMANCE_MODE_LOW_LATENCY` uses smaller buffers and an optimized data path for reduced latency. +* `OBOE_PERFORMANCE_MODE_POWER_SAVING` uses larger internal buffers and a data path that trades off latency for lower power. You can select the performance mode by calling `setPerformanceMode()`, and discover the current mode by calling `getPerformanceMode()`. -If low latency is more important than power savings in your application, use `PerformanceMode::LowLatency`. +If low latency is more important than power savings in your application, use `OBOE_PERFORMANCE_MODE_LOW_LATENCY`. This is useful for apps that are very interactive, such as games or keyboard synthesizers. -If saving power is more important than low latency in your application, use `PerformanceMode::PowerSaving`. +If saving power is more important than low latency in your application, use `OBOE_PERFORMANCE_MODE_POWER_SAVING`. This is typical for apps that play back previously generated music, such as streaming audio or MIDI file players. -In the current version of Oboe, in order to achieve the lowest possible latency you must use the `PerformanceMode::LowLatency` performance mode along with a high-priority callback. Follow this example: +In the current version of Oboe, in order to achieve the lowest possible latency you must use the `OBOE_PERFORMANCE_MODE_LOW_LATENCY` performance mode along with a high-priority callback. Follow this example: ``` // Create a callback object MyOboeStreamCallback myCallback; // Create a stream builder -AudioStreamBuilder builder; +OboeStreamBuilder builder; builder.setCallback(myCallback); -builder.setPerformanceMode(PerformanceMode::LowLatency); +builder.setPerformanceMode(OBOE_PERFORMANCE_MODE_LOW_LATENCY); // Use it to create the stream -AudioStream *stream; +OboeStream *stream; builder.openStream(&stream); ``` @@ -404,12 +399,18 @@ This is because Oboe avoids using mutexes, which can cause thread preemption and To be safe, don't call `waitForStateChange()` or read or write to the same stream from two different threads. Similarly, don't close a stream in one thread while reading or writing to it in another thread. -Calls that return stream settings, like `AudioStream::getSampleRate()` and `AudioStream::getChannelCount()`, are thread safe. +Calls that return stream settings, like `OboeStream::getSampleRate()` and `OboeStream::getChannelCount()`, are thread safe. These calls are also thread safe: -* `convertToText()` -* `AudioStream::get*()` except for `getTimestamp()` +* `Oboe_convertToText()` +* `Oboe_convertAudioFormatToText()` +* `Oboe_convertPerformanceModeToText()` +* `Oboe_convertSharingModeToText()` +* `Oboe_convertDataCallbackResultToText()` +* `Oboe_convertDirectionToText()` +* `OboeStream::get*()` except for `getTimestamp()` + Note: When a stream uses a callback function, it's safe to read/write from the callback thread while also closing the stream from the thread in which it is running. @@ -423,7 +424,7 @@ A small demo app is available on our [GitHub page](https://github.com/googlesamp ## Known Issues -The following methods are defined, but will return `Result::ErrorUnimplemented`: +The following methods are defined, but will return `OBOE_ERROR_UNIMPLEMENTED`: * `setBufferSizeInFrames()` * `getBufferSizeInFrames()` diff --git a/GettingStarted.md b/GettingStarted.md index 43b23a195..c28e0a443 100644 --- a/GettingStarted.md +++ b/GettingStarted.md @@ -1,53 +1,42 @@ # Getting Started The easiest way to start using Oboe is to build it from source by adding a few steps to an existing Android Studio project. -## Adding Oboe to your project - -### 1. Clone the github repository +## Building Oboe Start by cloning the Oboe repository: git clone https://github.com/google/oboe -**Make a note of the path which you cloned oboe into - you will need it shortly** - -### 2. Update CMakeLists.txt -Open your app's `CMakeLists.txt`. This can be found under `External Build Files` in the Android project view. +Open your app's `CMakeLists.txt`, this can be found under `External Build Files` in the Android project view. ![CMakeLists.txt location in Android Studio](cmakelists-location-in-as.png "CMakeLists.txt location in Android Studio") -Now add the following commands to the end of `CMakeLists.txt`. **Remember to update `**PATH TO OBOE**` with your local Oboe path from the previous step**: - - # Set the path to the Oboe directory. - set (OBOE_DIR ***PATH TO OBOE***) - - # Add the Oboe library as a subdirectory in your project. - add_subdirectory (${OBOE_DIR} ./oboe) - - # Specify the path to the Oboe header files. - include_directories (${OBOE_DIR}/include) +Now add the following build steps to `CMakeLists.txt`, making sure you update `/local/path/to/oboe` with your local Oboe repository directory: + # Build the Oboe library + set (OBOE_DIR /local/path/to/oboe) + file (GLOB_RECURSE OBOE_SOURCES ${OBOE_DIR}/src/*) + include_directories(${OBOE_DIR}/include ${OBOE_DIR}/src) + add_library(oboe STATIC ${OBOE_SOURCES}) -In the same file find the [`target_link_libraries`](https://cmake.org/cmake/help/latest/command/target_link_libraries.html) command. -Add `oboe` to the list of libraries which your app's library depends on. For example: +In the same file *after* your own library definition (by default it is named `native-lib`) add the dependencies for the Oboe and OpenSLES libraries: - target_link_libraries(native-lib oboe) + target_link_libraries(native-lib log oboe OpenSLES) Here's a complete example `CMakeLists.txt` file: cmake_minimum_required(VERSION 3.4.1) + # Build the Oboe library + set (OBOE_DIR /Users/donturner/Code/workspace-android/oboe) + file (GLOB_RECURSE OBOE_SOURCES ${OBOE_DIR}/src/*) + include_directories(${OBOE_DIR}/include ${OBOE_DIR}/src) + add_library(oboe STATIC ${OBOE_SOURCES}) + # Build our own native library - add_library (native-lib SHARED src/main/cpp/native-lib.cpp ) + add_library( native-lib SHARED src/main/cpp/native-lib.cpp ) - # Specify the libraries which our native library is dependent on, including Oboe - target_link_libraries (native-lib log oboe) - - # Build the Oboe library - set (OBOE_DIR ../../../oboe) - add_subdirectory (${OBOE_DIR} ./oboe) - - # Make the Oboe public headers available to our app - include_directories (${OBOE_DIR}/include) + # Specify the libraries which our native library is dependent on + target_link_libraries( native-lib log oboe OpenSLES ) Verify that your project builds correctly. If you have any issues building please [report them here](issues/new). @@ -59,24 +48,24 @@ Include the Oboe header: #include -Streams are built using an `AudioStreamBuilder`. Create one like this: +Streams are built using an `OboeStreamBuilder`. Create one like this: - oboe::AudioStreamBuilder builder; + OboeStreamBuilder builder; Use the builder's set methods to set properties on the stream (you can read more about these properties in the [full guide](FullGuide.md)): - builder.setDirection(oboe::Direction::Output); - builder.setPerformanceMode(oboe::PerformanceMode::LowLatency); - builder.setSharingMode(oboe::SharingMode::Exclusive); + builder.setDirection(OBOE_DIRECTION_OUTPUT); + builder.setPerformanceMode(OBOE_PERFORMANCE_MODE_LOW_LATENCY); + builder.setSharingMode(OBOE_SHARING_MODE_EXCLUSIVE); -Define an `AudioStreamCallback` class to receive callbacks whenever the stream requires new data. +Define an `OboeStreamCallback` class to receive callbacks whenever the stream requires new data. - class MyCallback : public oboe::AudioStreamCallback { + class MyCallback : public OboeStreamCallback { public: - oboe::Result - onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames){ + oboe_data_callback_result_t + onAudioReady(OboeStream *audioStream, void *audioData, int32_t numFrames){ generateSineWave(static_cast(audioData), numFrames); - return oboe::DataCallbackResult::Continue; + return OBOE_CALLBACK_RESULT_CONTINUE; } }; @@ -87,21 +76,21 @@ Supply this callback class to the builder: Open the stream: - oboe::AudioStream *stream; - oboe::Result result = builder.openStream(&stream); + OboeStream *stream; + oboe_result_t result = builder.openStream(&stream); -Check the result to make sure the stream was opened successfully. Oboe has a convenience method for converting its types into human-readable strings called `oboe::convertToText`: +Check the result to make sure the stream was opened successfully. Oboe has many convenience methods for converting its types into human-readable strings, they all start with `Oboe_convert`: - if (result != Result::OK){ - LOGE("Failed to create stream. Error: %s", oboe::convertToText(result)); + if (result != OBOE_OK){ + LOGE("Failed to create stream. Error: %s", Oboe_convertResultToText(result)); } Note that this sample code uses the [logging macros from here](https://github.com/googlesamples/android-audio-high-performance/blob/master/debug-utils/logging_macros.h). -Check the properties of the created stream. The **format** is one property which you should check. The default is `float` on API 21+ and `int16_t` on API 20 or lower. This will dictate the `audioData` type in the `AudioStreamCallback::onAudioReady` callback. +Check the properties of the created stream. The **format** is one property which you should check. The default is `float` on API 21+ and `int16_t` on API 20 or lower. This will dictate the `audioData` type in the `OboeStreamCallback::onAudioReady` callback. - oboe::AudioFormat format = stream->getFormat(); - LOGI("AudioStream format is %s", oboe::convertToText(format)); + oboe_audio_format_t format = stream->getFormat(); + LOGI("Stream format is %s", Oboe_convertAudioFormatToText(format)); Now start the stream. diff --git a/README.md b/README.md index e267af924..36f63c5ed 100755 --- a/README.md +++ b/README.md @@ -3,16 +3,18 @@ Oboe is a C++ library which makes it easy to build high-performance audio apps on Android. It was created primarily to allow developers to target a simplified API that works across multiple API levels back to API level 16 (Jelly Bean). +**Note:** This version (0.9) of Oboe only supports playback (output) streams. Support for recording (input) streams is in active development. + [Get started with Oboe here](GettingStarted.md). ## Features - Compatible with API 16 onwards - runs on 99% of Android devices -- Chooses the audio API (OpenSL ES on API 16+ or AAudio on API 27+) which will give the best audio performance on the target Android device +- Chooses the audio API (OpenSL ES on API 16+ or AAudio on API 26+) which will give the best audio performance on the target Android device - Automatic latency tuning - Modern C++ allowing you to write clean, elegant code ## Requirements -To build Oboe you will need the [Android NDK](https://developer.android.com/ndk/index.html) r15 or above +To build Oboe you will need the [Android NDK](https://developer.android.com/ndk/index.html) r16 or above ## Documentation - [Getting Started Guide](GettingStarted.md) diff --git a/build_all_android.sh b/build_all_android.sh index edbe6b7ae..056769325 100755 --- a/build_all_android.sh +++ b/build_all_android.sh @@ -121,8 +121,8 @@ build_oboe x86_64 21 printf "%s\r\n" "example: |" >> ${CDEP_MANIFEST_FILE} printf "%s\r\n" " #include " >> ${CDEP_MANIFEST_FILE} printf "%s\r\n" " void openStream() {" >> ${CDEP_MANIFEST_FILE} -printf "%s\r\n" " AudioStreamBuilder builder;" >> ${CDEP_MANIFEST_FILE} -printf "%s\r\n" " AudioStream *stream;" >> ${CDEP_MANIFEST_FILE} +printf "%s\r\n" " OboeStreamBuilder builder;" >> ${CDEP_MANIFEST_FILE} +printf "%s\r\n" " OboeStream *stream;" >> ${CDEP_MANIFEST_FILE} printf "%s\r\n" " builder.openStream(&stream);" >> ${CDEP_MANIFEST_FILE} printf "%s\r\n" " }" >> ${CDEP_MANIFEST_FILE} diff --git a/include/oboe/AudioStreamCallback.h b/include/oboe/AudioStreamCallback.h deleted file mode 100644 index 9a5db1763..000000000 --- a/include/oboe/AudioStreamCallback.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef OBOE_STREAM_CALLBACK_H -#define OBOE_STREAM_CALLBACK_H - -#include "oboe/Definitions.h" - -namespace oboe { - -class AudioStream; - -class AudioStreamCallback { -public: - virtual ~AudioStreamCallback() = default; - - /** - * A buffer is ready for processing. - * - * @param oboeStream pointer to the associated stream - * @param audioData buffer containing input data or a place to put output data - * @param numFrames number of frames to be processed - * @return DataCallbackResult::Continue or DataCallbackResult::Stop - */ - virtual DataCallbackResult onAudioReady( - AudioStream *oboeStream, - void *audioData, - int32_t numFrames) = 0; - - /** - * This will be called when an error occurs on a stream or when the stream is discomnnected. - * The underlying stream will already be stopped by Oboe but not yet closed. - * So the stream can be queried. - * - * @param oboeStream pointer to the associated stream - * @param error - */ - virtual void onErrorBeforeClose(AudioStream *oboeStream, Result error) {} - - /** - * This will be called when an error occurs on a stream or when the stream is disconnected. - * The underlying stream will already be stopped AND closed by Oboe. - * So the underlyng stream cannot be referenced. - * - * This callback could be used to reopen a new stream on another device. - * - * @param oboeStream pointer to the associated stream - * @param error - */ - virtual void onErrorAfterClose(AudioStream *oboeStream, Result error) {} - -}; - -} // namespace oboe - -#endif //OBOE_STREAM_CALLBACK_H diff --git a/include/oboe/Definitions.h b/include/oboe/Definitions.h deleted file mode 100644 index 4b02b3c30..000000000 --- a/include/oboe/Definitions.h +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef OBOE_DEFINITIONS_H -#define OBOE_DEFINITIONS_H - -#include -#include -#include - -// Ensure that all AAudio primitive data types are int32_t -#define ASSERT_INT32(type) static_assert(std::is_same::value, \ -#type" must be int32_t") - -ASSERT_INT32(aaudio_stream_state_t); -ASSERT_INT32(aaudio_direction_t); -ASSERT_INT32(aaudio_format_t); -ASSERT_INT32(aaudio_data_callback_result_t); -ASSERT_INT32(aaudio_result_t); -ASSERT_INT32(aaudio_sharing_mode_t); -ASSERT_INT32(aaudio_performance_mode_t); - -namespace oboe { - - constexpr int32_t kUnspecified = 0; - - // TODO: Investigate using std::chrono - constexpr int64_t kNanosPerMicrosecond = 1000; - constexpr int64_t kNanosPerMillisecond = kNanosPerMicrosecond * 1000; - constexpr int64_t kMillisPerSecond = 1000; - constexpr int64_t kNanosPerSecond = kNanosPerMillisecond * kMillisPerSecond; - - enum class StreamState : aaudio_stream_state_t { - Uninitialized = AAUDIO_STREAM_STATE_UNINITIALIZED, - Unknown = AAUDIO_STREAM_STATE_UNKNOWN, - Open = AAUDIO_STREAM_STATE_OPEN, - Starting = AAUDIO_STREAM_STATE_STARTING, - Started = AAUDIO_STREAM_STATE_STARTED, - Pausing = AAUDIO_STREAM_STATE_PAUSING, - Paused = AAUDIO_STREAM_STATE_PAUSED, - Flushing = AAUDIO_STREAM_STATE_FLUSHING, - Flushed = AAUDIO_STREAM_STATE_FLUSHED, - Stopping = AAUDIO_STREAM_STATE_STOPPING, - Stopped = AAUDIO_STREAM_STATE_STOPPED, - Closing = AAUDIO_STREAM_STATE_CLOSING, - Closed = AAUDIO_STREAM_STATE_CLOSED, - Disconnected = AAUDIO_STREAM_STATE_DISCONNECTED, - }; - - enum class Direction : aaudio_direction_t { - Output = AAUDIO_DIRECTION_OUTPUT, - Input = AAUDIO_DIRECTION_INPUT, - }; - - enum class AudioFormat : aaudio_format_t { - Invalid = AAUDIO_FORMAT_INVALID, - Unspecified = AAUDIO_FORMAT_UNSPECIFIED, - I16 = AAUDIO_FORMAT_PCM_I16, - Float = AAUDIO_FORMAT_PCM_FLOAT, - }; - - enum class DataCallbackResult : aaudio_data_callback_result_t { - Continue = AAUDIO_CALLBACK_RESULT_CONTINUE, - Stop = AAUDIO_CALLBACK_RESULT_STOP, - }; - - enum class Result : aaudio_result_t { - OK, - ErrorBase = AAUDIO_ERROR_BASE, - ErrorDisconnected = AAUDIO_ERROR_DISCONNECTED, - ErrorIllegalArgument = AAUDIO_ERROR_ILLEGAL_ARGUMENT, - ErrorInternal = AAUDIO_ERROR_INTERNAL, - ErrorInvalidState = AAUDIO_ERROR_INVALID_STATE, - ErrorInvalidHandle = AAUDIO_ERROR_INVALID_HANDLE, - ErrorUnimplemented = AAUDIO_ERROR_UNIMPLEMENTED, - ErrorUnavailable = AAUDIO_ERROR_UNAVAILABLE, - ErrorNoFreeHandles = AAUDIO_ERROR_NO_FREE_HANDLES, - ErrorNoMemory = AAUDIO_ERROR_NO_MEMORY, - ErrorNull = AAUDIO_ERROR_NULL, - ErrorTimeout = AAUDIO_ERROR_TIMEOUT, - ErrorWouldBlock = AAUDIO_ERROR_WOULD_BLOCK, - ErrorInvalidFormat = AAUDIO_ERROR_INVALID_FORMAT, - ErrorOutOfRange = AAUDIO_ERROR_OUT_OF_RANGE, - ErrorNoService = AAUDIO_ERROR_NO_SERVICE, - ErrorInvalidRate = AAUDIO_ERROR_INVALID_RATE, - }; - - enum class SharingMode : aaudio_sharing_mode_t { - - /** - * This will be the only stream using a particular source or sink. - * This mode will provide the lowest possible latency. - * You should close EXCLUSIVE streams immediately when you are not using them. - */ - Exclusive = AAUDIO_SHARING_MODE_EXCLUSIVE, - - /** - * Multiple applications will be mixed by the AAudio Server. - * This will have higher latency than the EXCLUSIVE mode. - */ - Shared = AAUDIO_SHARING_MODE_SHARED, - }; - - enum class PerformanceMode : aaudio_performance_mode_t { - - // No particular performance needs. Default. - None = AAUDIO_PERFORMANCE_MODE_NONE, - - // Extending battery life is most important. - PowerSaving = AAUDIO_PERFORMANCE_MODE_POWER_SAVING, - - // Reducing latency is most important. - LowLatency = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY - }; - - enum class AudioApi : int32_t { - /** - * Try to use AAudio. If not available then use OpenSL ES. - */ - Unspecified = kUnspecified, - - /** - * Use OpenSL ES. - */ - OpenSLES, - - /** - * Try to use AAudio. Fail if unavailable. - */ - AAudio - }; - -} // namespace oboe - -#endif // OBOE_DEFINITIONS_H diff --git a/include/oboe/Oboe.h b/include/oboe/Oboe.h index 1fd8a59ee..eab021ca8 100644 --- a/include/oboe/Oboe.h +++ b/include/oboe/Oboe.h @@ -17,12 +17,11 @@ #ifndef OBOE_OBOE_H #define OBOE_OBOE_H -#include "oboe/Definitions.h" -#include "oboe/LatencyTuner.h" -#include "oboe/AudioStream.h" -#include "oboe/AudioStreamBase.h" -#include "oboe/AudioStreamBuilder.h" -#include "oboe/Utilities.h" -#include "oboe/Version.h" +#include "oboe/OboeDefinitions.h" +#include "oboe/OboeLatencyTuner.h" +#include "oboe/OboeStream.h" +#include "oboe/OboeStreamBase.h" +#include "oboe/OboeStreamBuilder.h" +#include "oboe/OboeUtilities.h" #endif //OBOE_OBOE_H diff --git a/include/oboe/OboeDefinitions.h b/include/oboe/OboeDefinitions.h new file mode 100644 index 000000000..6c7a8f194 --- /dev/null +++ b/include/oboe/OboeDefinitions.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_OBOE_DEFINITIONS_H +#define OBOE_OBOE_DEFINITIONS_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef aaudio_result_t oboe_result_t; + +/** + * This is used to represent a value that has not been specified. + * For example, an application could use OBOE_UNSPECIFIED to indicate + * that it did not not care what the specific value of a parameter was + * and would accept whatever it was given. + */ +#define OBOE_UNSPECIFIED 0 +#define OBOE_NANOS_PER_MICROSECOND ((int64_t)1000) +#define OBOE_NANOS_PER_MILLISECOND (OBOE_NANOS_PER_MICROSECOND * 1000) +#define OBOE_MILLIS_PER_SECOND 1000 +#define OBOE_NANOS_PER_SECOND (OBOE_NANOS_PER_MILLISECOND * OBOE_MILLIS_PER_SECOND) + +#define oboe_direction_t aaudio_direction_t +#define OBOE_DIRECTION_OUTPUT AAUDIO_DIRECTION_OUTPUT +#define OBOE_DIRECTION_INPUT AAUDIO_DIRECTION_INPUT + +#define oboe_audio_format_t aaudio_format_t +#define OBOE_AUDIO_FORMAT_INVALID AAUDIO_FORMAT_INVALID +#define OBOE_AUDIO_FORMAT_UNSPECIFIED AAUDIO_FORMAT_UNSPECIFIED +#define OBOE_AUDIO_FORMAT_PCM_I16 AAUDIO_FORMAT_PCM_I16 +#define OBOE_AUDIO_FORMAT_PCM_FLOAT AAUDIO_FORMAT_PCM_FLOAT + +#define oboe_data_callback_result_t aaudio_data_callback_result_t +#define OBOE_CALLBACK_RESULT_CONTINUE AAUDIO_CALLBACK_RESULT_CONTINUE +#define OBOE_CALLBACK_RESULT_STOP AAUDIO_CALLBACK_RESULT_STOP + +enum { + OBOE_OK, + OBOE_ERROR_BASE = AAUDIO_ERROR_BASE, + OBOE_ERROR_DISCONNECTED = AAUDIO_ERROR_DISCONNECTED, + OBOE_ERROR_ILLEGAL_ARGUMENT = AAUDIO_ERROR_ILLEGAL_ARGUMENT, + // Reserved + OBOE_ERROR_INTERNAL = AAUDIO_ERROR_INTERNAL, + OBOE_ERROR_INVALID_STATE = AAUDIO_ERROR_INVALID_STATE, + // Reserved + // Reserved + OBOE_ERROR_INVALID_HANDLE = AAUDIO_ERROR_INVALID_HANDLE, + // Reserved + OBOE_ERROR_UNIMPLEMENTED = AAUDIO_ERROR_UNIMPLEMENTED, + OBOE_ERROR_UNAVAILABLE = AAUDIO_ERROR_UNAVAILABLE, + OBOE_ERROR_NO_FREE_HANDLES = AAUDIO_ERROR_NO_FREE_HANDLES, + OBOE_ERROR_NO_MEMORY = AAUDIO_ERROR_NO_MEMORY, + OBOE_ERROR_NULL = AAUDIO_ERROR_NULL, + OBOE_ERROR_TIMEOUT = AAUDIO_ERROR_TIMEOUT, + OBOE_ERROR_WOULD_BLOCK = AAUDIO_ERROR_WOULD_BLOCK, + OBOE_ERROR_INVALID_FORMAT = AAUDIO_ERROR_INVALID_FORMAT, + OBOE_ERROR_OUT_OF_RANGE = AAUDIO_ERROR_OUT_OF_RANGE, + OBOE_ERROR_NO_SERVICE = AAUDIO_ERROR_NO_SERVICE, + OBOE_ERROR_INVALID_RATE = AAUDIO_ERROR_INVALID_RATE +}; + +#define oboe_stream_state_t aaudio_stream_state_t + +#define OBOE_STREAM_STATE_UNINITIALIZED AAUDIO_STREAM_STATE_UNINITIALIZED +#define OBOE_STREAM_STATE_OPEN AAUDIO_STREAM_STATE_OPEN +#define OBOE_STREAM_STATE_STARTING AAUDIO_STREAM_STATE_STARTING +#define OBOE_STREAM_STATE_STARTED AAUDIO_STREAM_STATE_STARTED +#define OBOE_STREAM_STATE_PAUSING AAUDIO_STREAM_STATE_PAUSING +#define OBOE_STREAM_STATE_PAUSED AAUDIO_STREAM_STATE_PAUSED +#define OBOE_STREAM_STATE_FLUSHING AAUDIO_STREAM_STATE_FLUSHING +#define OBOE_STREAM_STATE_FLUSHED AAUDIO_STREAM_STATE_FLUSHED +#define OBOE_STREAM_STATE_STOPPING AAUDIO_STREAM_STATE_STOPPING +#define OBOE_STREAM_STATE_STOPPED AAUDIO_STREAM_STATE_STOPPED +#define OBOE_STREAM_STATE_CLOSING AAUDIO_STREAM_STATE_CLOSING +#define OBOE_STREAM_STATE_CLOSED AAUDIO_STREAM_STATE_CLOSED + +#define oboe_sharing_mode_t aaudio_sharing_mode_t + + /** + * This will be the only stream using a particular source or sink. + * This mode will provide the lowest possible latency. + * You should close EXCLUSIVE streams immediately when you are not using them. + */ +#define OBOE_SHARING_MODE_EXCLUSIVE AAUDIO_SHARING_MODE_EXCLUSIVE + /** + * Multiple applications will be mixed by the Oboe Server. + * This will have higher latency than the EXCLUSIVE mode. + */ +#define OBOE_SHARING_MODE_SHARED AAUDIO_SHARING_MODE_SHARED + + /** + * No particular performance needs. Default. + */ +#define OBOE_PERFORMANCE_MODE_NONE AAUDIO_PERFORMANCE_MODE_NONE + + /** + * Extending battery life is most important. + */ +#define OBOE_PERFORMANCE_MODE_POWER_SAVING AAUDIO_PERFORMANCE_MODE_POWER_SAVING + + /** + * Reducing latency is most important. + */ +#define OBOE_PERFORMANCE_MODE_LOW_LATENCY AAUDIO_PERFORMANCE_MODE_LOW_LATENCY + +#define oboe_performance_mode_t aaudio_performance_mode_t + +#ifdef __cplusplus +} +#endif + +#endif // OBOE_OBOE_DEFINITIONS_H diff --git a/include/oboe/LatencyTuner.h b/include/oboe/OboeLatencyTuner.h similarity index 78% rename from include/oboe/LatencyTuner.h rename to include/oboe/OboeLatencyTuner.h index 7b879ba57..b5ac6c617 100644 --- a/include/oboe/LatencyTuner.h +++ b/include/oboe/OboeLatencyTuner.h @@ -14,15 +14,13 @@ * limitations under the License. */ -#ifndef OBOE_LATENCY_TUNER_ -#define OBOE_LATENCY_TUNER_ +#ifndef OBOE_OBOE_LATENCY_TUNER_ +#define OBOE_OBOE_LATENCY_TUNER_ #include -#include -#include "oboe/Definitions.h" -#include "oboe/AudioStream.h" - -namespace oboe { +#include +#include "oboe/OboeDefinitions.h" +#include "oboe/OboeStream.h" /** * This can be used to dynamically tune the latency of an output stream. @@ -39,9 +37,9 @@ namespace oboe { * stream->getBufferSize() periodically. * */ -class LatencyTuner { +class OboeLatencyTuner { public: - explicit LatencyTuner(AudioStream &stream); + explicit OboeLatencyTuner(OboeStream &stream); /** * Adjust the bufferSizeInFrames to optimize latency. @@ -49,9 +47,9 @@ class LatencyTuner { * * Latency tuning is only supported for AAudio. * - * @return OK or negative error, ErrorUnimplemented for OpenSL ES + * @return OBOE_OK or negative error, OBOE_ERROR_UNIMPLEMENTED for OpenSL ES */ - Result tune(); + oboe_result_t tune(); /** * This may be called from another thread. Then tune() will call reset(), @@ -73,24 +71,23 @@ class LatencyTuner { */ void reset(); - enum class State { - Idle, - Active, - AtMax, - Unsupported + enum latency_tuner_state_t { + STATE_IDLE, + STATE_ACTIVE, + STATE_AT_MAX, + STATE_UNSUPPORTED } ; - // arbitrary number of calls to wait before bumping up the latency - static constexpr int32_t kIdleCount = 8; + enum { + IDLE_COUNT = 8 // arbitrary number of calls to wait before bumping up the latency + }; - AudioStream &mStream; - State mState = State::Idle; + OboeStream &mStream; + latency_tuner_state_t mState = STATE_IDLE; int32_t mPreviousXRuns = 0; int32_t mIdleCountDown = 0; std::atomic mLatencyTriggerRequests{0}; // TODO user atomic requester from AAudio std::atomic mLatencyTriggerResponses{0}; }; -} // namespace oboe - -#endif // OBOE_LATENCY_TUNER_ +#endif // OBOE_OBOE_LATENCY_TUNER_ diff --git a/include/oboe/AudioStream.h b/include/oboe/OboeStream.h similarity index 62% rename from include/oboe/AudioStream.h rename to include/oboe/OboeStream.h index 90b98a8dc..d3a7f9d80 100644 --- a/include/oboe/AudioStream.h +++ b/include/oboe/OboeStream.h @@ -14,31 +14,31 @@ * limitations under the License. */ -#ifndef OBOE_STREAM_H_ -#define OBOE_STREAM_H_ +#ifndef OBOE_OBOE_STREAM_H_ +#define OBOE_OBOE_STREAM_H_ -#include -#include -#include "oboe/Definitions.h" -#include "oboe/AudioStreamBuilder.h" -#include "oboe/AudioStreamBase.h" +#include +#include +#include "oboe/OboeDefinitions.h" +#include "oboe/OboeStreamCallback.h" +#include "oboe/OboeStreamBase.h" /** WARNING - UNDER CONSTRUCTION - THIS API WILL CHANGE. */ -namespace oboe { +class OboeStreamBuilder; -constexpr int64_t kDefaultTimeoutNanos = (2000 * kNanosPerMillisecond); +#define DEFAULT_TIMEOUT_NANOS (2000 * OBOE_NANOS_PER_MILLISECOND) /** * Base class for Oboe C++ audio stream. */ -class AudioStream : public AudioStreamBase { +class OboeStream : public OboeStreamBase { public: - AudioStream() {} - explicit AudioStream(const AudioStreamBuilder &builder); + OboeStream() {} + explicit OboeStream(const OboeStreamBuilder &builder); - virtual ~AudioStream() = default; + virtual ~OboeStream() = default; /** * Open a stream based on the current settings. @@ -48,62 +48,65 @@ class AudioStream : public AudioStreamBase { * * @return */ - virtual Result open(); + virtual oboe_result_t open(); /** * Close the stream and deallocate any resources from the open() call. */ - virtual Result close() = 0; + virtual oboe_result_t close() = 0; /* * These are synchronous and will block until the operation is complete. */ - virtual Result start(int64_t timeoutNanoseconds = kDefaultTimeoutNanos); - virtual Result pause(int64_t timeoutNanoseconds = kDefaultTimeoutNanos); - virtual Result flush(int64_t timeoutNanoseconds = kDefaultTimeoutNanos); - virtual Result stop(int64_t timeoutNanoseconds = kDefaultTimeoutNanos); + virtual oboe_result_t start(int64_t timeoutNanoseconds = DEFAULT_TIMEOUT_NANOS); + virtual oboe_result_t pause(int64_t timeoutNanoseconds = DEFAULT_TIMEOUT_NANOS); + virtual oboe_result_t flush(int64_t timeoutNanoseconds = DEFAULT_TIMEOUT_NANOS); + virtual oboe_result_t stop(int64_t timeoutNanoseconds = DEFAULT_TIMEOUT_NANOS); /* Asynchronous requests. * Use waitForStateChange() if you need to wait for completion. */ - virtual Result requestStart() = 0; - virtual Result requestPause() = 0; - virtual Result requestFlush() = 0; - virtual Result requestStop() = 0; + virtual oboe_result_t requestStart() = 0; + virtual oboe_result_t requestPause() = 0; + virtual oboe_result_t requestFlush() = 0; + virtual oboe_result_t requestStop() = 0; /** - * Query the current state, eg. StreamState::Pausing + * Query the current state, eg. OBOE_STREAM_STATE_PAUSING * * @return state or a negative error. */ - virtual StreamState getState() = 0; + virtual oboe_stream_state_t getState() = 0; /** - * Wait until the stream's current state no longer matches the input state. - * The input state is passed to avoid race conditions caused by the state + * Wait until the current state no longer matches the input state. + * The current state is passed to avoid race conditions caused by the state * changing between calls. * * Note that generally applications do not need to call this. It is considered * an advanced technique. * *

-     * int64_t timeoutNanos = 500 * kNanosPerMillisecond; // arbitrary 1/2 second
-     * StreamState currentState = stream->getState(stream);
-     * while (currentState >= 0 && currentState != StreamState::Paused) {
+     * int64_t timeoutNanos = 500 * OBOE_NANOS_PER_MILLISECOND; // arbitrary 1/2 second
+     * oboe_stream_state_t currentState = stream->getState(stream);
+     * while (currentState >= 0 && currentState != OBOE_STREAM_STATE_PAUSED) {
      *     currentState = stream->waitForStateChange(
      *                                   stream, currentState, timeoutNanos);
      * }
      * 
* - * @param inputState The state we want to avoid. + * @param stream A handle provided by OboeStreamBuilder_openStream() + * @param currentState The state we want to avoid. * @param nextState Pointer to a variable that will be set to the new state. * @param timeoutNanoseconds The maximum time to wait in nanoseconds. - * @return Result::OK or a Result::Error. + * @return OBOE_OK or a negative error. */ - virtual Result waitForStateChange(StreamState inputState, - StreamState *nextState, + virtual oboe_result_t waitForStateChange(oboe_stream_state_t currentState, + oboe_stream_state_t *nextState, int64_t timeoutNanoseconds) = 0; + + /** * This can be used to adjust the latency of the buffer by changing * the threshold where blocking will occur. @@ -113,10 +116,10 @@ class AudioStream : public AudioStreamBase { * This cannot be set higher than getBufferCapacity(). * * @param requestedFrames requested number of frames that can be filled without blocking - * @return resulting buffer size in frames or a Result::Error + * @return resulting buffer size in frames or a negative error */ - virtual Result setBufferSizeInFrames(int32_t requestedFrames) { - return Result::ErrorUnimplemented; + virtual oboe_result_t setBufferSizeInFrames(int32_t requestedFrames) { + return OBOE_ERROR_UNIMPLEMENTED; } /** @@ -131,7 +134,7 @@ class AudioStream : public AudioStreamBase { * @return the count or negative error. */ virtual int32_t getXRunCount() { - return static_cast(Result::ErrorUnimplemented); + return OBOE_ERROR_UNIMPLEMENTED; } /** @@ -143,6 +146,8 @@ class AudioStream : public AudioStreamBase { bool isPlaying(); + OboeStreamCallback *getCallback() const { return mStreamCallback; } + int32_t getBytesPerFrame() const { return mChannelCount * getBytesPerSample(); } int32_t getBytesPerSample() const; @@ -153,12 +158,12 @@ class AudioStream : public AudioStreamBase { */ virtual int64_t getFramesWritten() { return mFramesWritten; } - virtual int64_t getFramesRead() { return static_cast(Result::ErrorUnimplemented); } + virtual int64_t getFramesRead() { return OBOE_ERROR_UNIMPLEMENTED; } - virtual Result getTimestamp(clockid_t clockId, + virtual oboe_result_t getTimestamp(clockid_t clockId, int64_t *framePosition, int64_t *timeNanoseconds) { - return Result::ErrorUnimplemented; + return OBOE_ERROR_UNIMPLEMENTED; } // ============== I/O =========================== @@ -172,16 +177,16 @@ class AudioStream : public AudioStreamBase { * @param timeoutNanoseconds Maximum number of nanoseconds to wait for completion. * @return The number of frames actually written or a negative error. */ - virtual int32_t write(const void *buffer, + virtual oboe_result_t write(const void *buffer, int32_t numFrames, int64_t timeoutNanoseconds) { - return static_cast(Result::ErrorUnimplemented); + return OBOE_ERROR_UNIMPLEMENTED; } - virtual int32_t read(void *buffer, + virtual oboe_result_t read(void *buffer, int32_t numFrames, int64_t timeoutNanoseconds) { - return static_cast(Result::ErrorUnimplemented); + return OBOE_ERROR_UNIMPLEMENTED; } /** @@ -200,29 +205,28 @@ class AudioStream : public AudioStreamBase { /** * Wait for a transition from one state to another. - * @return OK if the endingState was observed, or ErrorUnexpectedState + * @return OBOE_OK if the endingState was observed, or OBOE_ERROR_UNEXPECTED_STATE * if any state that was not the startingState or endingState was observed - * or ErrorTimeout. + * or OBOE_ERROR_TIMEOUT. */ - virtual Result waitForStateTransition(StreamState startingState, - StreamState endingState, + virtual oboe_result_t waitForStateTransition(oboe_stream_state_t startingState, + oboe_stream_state_t endingState, int64_t timeoutNanoseconds); - Result fireCallback(void *audioData, int numFrames); + oboe_result_t fireCallback(void *audioData, int numFrames); - virtual void setNativeFormat(AudioFormat format) { + virtual void setNativeFormat(oboe_audio_format_t format) { mNativeFormat = format; } - // TODO: make private + // TODO make private // These do not change after open. - AudioFormat mNativeFormat = AudioFormat::Invalid; + oboe_audio_format_t mNativeFormat = OBOE_AUDIO_FORMAT_INVALID; private: int64_t mFramesWritten = 0; int mPreviousScheduler = -1; }; -} // namespace oboe -#endif /* OBOE_STREAM_H_ */ +#endif /* OBOE_OBOE_STREAM_H_ */ diff --git a/include/oboe/AudioStreamBase.h b/include/oboe/OboeStreamBase.h similarity index 59% rename from include/oboe/AudioStreamBase.h rename to include/oboe/OboeStreamBase.h index be2d892fd..8b77d56cd 100644 --- a/include/oboe/AudioStreamBase.h +++ b/include/oboe/OboeStreamBase.h @@ -14,14 +14,11 @@ * limitations under the License. */ -#ifndef OBOE_STREAM_BASE_H_ -#define OBOE_STREAM_BASE_H_ +#ifndef OBOE_OBOE_STREAM_BASE_H_ +#define OBOE_OBOE_STREAM_BASE_H_ -#include -#include "oboe/AudioStreamCallback.h" -#include "oboe/Definitions.h" - -namespace oboe { +#include "oboe/OboeStreamCallback.h" +#include "oboe/OboeDefinitions.h" /** * Base class containing parameters for Oboe streams and builders. @@ -31,17 +28,17 @@ namespace oboe { * OboeStream will generally return the actual final value, but getFramesPerCallback() * can be unspecified even for a stream. */ -class AudioStreamBase { +class OboeStreamBase { public: - AudioStreamBase() {} + OboeStreamBase() {} - virtual ~AudioStreamBase() = default; + virtual ~OboeStreamBase() = default; // This class only contains primitives so we can use default constructor and copy methods. - AudioStreamBase(const AudioStreamBase&) = default; + OboeStreamBase(const OboeStreamBase&) = default; - AudioStreamBase& operator=(const AudioStreamBase&) = default; + OboeStreamBase& operator=(const OboeStreamBase&) = default; /** * @return number of channels, for example 2 for stereo @@ -49,9 +46,9 @@ class AudioStreamBase { int getChannelCount() const { return mChannelCount; } /** - * @return Direction::Input or Direction::Output + * @return OBOE_DIRECTION_INPUT or OBOE_DIRECTION_OUTPUT */ - Direction getDirection() const { return mDirection; } + oboe_direction_t getDirection() const { return mDirection; } /** * @return sample rate for the stream @@ -67,7 +64,7 @@ class AudioStreamBase { * @return OBOE_AUDIO_FORMAT_PCM_FLOAT, OBOE_AUDIO_FORMAT_PCM_I16 * or OBOE_AUDIO_FORMAT_UNSPECIFIED */ - AudioFormat getFormat() const { return mFormat; } + oboe_audio_format_t getFormat() const { return mFormat; } /** * Query the maximum number of frames that can be filled without blocking. @@ -84,30 +81,28 @@ class AudioStreamBase { */ virtual int32_t getBufferCapacityInFrames() const { return mBufferCapacityInFrames; } - SharingMode getSharingMode() const { return mSharingMode; } + oboe_sharing_mode_t getSharingMode() const { return mSharingMode; } - PerformanceMode getPerformanceMode() const { return mPerformanceMode; } + oboe_performance_mode_t getPerformanceMode() const { return mPerformanceMode; } int32_t getDeviceId() const { return mDeviceId; } - AudioStreamCallback* getCallback() const { + OboeStreamCallback *getCallback() const { return mStreamCallback; } protected: - AudioStreamCallback *mStreamCallback = nullptr; - int32_t mFramesPerCallback = kUnspecified; - int32_t mChannelCount = kUnspecified; - int32_t mSampleRate = kUnspecified; - int32_t mDeviceId = kUnspecified; - int32_t mBufferCapacityInFrames = kUnspecified; - - SharingMode mSharingMode = SharingMode::Shared; - AudioFormat mFormat = AudioFormat::Unspecified; - Direction mDirection = Direction::Output; - PerformanceMode mPerformanceMode = PerformanceMode::None; + OboeStreamCallback *mStreamCallback = NULL; + int32_t mFramesPerCallback = OBOE_UNSPECIFIED; + int32_t mChannelCount = OBOE_UNSPECIFIED; + int32_t mSampleRate = OBOE_UNSPECIFIED; + int32_t mDeviceId = OBOE_UNSPECIFIED; + int32_t mBufferCapacityInFrames = OBOE_UNSPECIFIED; + + oboe_sharing_mode_t mSharingMode = OBOE_SHARING_MODE_SHARED; + oboe_audio_format_t mFormat = OBOE_AUDIO_FORMAT_UNSPECIFIED; + oboe_direction_t mDirection = OBOE_DIRECTION_OUTPUT; + oboe_performance_mode_t mPerformanceMode = OBOE_PERFORMANCE_MODE_NONE; }; -} // namespace oboe - -#endif /* OBOE_STREAM_BASE_H_ */ +#endif /* OBOE_OBOE_STREAM_BASE_H_ */ diff --git a/include/oboe/AudioStreamBuilder.h b/include/oboe/OboeStreamBuilder.h similarity index 60% rename from include/oboe/AudioStreamBuilder.h rename to include/oboe/OboeStreamBuilder.h index 34c38b8f2..1985b44a0 100644 --- a/include/oboe/AudioStreamBuilder.h +++ b/include/oboe/OboeStreamBuilder.h @@ -14,41 +14,56 @@ * limitations under the License. */ -#ifndef OBOE_STREAM_BUILDER_H_ -#define OBOE_STREAM_BUILDER_H_ +#ifndef OBOE_OBOE_STREAM_BUILDER_H_ +#define OBOE_OBOE_STREAM_BUILDER_H_ -#include "oboe/Definitions.h" -#include "oboe/AudioStreamBase.h" - -namespace oboe { - -constexpr int32_t kDefaultFramesPerBurst = 192; // arbitrary value, 4 msec at 48000 Hz +#include "OboeStreamBase.h" +#include "OboeStream.h" +#include "oboe/OboeDefinitions.h" /** - * Factory class for an audio Stream. + * Factory class for an AudioStream. */ -class AudioStreamBuilder : public AudioStreamBase { +class OboeStreamBuilder : public OboeStreamBase { public: - AudioStreamBuilder() : AudioStreamBase() {} + OboeStreamBuilder() : OboeStreamBase() {} + + enum audio_api_index_t { + /** + * Try to use AAudio. If not available then use OpenSL ES. + */ + API_UNSPECIFIED, + + /** + * Use OpenSL ES. + */ + API_OPENSL_ES, + + /** + * Try to use AAudio. Fail if unavailable. + */ + API_AAUDIO + }; + /** * Request a specific number of channels. * - * Default is kUnspecified. If the value is unspecified then + * Default is OBOE_UNSPECIFIED. If the value is unspecified then * the application should query for the actual value after the stream is opened. */ - AudioStreamBuilder *setChannelCount(int channelCount) { + OboeStreamBuilder *setChannelCount(int channelCount) { mChannelCount = channelCount; return this; } /** - * Request the direction for a stream. The default is Direction::Output. + * Request the direction for a stream. The default is OBOE_DIRECTION_OUTPUT. * - * @param direction Direction::Output or Direction::Input + * @param direction OBOE_DIRECTION_OUTPUT or OBOE_DIRECTION_INPUT */ - AudioStreamBuilder *setDirection(Direction direction) { + OboeStreamBuilder *setDirection(oboe_direction_t direction) { mDirection = direction; return this; } @@ -56,7 +71,7 @@ class AudioStreamBuilder : public AudioStreamBase { /** * Request a specific sample rate in Hz. * - * Default is kUnspecified. If the value is unspecified then + * Default is OBOE_UNSPECIFIED. If the value is unspecified then * the application should query for the actual value after the stream is opened. * * Technically, this should be called the "frame rate" or "frames per second", @@ -64,7 +79,7 @@ class AudioStreamBuilder : public AudioStreamBase { * But it is traditionally called "sample rate". Se we use that term. * */ - AudioStreamBuilder *setSampleRate(int32_t sampleRate) { + OboeStreamBuilder *setSampleRate(int32_t sampleRate) { mSampleRate = sampleRate; return this; } @@ -72,7 +87,7 @@ class AudioStreamBuilder : public AudioStreamBase { /** * Request a specific number of frames for the data callback. * - * Default is kUnspecified. If the value is unspecified then + * Default is OBOE_UNSPECIFIED. If the value is unspecified then * the actual number may vary from callback to callback. * * If an application can handle a varying number of frames then we recommend @@ -83,18 +98,18 @@ class AudioStreamBuilder : public AudioStreamBase { * @param framesPerCallback * @return */ - AudioStreamBuilder *setFramesPerCallback(int framesPerCallback) { + OboeStreamBuilder *setFramesPerCallback(int framesPerCallback) { mFramesPerCallback = framesPerCallback; return this; } /** - * Request a sample data format, for example Format::Float. + * Request a sample data format, for example OBOE_FORMAT_PCM_FLOAT. * - * Default is Format::Unspecified. If the value is unspecified then + * Default is OBOE_UNSPECIFIED. If the value is unspecified then * the application should query for the actual value after the stream is opened. */ - AudioStreamBuilder *setFormat(AudioFormat format) { + OboeStreamBuilder *setFormat(oboe_audio_format_t format) { mFormat = format; return this; } @@ -103,26 +118,26 @@ class AudioStreamBuilder : public AudioStreamBase { * Set the requested maximum buffer capacity in frames. * The final stream capacity may differ, but will probably be at least this big. * - * Default is kUnspecified. + * Default is OBOE_UNSPECIFIED. * - * @param frames the desired buffer capacity in frames or kUnspecified + * @param frames the desired buffer capacity in frames or OBOE_UNSPECIFIED * @return pointer to the builder so calls can be chained */ - AudioStreamBuilder *setBufferCapacityInFrames(int32_t bufferCapacityInFrames) { + OboeStreamBuilder *setBufferCapacityInFrames(int32_t bufferCapacityInFrames) { mBufferCapacityInFrames = bufferCapacityInFrames; return this; } - AudioApi getAudioApi() const { return mAudioApi; } + audio_api_index_t getApiIndex() const { return mAudioApi; } /** * Normally you would leave this unspecified, and Oboe will chose the best API * for the device at runtime. - * @param Must be AudioApi::Unspecified, AudioApi::OpenSLES or AudioApi::AAudio. + * @param Must be API_UNSPECIFIED, API_OPENSL_ES or API_AAUDIO. * @return pointer to the builder so calls can be chained */ - AudioStreamBuilder *setAudioApi(AudioApi audioApi) { - mAudioApi = audioApi; + OboeStreamBuilder *setApiIndex(audio_api_index_t apiIndex) { + mAudioApi = apiIndex; return this; } @@ -140,10 +155,10 @@ class AudioStreamBuilder : public AudioStreamBase { * The requested sharing mode may not be available. * So the application should query for the actual mode after the stream is opened. * - * @param sharingMode SharingMode::Shared or SharingMode::Exclusive + * @param sharingMode OBOE_SHARING_MODE_LEGACY or OBOE_SHARING_MODE_EXCLUSIVE * @return pointer to the builder so calls can be chained */ - AudioStreamBuilder *setSharingMode(SharingMode sharingMode) { + OboeStreamBuilder *setSharingMode(oboe_sharing_mode_t sharingMode) { mSharingMode = sharingMode; return this; } @@ -153,10 +168,10 @@ class AudioStreamBuilder : public AudioStreamBase { * This will determine the latency, the power consumption, and the level of * protection from glitches. * - * @param performanceMode for example, PerformanceMode::LowLatency + * @param performanceMode for example, OBOE_PERFORMANCE_MODE_LOW_LATENCY * @return pointer to the builder so calls can be chained */ - AudioStreamBuilder *setPerformanceMode(PerformanceMode performanceMode) { + OboeStreamBuilder *setPerformanceMode(oboe_performance_mode_t performanceMode) { mPerformanceMode = performanceMode; return this; } @@ -167,33 +182,15 @@ class AudioStreamBuilder : public AudioStreamBase { * * By default, the primary device will be used. * - * @param deviceId device identifier or kUnspecified + * @param deviceId device identifier or OBOE_DEVICE_UNSPECIFIED * @return pointer to the builder so calls can be chained */ - AudioStreamBuilder *setDeviceId(int32_t deviceId) { + OboeStreamBuilder *setDeviceId(int32_t deviceId) { mDeviceId = deviceId; return this; } - /** - * Specifies an object to handle data or error related callbacks from the underlying API. - * - * When an error callback occurs, the associated stream will be stopped and closed in a separate thread. - * - * A note on why the streamCallback parameter is a raw pointer rather than a smart pointer: - * - * The caller should retain ownership of the object streamCallback points to. At first glance weak_ptr may seem like - * a good candidate for streamCallback as this implies temporary ownership. However, a weak_ptr can only be created - * from a shared_ptr. A shared_ptr incurs some performance overhead. The callback object is likely to be accessed - * every few milliseconds when the stream requires new data so this overhead is something we want to avoid. - * - * This leaves a raw pointer as the logical type choice. The only caveat being that the caller must not destroy - * the callback before the stream has been closed. - * - * @param streamCallback - * @return pointer to the builder so calls can be chained - */ - AudioStreamBuilder *setCallback(AudioStreamCallback *streamCallback) { + OboeStreamBuilder *setCallback(OboeStreamCallback *streamCallback) { mStreamCallback = streamCallback; return this; } @@ -216,7 +213,7 @@ class AudioStreamBuilder : public AudioStreamBase { * @param defaultFramesPerBurst * @return pointer to the builder so calls can be chained */ - AudioStreamBuilder *setDefaultFramesPerBurst(int32_t defaultFramesPerBurst) { + OboeStreamBuilder *setDefaultFramesPerBurst(int32_t defaultFramesPerBurst) { mDefaultFramesPerBurst = defaultFramesPerBurst; return this; } @@ -231,7 +228,7 @@ class AudioStreamBuilder : public AudioStreamBase { * @param stream pointer to a variable to receive the stream address * @return OBOE_OK if successful or a negative error code */ - Result openStream(AudioStream **stream); + oboe_result_t openStream(OboeStream **stream); protected: @@ -242,13 +239,11 @@ class AudioStreamBuilder : public AudioStreamBase { * * @return pointer to an AudioStream object. */ - oboe::AudioStream *build(); + OboeStream *build(); - AudioApi mAudioApi = AudioApi::Unspecified; + audio_api_index_t mAudioApi = API_UNSPECIFIED; - int32_t mDefaultFramesPerBurst = kDefaultFramesPerBurst; + int32_t mDefaultFramesPerBurst = 192; // arbitrary value, 4 msec at 48000 Hz }; -} // namespace oboe - -#endif /* OBOE_STREAM_BUILDER_H_ */ +#endif /* OBOE_OBOE_STREAM_BUILDER_H_ */ diff --git a/include/oboe/OboeStreamCallback.h b/include/oboe/OboeStreamCallback.h new file mode 100644 index 000000000..56f7d31b0 --- /dev/null +++ b/include/oboe/OboeStreamCallback.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_OBOE_STREAM_CALLBACK_H +#define OBOE_OBOE_STREAM_CALLBACK_H + +#include "oboe/OboeDefinitions.h" + +class OboeStream; + +class OboeStreamCallback { +public: + virtual ~OboeStreamCallback() = default; + /** + * A buffer is ready for processing. + * + * @param input buffer containing recorded audio, may be NULL + * @param output fill this with audio data to be played, may be NULL + * @param number of frames of input or output + * @return OBOE_CALLBACK_RESULT_CONTINUE or OBOE_CALLBACK_RESULT_STOP + */ + virtual oboe_data_callback_result_t onAudioReady( + OboeStream *audioStream, + void *audioData, + int32_t numFrames) = 0; + + virtual void onError(OboeStream *audioStream, oboe_result_t error) {} + + /** + * The callback thread is exiting. + * + * @param reason Why it is exiting. OBOE_OK if requested. + * Or maybe OBOE_ERROR_TIMEOUT or OBOE_ERROR_DISCONNECTED. + */ + virtual void onExit(oboe_result_t reason) {} +}; + + +#endif //OBOE_OBOE_STREAM_CALLBACK_H diff --git a/include/oboe/OboeUtilities.h b/include/oboe/OboeUtilities.h new file mode 100644 index 000000000..a5ccbd437 --- /dev/null +++ b/include/oboe/OboeUtilities.h @@ -0,0 +1,91 @@ +/* + * Copyright 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_OBOE_UTILITIES_H +#define OBOE_OBOE_UTILITIES_H + +#include +#include +#include "oboe/OboeDefinitions.h" + +void Oboe_convertFloatToPcm16(const float *source, int16_t *destination, int32_t numSamples); +void Oboe_convertPcm16ToFloat(const int16_t *source, float *destination, int32_t numSamples); + +/** + * @return the size of a sample of the given format in bytes or OBOE_ERROR_ILLEGAL_ARGUMENT + */ +int32_t Oboe_convertFormatToSizeInBytes(oboe_audio_format_t format); + +/** + * The text is the ASCII symbol corresponding to the returnCode, + * or an English message saying the returnCode is unrecognized. + * This is intended for developers to use when debugging. + * It is not for displaying to users. + * + * @return pointer to a text representation of an Oboe result code. + */ +const char * Oboe_convertResultToText(oboe_result_t returnCode); + +/** + * The text is the ASCII symbol corresponding to the audio format, + * or an English message saying the audio format is unrecognized. + * This is intended for developers to use when debugging. + * It is not for displaying to users. + * + * @return pointer to a text representation of an Oboe audio format. + */ +const char * Oboe_convertAudioFormatToText(oboe_audio_format_t format); + +/** + * The text is the ASCII symbol corresponding to the performance mode, + * or an English message saying the performance is unrecognized. + * This is intended for developers to use when debugging. + * It is not for displaying to users. + * + * @return pointer to a text representation of an Oboe performance mode. + */ +const char * Oboe_convertPerformanceModeToText(oboe_performance_mode_t mode); + +/** + * The text is the ASCII symbol corresponding to the sharing mode, + * or an English message saying the sharing mode is unrecognized. + * This is intended for developers to use when debugging. + * It is not for displaying to users. + * + * @return pointer to a text representation of an Oboe sharing mode. + */ +const char * Oboe_convertSharingModeToText(oboe_sharing_mode_t mode); + +/** + * The text is the ASCII symbol corresponding to the data callback result, + * or an English message saying the data callback result is unrecognized. + * This is intended for developers to use when debugging. + * It is not for displaying to users. + * + * @return pointer to a text representation of an Oboe data callback result. + */ +const char * Oboe_convertDataCallbackResultToText(oboe_data_callback_result_t result); + +/** + * The text is the ASCII symbol corresponding to the stream direction, + * or an English message saying the stream direction is unrecognized. + * This is intended for developers to use when debugging. + * It is not for displaying to users. + * + * @return pointer to a text representation of an Oboe stream direction. + */ +const char *Oboe_convertDirectionToText(oboe_direction_t direction); +#endif //OBOE_OBOE_UTILITIES_H diff --git a/include/oboe/Utilities.h b/include/oboe/Utilities.h deleted file mode 100644 index 4f2b1399d..000000000 --- a/include/oboe/Utilities.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef OBOE_UTILITIES_H -#define OBOE_UTILITIES_H - -#include -#include -#include "oboe/Definitions.h" - -namespace oboe { - -void convertFloatToPcm16(const float *source, int16_t *destination, int32_t numSamples); -void convertPcm16ToFloat(const int16_t *source, float *destination, int32_t numSamples); - -/** - * @return the size of a sample of the given format in bytes or OBOE_ERROR_ILLEGAL_ARGUMENT - */ -int32_t convertFormatToSizeInBytes(AudioFormat format); - -/** - * The text is the ASCII symbol corresponding to the supplied Oboe enum value, - * or an English message saying the value is unrecognized. - * This is intended for developers to use when debugging. - * It is not for displaying to users. - * - * @param enum value @see common/Utilities.cpp for concrete implementations - * @return text representation of an Oboe enum value. - */ -template -const char * convertToText(FromType); - - -} // namespace oboe - -#endif //OBOE_UTILITIES_H diff --git a/include/oboe/Version.h b/include/oboe/Version.h deleted file mode 100644 index 46143d5f5..000000000 --- a/include/oboe/Version.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef OBOE_VERSIONINFO_H -#define OBOE_VERSIONINFO_H - -/** - * A note on use of preprocessor defines: - * - * This is one of the few times when it's suitable to use preprocessor defines rather than constexpr - * Why? Because C++11 requires a lot of boilerplate code to convert integers into compile-time - * string literals. The preprocessor, despite it's lack of type checking, is more suited to the task - * - * See: https://stackoverflow.com/questions/6713420/c-convert-integer-to-string-at-compile-time/26824971#26824971 - * - */ - -// Type: 8-bit unsigned int. Min value: 0 Max value: 255. See below for description. -#define OBOE_VERSION_MAJOR 0 - -// Type: 8-bit unsigned int. Min value: 0 Max value: 255. See below for description. -#define OBOE_VERSION_MINOR 10 - -// Type: 16-bit unsigned int. Min value: 0 Max value: 65535. See below for description. -#define OBOE_VERSION_PATCH 0 - -#define OBOE_STRINGIFY(x) #x -#define OBOE_TOSTRING(x) OBOE_STRINGIFY(x) - -// Type: String literal. See below for description. -#define OBOE_VERSION_TEXT \ - OBOE_TOSTRING(OBOE_VERSION_MAJOR) "." \ - OBOE_TOSTRING(OBOE_VERSION_MINOR) "." \ - OBOE_TOSTRING(OBOE_VERSION_PATCH) - -// Type: 32-bit unsigned int. See below for description. -#define OBOE_VERSION_NUMBER ((OBOE_VERSION_MAJOR << 24) | (OBOE_VERSION_MINOR << 16) | OBOE_VERSION_PATCH) - -namespace oboe { - -struct Version { - /** - * This is incremented when we make breaking API changes. Based loosely on https://semver.org/. - */ - static constexpr uint8_t Major = OBOE_VERSION_MAJOR; - - /** - * This is incremented when we add backwards compatible functionality. Or set to zero when MAJOR is - * incremented. - */ - static constexpr uint8_t Minor = OBOE_VERSION_MINOR; - - /** - * This is incremented when we make backwards compatible bug fixes. Or set to zero when MINOR is - * incremented. - */ - static constexpr uint16_t Patch = OBOE_VERSION_PATCH; - - /** - * Version string in the form MAJOR.MINOR.PATCH. - */ - static constexpr const char * Text = OBOE_VERSION_TEXT; - - /** - * Integer representation of the current Oboe library version. This will always increase when the - * version number changes so can be compared using integer comparison. - */ - static constexpr uint32_t Number = OBOE_VERSION_NUMBER; -}; - -} // namespace oboe -#endif //OBOE_VERSIONINFO_H diff --git a/oboe-sharing-mode-exclusive.jpg b/oboe-sharing-mode-exclusive.jpg deleted file mode 100644 index 0d23481b8..000000000 Binary files a/oboe-sharing-mode-exclusive.jpg and /dev/null differ diff --git a/oboe-sharing-mode-shared.jpg b/oboe-sharing-mode-shared.jpg deleted file mode 100644 index 5233689c9..000000000 Binary files a/oboe-sharing-mode-shared.jpg and /dev/null differ diff --git a/src/aaudio/AAudioLoader.cpp b/src/aaudio/AAudioLoader.cpp index aeaf6f8d5..14d76ac0b 100644 --- a/src/aaudio/AAudioLoader.cpp +++ b/src/aaudio/AAudioLoader.cpp @@ -20,8 +20,6 @@ #define LIB_AAUDIO_NAME "libaaudio.so" -namespace oboe { - AAudioLoader::~AAudioLoader() { close(); // TODO dangerous from a destructor, require caller to close() } @@ -120,8 +118,8 @@ int AAudioLoader::open() { stream_getDirection = load_I_PS("AAudioStream_getDirection"); stream_getBufferCapacity = load_I_PS("AAudioStream_getBufferCapacityInFrames"); stream_getFramesPerBurst = load_I_PS("AAudioStream_getFramesPerBurst"); - stream_getFramesRead = load_L_PS("AAudioStream_getFramesRead"); - stream_getFramesWritten = load_L_PS("AAudioStream_getFramesWritten"); + stream_getFramesRead = load_L_PS("AAudioStream_getFramesWritten"); + stream_getFramesWritten = load_L_PS("AAudioStream_getFramesRead"); stream_getPerformanceMode = load_I_PS("AAudioStream_getPerformanceMode"); stream_getSampleRate = load_I_PS("AAudioStream_getSampleRate"); stream_getSharingMode = load_I_PS("AAudioStream_getSharingMode"); @@ -192,5 +190,3 @@ AAudioLoader::signature_I_PB AAudioLoader::load_I_PB(const char *functionName) { AAudioLoader_check((void *)proc, functionName); return proc; } - -} // namespace oboe \ No newline at end of file diff --git a/src/aaudio/AAudioLoader.h b/src/aaudio/AAudioLoader.h index a4491a26a..e2eb73953 100644 --- a/src/aaudio/AAudioLoader.h +++ b/src/aaudio/AAudioLoader.h @@ -18,12 +18,10 @@ #define OBOE_AAUDIO_LOADER_H_ #include -#include "oboe/Definitions.h" +#include "oboe/OboeDefinitions.h" #include "aaudio/AAudio.h" -namespace oboe { - /** * The AAudio API was not available in early versions of Android. * To avoid linker errors, we dynamically link with the functions by name using dlsym(). @@ -162,6 +160,4 @@ class AAudioLoader { void *mLibHandle = nullptr; }; -} // namespace oboe - #endif //OBOE_AAUDIO_LOADER_H_ diff --git a/src/aaudio/AudioStreamAAudio.cpp b/src/aaudio/AudioStreamAAudio.cpp deleted file mode 100644 index 452f8407e..000000000 --- a/src/aaudio/AudioStreamAAudio.cpp +++ /dev/null @@ -1,402 +0,0 @@ -/* - * Copyright 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include - -#include "aaudio/AAudioLoader.h" -#include "aaudio/AudioStreamAAudio.h" -#include "common/OboeDebug.h" -#include "oboe/Utilities.h" - -#ifdef __ANDROID__ -#include -#endif - - -using namespace oboe; -AAudioLoader *AudioStreamAAudio::mLibLoader = nullptr; - - -// 'C' wrapper for the data callback method -static aaudio_data_callback_result_t oboe_aaudio_data_callback_proc( - AAudioStream *stream, - void *userData, - void *audioData, - int32_t numFrames) { - - AudioStreamAAudio *oboeStream = (AudioStreamAAudio *)userData; - if (oboeStream != NULL) { - return static_cast( - oboeStream->callOnAudioReady(stream, audioData, numFrames)); - } else { - return static_cast(DataCallbackResult::Stop); - } -} - -static void oboe_aaudio_error_thread_proc(AudioStreamAAudio *oboeStream, - AAudioStream *stream, - Result error) { - if (oboeStream != NULL) { - oboeStream->onErrorInThread(stream, error); - } -} - -// 'C' wrapper for the error callback method -static void oboe_aaudio_error_callback_proc( - AAudioStream *stream, - void *userData, - aaudio_result_t error) { - - AudioStreamAAudio *oboeStream = (AudioStreamAAudio *)userData; - if (oboeStream != NULL) { - // Handle error on a separate thread - std::thread t(oboe_aaudio_error_thread_proc, oboeStream, stream, static_cast(error)); - t.detach(); - } -} - - -namespace oboe { - -/* - * Create a stream that uses Oboe Audio API. - */ -AudioStreamAAudio::AudioStreamAAudio(const AudioStreamBuilder &builder) - : AudioStream(builder) - , mFloatCallbackBuffer(nullptr) - , mShortCallbackBuffer(nullptr) - , mAAudioStream(nullptr) -{ - mCallbackThreadEnabled.store(false); - LOGD("AudioStreamAAudio() call isSupported()"); - isSupported(); -} - -AudioStreamAAudio::~AudioStreamAAudio() -{ - delete[] mFloatCallbackBuffer; - delete[] mShortCallbackBuffer; -} - -static bool isRunningOnAndroid8_1OrHigher() { -#ifdef __ANDROID__ - char sdk[PROP_VALUE_MAX] = {0}; - if (__system_property_get("ro.build.version.sdk", sdk) != 0) { - return atoi(sdk) >= 27; // SDK Level 27 is Android Oreo 8.1 - } -#endif - return false; -} - -bool AudioStreamAAudio::isSupported() { - if (!isRunningOnAndroid8_1OrHigher()) { - // See https://github.com/google/oboe/issues/40, - // AAudio is not stable enough on Android 8.0. - return false; - } - mLibLoader = AAudioLoader::getInstance(); - int openResult = mLibLoader->open(); - return openResult == 0; -} - - -Result AudioStreamAAudio::open() { - Result result = Result::OK; - - if (mAAudioStream != nullptr) { - return Result::ErrorInvalidState; - } - - result = AudioStream::open(); - if (result != Result::OK) { - return result; - } - - LOGD("AudioStreamAAudio(): AAudio_createStreamBuilder()"); - AAudioStreamBuilder *aaudioBuilder; - result = static_cast(mLibLoader->createStreamBuilder(&aaudioBuilder)); - if (result != Result::OK) { - return result; - } - - LOGD("AudioStreamAAudio.open() try with deviceId = %d", (int) mDeviceId); - mLibLoader->builder_setBufferCapacityInFrames(aaudioBuilder, mBufferCapacityInFrames); - mLibLoader->builder_setChannelCount(aaudioBuilder, mChannelCount); - mLibLoader->builder_setDeviceId(aaudioBuilder, mDeviceId); - mLibLoader->builder_setDirection(aaudioBuilder, static_cast(mDirection)); - mLibLoader->builder_setFormat(aaudioBuilder, static_cast(mFormat)); - mLibLoader->builder_setSampleRate(aaudioBuilder, mSampleRate); - mLibLoader->builder_setSharingMode(aaudioBuilder, - static_cast(mSharingMode)); - mLibLoader->builder_setPerformanceMode(aaudioBuilder, - static_cast(mPerformanceMode)); - - // TODO get more parameters from the builder? - - if (mStreamCallback != nullptr) { - mLibLoader->builder_setDataCallback(aaudioBuilder, oboe_aaudio_data_callback_proc, this); - mLibLoader->builder_setFramesPerDataCallback(aaudioBuilder, getFramesPerCallback()); - } - mLibLoader->builder_setErrorCallback(aaudioBuilder, oboe_aaudio_error_callback_proc, this); - - { - AAudioStream *stream = nullptr; - result = static_cast(mLibLoader->builder_openStream(aaudioBuilder, &stream)); - mAAudioStream.store(stream); - } - if (result != Result::OK) { - goto error2; - } - - // Query and cache the values that will not change. - mDeviceId = mLibLoader->stream_getDeviceId(mAAudioStream); - mChannelCount = mLibLoader->stream_getChannelCount(mAAudioStream); - mSampleRate = mLibLoader->stream_getSampleRate(mAAudioStream); - mNativeFormat = static_cast(mLibLoader->stream_getFormat(mAAudioStream)); - if (mFormat == AudioFormat::Unspecified) { - mFormat = mNativeFormat; - } - mSharingMode = static_cast(mLibLoader->stream_getSharingMode(mAAudioStream)); - mPerformanceMode = static_cast( - mLibLoader->stream_getPerformanceMode(mAAudioStream)); - mBufferCapacityInFrames = mLibLoader->stream_getBufferCapacity(mAAudioStream); - - LOGD("AudioStreamAAudio.open() app format = %d", (int) mFormat); - LOGD("AudioStreamAAudio.open() native format = %d", (int) mNativeFormat); - LOGD("AudioStreamAAudio.open() sample rate = %d", (int) mSampleRate); - LOGD("AudioStreamAAudio.open() capacity = %d", (int) mBufferCapacityInFrames); - -error2: - mLibLoader->builder_delete(aaudioBuilder); - LOGD("AudioStreamAAudio.open: AAudioStream_Open() returned %s, mAAudioStream = %p", - mLibLoader->convertResultToText(static_cast(result)), - mAAudioStream.load()); - return result; -} - -Result AudioStreamAAudio::close() -{ - // The main reason we have this mutex if to prevent a collision between a call - // by the application to stop a stream at the same time that an onError callback - // is being executed because of a disconnect. The close will delete the stream, - // which could otherwise cause the requestStop() to crash. - std::lock_guard lock(mLock); - Result result = Result::OK; - // This will delete the AAudio stream object so we need to null out the pointer. - AAudioStream *stream = mAAudioStream.exchange(nullptr); - if (stream != nullptr) { - result = static_cast(mLibLoader->stream_close(stream)); - } - return result; -} - -DataCallbackResult AudioStreamAAudio::callOnAudioReady(AAudioStream *stream, - void *audioData, - int32_t numFrames) { - return mStreamCallback->onAudioReady( - this, - audioData, - numFrames); -} - -void AudioStreamAAudio::onErrorInThread(AAudioStream *stream, Result error) { - LOGD("onErrorInThread() - entering ==================================="); - assert(stream == mAAudioStream.load()); - requestStop(); - if (mStreamCallback != nullptr) { - mStreamCallback->onErrorBeforeClose(this, error); - } - close(); - if (mStreamCallback != nullptr) { - mStreamCallback->onErrorAfterClose(this, error); - } - LOGD("onErrorInThread() - exiting ==================================="); -} - -Result AudioStreamAAudio::convertApplicationDataToNative(int32_t numFrames) { - Result result = Result::ErrorUnimplemented; - int32_t numSamples = numFrames * getChannelCount(); - if (mFormat == AudioFormat::Float) { - if (mNativeFormat == AudioFormat::I16) { - convertFloatToPcm16(mFloatCallbackBuffer, mShortCallbackBuffer, numSamples); - result = Result::OK; - } - } else if (mFormat == AudioFormat::I16) { - if (mNativeFormat == AudioFormat::Float) { - convertPcm16ToFloat(mShortCallbackBuffer, mFloatCallbackBuffer, numSamples); - result = Result::OK; - } - } - return result; -} - -Result AudioStreamAAudio::requestStart() -{ - std::lock_guard lock(mLock); - AAudioStream *stream = mAAudioStream.load(); - if (stream != nullptr) { - return static_cast(mLibLoader->stream_requestStart(stream)); - } else { - return Result::ErrorNull; - } -} - -Result AudioStreamAAudio::requestPause() -{ - std::lock_guard lock(mLock); - AAudioStream *stream = mAAudioStream.load(); - if (stream != nullptr) { - return static_cast(mLibLoader->stream_requestPause(stream)); - } else { - return Result::ErrorNull; - } -} - -Result AudioStreamAAudio::requestFlush() { - std::lock_guard lock(mLock); - AAudioStream *stream = mAAudioStream.load(); - if (stream != nullptr) { - return static_cast(mLibLoader->stream_requestFlush(stream)); - } else { - return Result::ErrorNull; - } -} - -Result AudioStreamAAudio::requestStop() -{ - std::lock_guard lock(mLock); - AAudioStream *stream = mAAudioStream.load(); - if (stream != nullptr) { - return static_cast(mLibLoader->stream_requestStop(stream)); - } else { - return Result::ErrorNull; - } -} - -// TODO: Update to return tuple of Result and framesWritten (avoids cast) -int32_t AudioStreamAAudio::write(const void *buffer, - int32_t numFrames, - int64_t timeoutNanoseconds) -{ - AAudioStream *stream = mAAudioStream.load(); - if (stream != nullptr) { - return mLibLoader->stream_write(mAAudioStream, buffer, numFrames, timeoutNanoseconds); - } else { - return static_cast(Result::ErrorNull); - } -} - -Result AudioStreamAAudio::waitForStateChange(StreamState currentState, - StreamState *nextState, - int64_t timeoutNanoseconds) -{ - AAudioStream *stream = mAAudioStream.load(); - if (stream != nullptr) { - - aaudio_stream_state_t aaudioNextState; - aaudio_result_t result = mLibLoader->stream_waitForStateChange( - mAAudioStream, - static_cast(currentState), - &aaudioNextState, - timeoutNanoseconds); - *nextState = static_cast(aaudioNextState); - return static_cast(result); - } else { - return Result::ErrorNull; - } -} - -Result AudioStreamAAudio::setBufferSizeInFrames(int32_t requestedFrames) -{ - if (requestedFrames > mBufferCapacityInFrames) { - requestedFrames = mBufferCapacityInFrames; - } - return static_cast(mLibLoader->stream_setBufferSize(mAAudioStream, requestedFrames)); -} - -StreamState AudioStreamAAudio::getState() -{ - AAudioStream *stream = mAAudioStream.load(); - if (stream != nullptr) { - return static_cast(mLibLoader->stream_getState(stream)); - } else { - return StreamState::Closed; - } -} - -int32_t AudioStreamAAudio::getBufferSizeInFrames() const { - AAudioStream *stream = mAAudioStream.load(); - if (stream != nullptr) { - return mLibLoader->stream_getBufferSize(stream); - } else { - return static_cast(Result::ErrorNull); - } -} - -int32_t AudioStreamAAudio::getFramesPerBurst() -{ - AAudioStream *stream = mAAudioStream.load(); - if (stream != nullptr) { - return mLibLoader->stream_getFramesPerBurst(stream); - } else { - return static_cast(Result::ErrorNull); - } -} - -int64_t AudioStreamAAudio::getFramesRead() -{ - AAudioStream *stream = mAAudioStream.load(); - if (stream != nullptr) { - return mLibLoader->stream_getFramesRead(stream); - } else { - return static_cast(Result::ErrorNull); - } -} -int64_t AudioStreamAAudio::getFramesWritten() -{ - AAudioStream *stream = mAAudioStream.load(); - if (stream != nullptr) { - return mLibLoader->stream_getFramesWritten(stream); - } else { - return static_cast(Result::ErrorNull); - } -} - -int32_t AudioStreamAAudio::getXRunCount() -{ - AAudioStream *stream = mAAudioStream.load(); - if (stream != nullptr) { - return mLibLoader->stream_getXRunCount(stream); - } else { - return static_cast(Result::ErrorNull); - } -} - -Result AudioStreamAAudio::getTimestamp(clockid_t clockId, - int64_t *framePosition, - int64_t *timeNanoseconds) { - AAudioStream *stream = mAAudioStream.load(); - if (stream != nullptr) { - return static_cast(mLibLoader->stream_getTimestamp(stream, clockId, - framePosition, timeNanoseconds)); - } else { - return Result::ErrorNull; - } -} - -} // namespace oboe diff --git a/src/aaudio/OboeStreamAAudio.cpp b/src/aaudio/OboeStreamAAudio.cpp new file mode 100644 index 000000000..860f9368b --- /dev/null +++ b/src/aaudio/OboeStreamAAudio.cpp @@ -0,0 +1,260 @@ +/* + * Copyright 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "oboe/OboeUtilities.h" +#include "common/OboeDebug.h" +#include "aaudio/AAudioLoader.h" +#include "aaudio/OboeStreamAAudio.h" + +AAudioLoader *OboeStreamAAudio::mLibLoader = nullptr; + +/* + * Create a stream that uses Oboe Audio API. + */ +OboeStreamAAudio::OboeStreamAAudio(const OboeStreamBuilder &builder) + : OboeStream(builder) + , mFloatCallbackBuffer(nullptr) + , mShortCallbackBuffer(nullptr) + , mAAudioStream(nullptr) +{ + mCallbackThreadEnabled.store(false); + LOGD("OboeStreamAAudio() call isSupported()"); + isSupported(); +} + +OboeStreamAAudio::~OboeStreamAAudio() +{ + delete[] mFloatCallbackBuffer; + delete[] mShortCallbackBuffer; +} + +bool OboeStreamAAudio::isSupported() { + mLibLoader = AAudioLoader::getInstance(); + int openResult = mLibLoader->open(); + return openResult == 0; +} + +// 'C' wrapper for the data callback method +static aaudio_data_callback_result_t oboe_aaudio_data_callback_proc( + AAudioStream *stream, + void *userData, + void *audioData, + int32_t numFrames) { + + OboeStreamAAudio *oboeStream = (OboeStreamAAudio *)userData; + if (oboeStream != NULL) { + return oboeStream->callOnAudioReady(stream, audioData, numFrames); + } else { + return AAUDIO_CALLBACK_RESULT_STOP; + } +} + +// 'C' wrapper for the error callback method +static void oboe_aaudio_error_callback_proc( + AAudioStream *stream, + void *userData, + aaudio_result_t error) { + + OboeStreamAAudio *oboeStream = (OboeStreamAAudio *)userData; + if (oboeStream != NULL) { + oboeStream->callOnError(stream, error); + } +} + +oboe_result_t OboeStreamAAudio::open() { + oboe_result_t result = AAUDIO_OK; + + if (mAAudioStream != nullptr) { + return AAUDIO_ERROR_INVALID_STATE; + } + + result = OboeStream::open(); + if (result != AAUDIO_OK) { + return result; + } + + LOGD("OboeStreamAAudio(): AAudio_createStreamBuilder()"); + AAudioStreamBuilder *aaudioBuilder; + result = mLibLoader->createStreamBuilder(&aaudioBuilder); + if (result != AAUDIO_OK) { + return result; + } + + LOGD("OboeStreamAAudio.open() try with deviceId = %d", (int) mDeviceId); + mLibLoader->builder_setBufferCapacityInFrames(aaudioBuilder, mBufferCapacityInFrames); + mLibLoader->builder_setChannelCount(aaudioBuilder, mChannelCount); + mLibLoader->builder_setDeviceId(aaudioBuilder, mDeviceId); + mLibLoader->builder_setDirection(aaudioBuilder, mDirection); + mLibLoader->builder_setFormat(aaudioBuilder, mFormat); + mLibLoader->builder_setSampleRate(aaudioBuilder, mSampleRate); + mLibLoader->builder_setSharingMode(aaudioBuilder, mSharingMode); + mLibLoader->builder_setPerformanceMode(aaudioBuilder, mPerformanceMode); + + // TODO get more parameters from the builder + + if (mStreamCallback != nullptr) { + mLibLoader->builder_setDataCallback(aaudioBuilder, oboe_aaudio_data_callback_proc, this); + mLibLoader->builder_setErrorCallback(aaudioBuilder, oboe_aaudio_error_callback_proc, this); + mLibLoader->builder_setFramesPerDataCallback(aaudioBuilder, getFramesPerCallback()); + } + + result = mLibLoader->builder_openStream(aaudioBuilder, &mAAudioStream); + if (result != AAUDIO_OK) { + goto error2; + } + + // Query and cache the values that will not change. + mDeviceId = mLibLoader->stream_getDeviceId(mAAudioStream); + mChannelCount = mLibLoader->stream_getChannelCount(mAAudioStream); + mSampleRate = mLibLoader->stream_getSampleRate(mAAudioStream); + mNativeFormat = mLibLoader->stream_getFormat(mAAudioStream); + if (mFormat == OBOE_AUDIO_FORMAT_UNSPECIFIED) { + mFormat = mNativeFormat; + } + mSharingMode = mLibLoader->stream_getSharingMode(mAAudioStream); + mPerformanceMode = mLibLoader->stream_getPerformanceMode(mAAudioStream); + mBufferCapacityInFrames = getBufferCapacityInFrames(); + + LOGD("OboeStreamAAudio.open() app format = %d", (int) mFormat); + LOGD("OboeStreamAAudio.open() native format = %d", (int) mNativeFormat); + LOGD("OboeStreamAAudio.open() sample rate = %d", (int) mSampleRate); + +error2: + mLibLoader->builder_delete(aaudioBuilder); + LOGD("OboeStreamAAudio.open: AAudioStream_Open() returned %s, mAAudioStream = %p", + mLibLoader->convertResultToText(result), mAAudioStream); + return result; +} + +oboe_result_t OboeStreamAAudio::close() +{ + oboe_result_t result = mLibLoader->stream_close(mAAudioStream); + mAAudioStream = nullptr; + return result; +} + +aaudio_data_callback_result_t OboeStreamAAudio::callOnAudioReady(AAudioStream *stream, + void *audioData, + int32_t numFrames) { + return mStreamCallback->onAudioReady( + this, + audioData, + numFrames); +} + +void OboeStreamAAudio::callOnError(AAudioStream *stream, oboe_result_t error) { + mStreamCallback->onError( this, error); +} + +oboe_result_t OboeStreamAAudio::convertApplicationDataToNative(int32_t numFrames) { + oboe_result_t result = OBOE_ERROR_UNIMPLEMENTED; + int32_t numSamples = numFrames * getChannelCount(); + if (mFormat == OBOE_AUDIO_FORMAT_PCM_FLOAT) { + if (mNativeFormat == OBOE_AUDIO_FORMAT_PCM_I16) { + Oboe_convertFloatToPcm16(mFloatCallbackBuffer, mShortCallbackBuffer, numSamples); + result = AAUDIO_OK; + } + } else if (mFormat == OBOE_AUDIO_FORMAT_PCM_I16) { + if (mNativeFormat == OBOE_AUDIO_FORMAT_PCM_FLOAT) { + Oboe_convertPcm16ToFloat(mShortCallbackBuffer, mFloatCallbackBuffer, numSamples); + result = AAUDIO_OK; + } + } + return result; +} + +oboe_result_t OboeStreamAAudio::requestStart() +{ + return mLibLoader->stream_requestStart(mAAudioStream); +} + +oboe_result_t OboeStreamAAudio::requestPause() +{ + return mLibLoader->stream_requestPause(mAAudioStream); +} + +oboe_result_t OboeStreamAAudio::requestFlush() { + return mLibLoader->stream_requestFlush(mAAudioStream); +} + +oboe_result_t OboeStreamAAudio::requestStop() +{ + return mLibLoader->stream_requestStop(mAAudioStream); +} + +oboe_result_t OboeStreamAAudio::write(const void *buffer, + int32_t numFrames, + int64_t timeoutNanoseconds) +{ + return mLibLoader->stream_write(mAAudioStream, buffer, numFrames, timeoutNanoseconds); +} + +oboe_result_t OboeStreamAAudio::waitForStateChange(oboe_stream_state_t currentState, + oboe_stream_state_t *nextState, + int64_t timeoutNanoseconds) +{ + return mLibLoader->stream_waitForStateChange(mAAudioStream, currentState, + nextState, timeoutNanoseconds); +} + +oboe_result_t OboeStreamAAudio::setBufferSizeInFrames(int32_t requestedFrames) +{ + return mLibLoader->stream_setBufferSize(mAAudioStream, requestedFrames); +} + +oboe_stream_state_t OboeStreamAAudio::getState() +{ + if (mAAudioStream == nullptr) { + return OBOE_STREAM_STATE_CLOSED; + } + return mLibLoader->stream_getState(mAAudioStream); +} + +int32_t OboeStreamAAudio::getBufferSizeInFrames() const { + return mLibLoader->stream_getBufferSize(mAAudioStream); +} + +int32_t OboeStreamAAudio::getBufferCapacityInFrames() const { + return mLibLoader->stream_getBufferCapacity(mAAudioStream); +} + +int32_t OboeStreamAAudio::getFramesPerBurst() +{ + return mLibLoader->stream_getFramesPerBurst(mAAudioStream); +} + +int64_t OboeStreamAAudio::getFramesRead() +{ + return mLibLoader->stream_getFramesRead(mAAudioStream); +} +int64_t OboeStreamAAudio::getFramesWritten() +{ + return mLibLoader->stream_getFramesWritten(mAAudioStream); +} + +int32_t OboeStreamAAudio::getXRunCount() +{ + return mLibLoader->stream_getXRunCount(mAAudioStream); +} + +oboe_result_t OboeStreamAAudio::getTimestamp(clockid_t clockId, + int64_t *framePosition, + int64_t *timeNanoseconds) { + return mLibLoader->stream_getTimestamp(mAAudioStream, clockId, + framePosition, timeNanoseconds); +} diff --git a/src/aaudio/AudioStreamAAudio.h b/src/aaudio/OboeStreamAAudio.h similarity index 50% rename from src/aaudio/AudioStreamAAudio.h rename to src/aaudio/OboeStreamAAudio.h index 9c5c40658..fc55975f2 100644 --- a/src/aaudio/AudioStreamAAudio.h +++ b/src/aaudio/OboeStreamAAudio.h @@ -14,20 +14,16 @@ * limitations under the License. */ -#ifndef OBOE_STREAM_AAUDIO_H_ -#define OBOE_STREAM_AAUDIO_H_ +#ifndef OBOE_OBOE_STREAM_AAUDIO_H_ +#define OBOE_OBOE_STREAM_AAUDIO_H_ #include -#include -#include -#include "aaudio/AAudio.h" - -#include "oboe/AudioStreamBuilder.h" -#include "oboe/AudioStream.h" -#include "oboe/Definitions.h" +#include "oboe/OboeStreamBuilder.h" +#include "oboe/OboeStream.h" +#include "oboe/OboeDefinitions.h" -namespace oboe { +#include "aaudio/AAudio.h" class AAudioLoader; @@ -37,12 +33,12 @@ class AAudioLoader; * Do not create this class directly. * Use an OboeStreamBuilder to create one. */ -class AudioStreamAAudio : public AudioStream { +class OboeStreamAAudio : public OboeStream { public: - AudioStreamAAudio(); - explicit AudioStreamAAudio(const AudioStreamBuilder &builder); + OboeStreamAAudio(); + explicit OboeStreamAAudio(const OboeStreamBuilder &builder); - ~AudioStreamAAudio(); + ~OboeStreamAAudio(); /** * @@ -50,37 +46,38 @@ class AudioStreamAAudio : public AudioStream { */ static bool isSupported(); - // These functions override methods in AudioStream. - // See AudioStream for documentation. - Result open() override; - Result close() override; + // These functions override methods in OboeStream. + // See OboeStream for documentation. + oboe_result_t open() override; + oboe_result_t close() override; - Result requestStart() override; - Result requestPause() override; - Result requestFlush() override; - Result requestStop() override; + oboe_result_t requestStart() override; + oboe_result_t requestPause() override; + oboe_result_t requestFlush() override; + oboe_result_t requestStop() override; - int32_t write(const void *buffer, + oboe_result_t write(const void *buffer, int32_t numFrames, int64_t timeoutNanoseconds) override; - Result setBufferSizeInFrames(int32_t requestedFrames) override; + oboe_result_t setBufferSizeInFrames(int32_t requestedFrames) override; int32_t getBufferSizeInFrames() const override; + int32_t getBufferCapacityInFrames() const override; int32_t getFramesPerBurst() override; int32_t getXRunCount() override; int64_t getFramesRead() override; int64_t getFramesWritten() override; - Result waitForStateChange(StreamState currentState, - StreamState *nextState, - int64_t timeoutNanoseconds) override; + oboe_result_t waitForStateChange(oboe_stream_state_t currentState, + oboe_stream_state_t *nextState, + int64_t timeoutNanoseconds) override; - Result getTimestamp(clockid_t clockId, + oboe_result_t getTimestamp(clockid_t clockId, int64_t *framePosition, int64_t *timeNanoseconds) override; - StreamState getState() override; + oboe_stream_state_t getState() override; bool usesAAudio() const override { @@ -88,30 +85,22 @@ class AudioStreamAAudio : public AudioStream { } public: - DataCallbackResult callOnAudioReady(AAudioStream *stream, + aaudio_data_callback_result_t callOnAudioReady(AAudioStream *stream, void *audioData, int32_t numFrames); - - void onErrorCallback(AAudioStream *stream, Result error); - - void onErrorInThread(AAudioStream *stream, Result error); + void callOnError(AAudioStream *stream, oboe_result_t error); protected: - Result convertApplicationDataToNative(int32_t numFrames); // TODO remove? + oboe_result_t convertApplicationDataToNative(int32_t numFrames); private: - - float *mFloatCallbackBuffer; - int16_t *mShortCallbackBuffer; - std::atomic mCallbackThreadEnabled; - std::thread *mErrorHandlingThread = nullptr; - - std::mutex mLock; // for synchronizing start/stop/close - std::atomic mAAudioStream{nullptr}; + float *mFloatCallbackBuffer; + int16_t *mShortCallbackBuffer; + std::atomic mCallbackThreadEnabled; + AAudioStream *mAAudioStream; static AAudioLoader *mLibLoader; }; -} // namespace oboe -#endif // OBOE_STREAM_AAUDIO_H_ +#endif // OBOE_OBOE_STREAM_AAUDIO_H_ diff --git a/src/aaudio/build.gradle b/src/aaudio/build.gradle new file mode 100644 index 000000000..f32ecc8ae --- /dev/null +++ b/src/aaudio/build.gradle @@ -0,0 +1,3 @@ +dependencies { + compile 'com.android.support.constraint:constraint-layout:1.0.2' +} diff --git a/src/common/AudioClock.h b/src/common/AudioClock.h index 3fe20cb0a..a9bbd0a8e 100644 --- a/src/common/AudioClock.h +++ b/src/common/AudioClock.h @@ -18,12 +18,9 @@ #define OBOE_AUDIO_CLOCK_H #include -#include -#include "oboe/Definitions.h" +#include +#include "oboe/OboeDefinitions.h" -namespace oboe { - -// TODO: Move this class into the public headers because it is useful when calculating stream latency class AudioClock { public: static int64_t getNanoseconds(clockid_t clockId = CLOCK_MONOTONIC) { @@ -32,7 +29,7 @@ class AudioClock { if (result < 0) { return result; } - return (time.tv_sec * kNanosPerSecond) + time.tv_nsec; + return (time.tv_sec * OBOE_NANOS_PER_SECOND) + time.tv_nsec; } /** @@ -45,8 +42,8 @@ class AudioClock { static int sleepUntilNanoTime(int64_t nanoTime, clockid_t clockId = CLOCK_MONOTONIC) { struct timespec time; - time.tv_sec = nanoTime / kNanosPerSecond; - time.tv_nsec = nanoTime - (time.tv_sec * kNanosPerSecond); + time.tv_sec = nanoTime / OBOE_NANOS_PER_SECOND; + time.tv_nsec = nanoTime - (time.tv_sec * OBOE_NANOS_PER_SECOND); return 0 - clock_nanosleep(clockId, TIMER_ABSTIME, &time, NULL); } @@ -62,14 +59,13 @@ class AudioClock { static int sleepForNanos(int64_t nanoseconds, clockid_t clockId = CLOCK_REALTIME) { if (nanoseconds > 0) { struct timespec time; - time.tv_sec = nanoseconds / kNanosPerSecond; - time.tv_nsec = nanoseconds - (time.tv_sec * kNanosPerSecond); + time.tv_sec = nanoseconds / OBOE_NANOS_PER_SECOND; + time.tv_nsec = nanoseconds - (time.tv_sec * OBOE_NANOS_PER_SECOND); return 0 - clock_nanosleep(clockId, 0, &time, NULL); } return 0; } }; -} // namespace oboe #endif //OBOE_AUDIO_CLOCK_H diff --git a/src/common/AudioStream.cpp b/src/common/AudioStream.cpp deleted file mode 100644 index 9ece16682..000000000 --- a/src/common/AudioStream.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include "OboeDebug.h" -#include - -namespace oboe { - -/* - * AudioStream - */ -AudioStream::AudioStream(const AudioStreamBuilder &builder) - : AudioStreamBase(builder) { -} - -Result AudioStream::open() { - // TODO validate parameters or let underlyng API validate them? - return Result::OK; -} - -Result AudioStream::fireCallback(void *audioData, int32_t numFrames) -{ - int scheduler = sched_getscheduler(0) & ~SCHED_RESET_ON_FORK; // for current thread - if (scheduler != mPreviousScheduler) { - LOGD("AudioStream::fireCallback() scheduler = %s", - ((scheduler == SCHED_FIFO) ? "SCHED_FIFO" : - ((scheduler == SCHED_OTHER) ? "SCHED_OTHER" : - ((scheduler == SCHED_RR) ? "SCHED_RR" : "UNKNOWN"))) - ); - mPreviousScheduler = scheduler; - } - if (mStreamCallback == nullptr) { - return Result::ErrorNull; - } else { - /** - * TODO: onAudioRead doesn't return an Result, it returns either Continue or Stop - * neither of which tells us whether an error occured. Figure out what to do here. - */ - /*Result result = mStreamCallback->onAudioReady(this, audioData, numFrames); - if (result == OBOE_OK) { - mFramesWritten += numFrames; - }*/ - mStreamCallback->onAudioReady(this, audioData, numFrames); - mFramesWritten += numFrames; - return Result::OK; - } -} - -Result AudioStream::waitForStateTransition(StreamState startingState, - StreamState endingState, - int64_t timeoutNanoseconds) -{ - StreamState state = getState(); - StreamState nextState = state; - if (state == startingState && state != endingState) { - Result result = waitForStateChange(state, &nextState, timeoutNanoseconds); - if (result != Result::OK) { - return result; - } - } - if (nextState != endingState) { - return Result::ErrorInvalidState; - } else { - return Result::OK; - } -} - -Result AudioStream::start(int64_t timeoutNanoseconds) -{ - Result result = requestStart(); - if (result != Result::OK) return result; - return waitForStateTransition(StreamState::Starting, - StreamState::Started, timeoutNanoseconds); -} - -Result AudioStream::pause(int64_t timeoutNanoseconds) -{ - Result result = requestPause(); - if (result != Result::OK) return result; - return waitForStateTransition(StreamState::Pausing, - StreamState::Paused, timeoutNanoseconds); -} - -Result AudioStream::flush(int64_t timeoutNanoseconds) -{ - Result result = requestFlush(); - if (result != Result::OK) return result; - return waitForStateTransition(StreamState::Flushing, - StreamState::Flushed, timeoutNanoseconds); -} - -Result AudioStream::stop(int64_t timeoutNanoseconds) -{ - Result result = requestStop(); - if (result != Result::OK) return result; - return waitForStateTransition(StreamState::Stopping, - StreamState::Stopped, timeoutNanoseconds); -} - -bool AudioStream::isPlaying() { - StreamState state = getState(); - return state == StreamState::Starting || state == StreamState::Started; -} - -int32_t AudioStream::getBytesPerSample() const { - return convertFormatToSizeInBytes(mFormat); -} - -} // namespace oboe \ No newline at end of file diff --git a/src/common/AudioStreamBuilder.cpp b/src/common/AudioStreamBuilder.cpp deleted file mode 100644 index a3582e889..000000000 --- a/src/common/AudioStreamBuilder.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "aaudio/AudioStreamAAudio.h" -#include "OboeDebug.h" -#include "oboe/Oboe.h" -#include "oboe/AudioStreamBuilder.h" -#include "opensles/AudioInputStreamOpenSLES.h" -#include "opensles/AudioOutputStreamOpenSLES.h" -#include "opensles/AudioStreamOpenSLES.h" - -namespace oboe { - -bool AudioStreamBuilder::isAAudioSupported() { - return AudioStreamAAudio::isSupported(); -} - -AudioStream *AudioStreamBuilder::build() { - LOGD("AudioStreamBuilder.build(): mAudioApi %d, mChannelCount = %d, mFramesPerCallback = %d", - mAudioApi, mChannelCount, mFramesPerCallback); - AudioStream *stream = nullptr; - switch(mAudioApi) { - case AudioApi::Unspecified: - case AudioApi::AAudio: - if (AudioStreamAAudio::isSupported()) { - stream = new AudioStreamAAudio(*this); - break; - } - // fall into using older existing API - case AudioApi::OpenSLES: - if (getDirection() == oboe::Direction::Output) { - stream = new AudioOutputStreamOpenSLES(*this); - } else if (getDirection() == oboe::Direction::Input) { - stream = new AudioInputStreamOpenSLES(*this); - } - break; - } - return stream; -} - -Result AudioStreamBuilder::openStream(AudioStream **streamPP) { - if (streamPP == nullptr) { - return Result::ErrorNull; - } - *streamPP = nullptr; - AudioStream *streamP = build(); - if (streamP == nullptr) { - return Result::ErrorNull; - } - Result result = streamP->open(); // TODO review API - if (result == Result::OK) { - *streamPP = streamP; - } - return result; -} - -} // namespace oboe \ No newline at end of file diff --git a/src/common/OboeDebug.h b/src/common/OboeDebug.h index bc72c93cb..d998ecd57 100644 --- a/src/common/OboeDebug.h +++ b/src/common/OboeDebug.h @@ -14,8 +14,8 @@ * limitations under the License. * */ -#ifndef OBOE_DEBUG_H -#define OBOE_DEBUG_H +#ifndef OBOE_OBOE_DEBUG_H +#define OBOE_OBOE_DEBUG_H #include #if 1 @@ -40,4 +40,4 @@ #define LOGF(...) #endif -#endif //OBOE_DEBUG_H +#endif //OBOE_OBOE_DEBUG_H diff --git a/src/common/LatencyTuner.cpp b/src/common/OboeLatencyTuner.cpp similarity index 62% rename from src/common/LatencyTuner.cpp rename to src/common/OboeLatencyTuner.cpp index b28ef667c..5cac2f818 100644 --- a/src/common/LatencyTuner.cpp +++ b/src/common/OboeLatencyTuner.cpp @@ -14,21 +14,19 @@ * limitations under the License. */ -#include "oboe/LatencyTuner.h" +#include "oboe/OboeLatencyTuner.h" -using namespace oboe; - -LatencyTuner::LatencyTuner(AudioStream &stream) +OboeLatencyTuner::OboeLatencyTuner(OboeStream &stream) : mStream(stream) { reset(); } -Result LatencyTuner::tune() { - if (mState == State::Unsupported) { - return Result::ErrorUnimplemented; +oboe_result_t OboeLatencyTuner::tune() { + if (mState == STATE_UNSUPPORTED) { + return OBOE_ERROR_UNIMPLEMENTED; } - Result result = Result::OK; + oboe_result_t result = OBOE_OK; // Process reset requests. int32_t numRequests = mLatencyTriggerRequests.load(); @@ -38,50 +36,49 @@ Result LatencyTuner::tune() { } switch (mState) { - case State::Idle: + case STATE_IDLE: if (--mIdleCountDown <= 0) { - mState = State::Active; + mState = STATE_ACTIVE; } mPreviousXRuns = mStream.getXRunCount(); if (mPreviousXRuns < 0) { - result = static_cast(mPreviousXRuns); // error code - mState = State::Unsupported; + result = mPreviousXRuns; // error code + mState = STATE_UNSUPPORTED; } break; - case State::Active: { + case STATE_ACTIVE: { int32_t xRuns = mStream.getXRunCount(); if ((xRuns - mPreviousXRuns) > 0) { mPreviousXRuns = xRuns; int32_t oldBufferSize = mStream.getBufferSizeInFrames(); int32_t requestedBufferSize = oldBufferSize + mStream.getFramesPerBurst(); - int32_t resultingSize = static_cast( - mStream.setBufferSizeInFrames(requestedBufferSize)); + int32_t resultingSize = mStream.setBufferSizeInFrames(requestedBufferSize); if (resultingSize == oldBufferSize) { - mState = State::AtMax; // can't go any higher + mState = STATE_AT_MAX; // can't go any higher } else if (resultingSize < 0) { - result = static_cast(resultingSize); // error code - mState = State::Unsupported; + result = resultingSize; // error code + mState = STATE_UNSUPPORTED; } } } - case State::AtMax: - case State::Unsupported: + case STATE_AT_MAX: + case STATE_UNSUPPORTED: break; } return result; } -void LatencyTuner::requestReset() { - if (mState != State::Unsupported) { +void OboeLatencyTuner::requestReset() { + if (mState != STATE_UNSUPPORTED) { mLatencyTriggerRequests++; } } -void LatencyTuner::reset() { - mState = State::Idle; - mIdleCountDown = kIdleCount; +void OboeLatencyTuner::reset() { + mState = STATE_IDLE; + mIdleCountDown = IDLE_COUNT; // Set to minimal latency mStream.setBufferSizeInFrames(mStream.getFramesPerBurst()); } diff --git a/src/common/OboeStream.cpp b/src/common/OboeStream.cpp new file mode 100644 index 000000000..5262e42cb --- /dev/null +++ b/src/common/OboeStream.cpp @@ -0,0 +1,117 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "oboe/OboeUtilities.h" +#include "OboeDebug.h" +#include "oboe/Oboe.h" + +/* + * OboeStream + */ + +OboeStream::OboeStream(const OboeStreamBuilder &builder) + : OboeStreamBase(builder) { +} + +oboe_result_t OboeStream::open() { + // TODO validate parameters or let underlyng API validate them? + return OBOE_OK; +} + +oboe_result_t OboeStream::fireCallback(void *audioData, int32_t numFrames) +{ + int scheduler = sched_getscheduler(0) & ~SCHED_RESET_ON_FORK; // for current thread + if (scheduler != mPreviousScheduler) { + LOGD("OboeStream::fireCallback() scheduler = %s", + ((scheduler == SCHED_FIFO) ? "SCHED_FIFO" : + ((scheduler == SCHED_OTHER) ? "SCHED_OTHER" : + ((scheduler == SCHED_RR) ? "SCHED_RR" : "UNKNOWN"))) + ); + mPreviousScheduler = scheduler; + } + if (mStreamCallback == NULL) { + return OBOE_ERROR_NULL; + } else { + oboe_result_t result = mStreamCallback->onAudioReady(this, audioData, numFrames); + if (result == OBOE_OK) { + mFramesWritten += numFrames; + } + return result; + } +} + +oboe_result_t OboeStream::waitForStateTransition(oboe_stream_state_t startingState, + oboe_stream_state_t endingState, + int64_t timeoutNanoseconds) +{ + oboe_stream_state_t state = getState(); + oboe_stream_state_t nextState = state; + if (state == startingState && state != endingState) { + oboe_result_t result = waitForStateChange(state, &nextState, timeoutNanoseconds); + if (result < 0) { + return result; + } + } + if (nextState != endingState) { + return OBOE_ERROR_INVALID_STATE; + } else { + return OBOE_OK; + } +} + +oboe_result_t OboeStream::start(int64_t timeoutNanoseconds) +{ + oboe_result_t result = requestStart(); + if (result < 0) return result; + return waitForStateTransition(OBOE_STREAM_STATE_STARTING, + OBOE_STREAM_STATE_STARTED, timeoutNanoseconds); +} + +oboe_result_t OboeStream::pause(int64_t timeoutNanoseconds) +{ + oboe_result_t result = requestPause(); + if (result < 0) return result; + return waitForStateTransition(OBOE_STREAM_STATE_PAUSING, + OBOE_STREAM_STATE_PAUSED, timeoutNanoseconds); +} + +oboe_result_t OboeStream::flush(int64_t timeoutNanoseconds) +{ + oboe_result_t result = requestFlush(); + if (result < 0) return result; + return waitForStateTransition(OBOE_STREAM_STATE_FLUSHING, + OBOE_STREAM_STATE_FLUSHED, timeoutNanoseconds); +} + +oboe_result_t OboeStream::stop(int64_t timeoutNanoseconds) +{ + oboe_result_t result = requestStop(); + if (result < 0) return result; + return waitForStateTransition(OBOE_STREAM_STATE_STOPPING, + OBOE_STREAM_STATE_STOPPED, timeoutNanoseconds); +} + +bool OboeStream::isPlaying() { + oboe_stream_state_t state = getState(); + return state == OBOE_STREAM_STATE_STARTING || state == OBOE_STREAM_STATE_STARTED; +} + +int32_t OboeStream::getBytesPerSample() const { + return Oboe_convertFormatToSizeInBytes(mFormat); +} diff --git a/src/common/OboeStreamBuilder.cpp b/src/common/OboeStreamBuilder.cpp new file mode 100644 index 000000000..aa184c77e --- /dev/null +++ b/src/common/OboeStreamBuilder.cpp @@ -0,0 +1,62 @@ +/* + * Copyright 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "OboeDebug.h" +#include "oboe/Oboe.h" + +#include "opensles/OboeStreamOpenSLES.h" +#include "aaudio/OboeStreamAAudio.h" + +bool OboeStreamBuilder::isAAudioSupported() { + return OboeStreamAAudio::isSupported(); +} + +OboeStream *OboeStreamBuilder::build() { + LOGD("OboeStreamBuilder.build(): mAudioApi %d, mChannelCount = %d, mFramesPerCallback = %d", + mAudioApi, mChannelCount, mFramesPerCallback); + OboeStream *stream = nullptr; + switch(mAudioApi) { + case API_UNSPECIFIED: + case API_AAUDIO: + if (OboeStreamAAudio::isSupported()) { + stream = new OboeStreamAAudio(*this); + break; + } + // fall into using older existing API + case API_OPENSL_ES: + stream = new OboeStreamOpenSLES(*this); + break; + } + return stream; +} + +oboe_result_t OboeStreamBuilder::openStream(OboeStream **streamPP) { + if (streamPP == nullptr) { + return OBOE_ERROR_NULL; + } + *streamPP = nullptr; + OboeStream *streamP = build(); + if (streamP == nullptr) { + return OBOE_ERROR_NULL; + } + oboe_result_t result = streamP->open(); // TODO review API + if (result == OBOE_OK) { + *streamPP = streamP; + } + return result; +} diff --git a/src/common/OboeUtilities.cpp b/src/common/OboeUtilities.cpp new file mode 100644 index 000000000..4b8b83924 --- /dev/null +++ b/src/common/OboeUtilities.cpp @@ -0,0 +1,127 @@ +/* + * Copyright 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include "oboe/OboeDefinitions.h" +#include "oboe/OboeUtilities.h" + +#define OBOE_CASE_ENUM(name) case name: return #name + +void Oboe_convertFloatToPcm16(const float *source, int16_t *destination, int32_t numSamples) { + for (int i = 0; i < numSamples; i++) { + float fval = source[i]; + fval += 1.0; // to avoid discontinuity at 0.0 caused by truncation + fval *= 32768.0f; + int32_t sample = (int32_t) fval; + // clip to 16-bit range + if (sample < 0) sample = 0; + else if (sample > 0x0FFFF) sample = 0x0FFFF; + sample -= 32768; // center at zero + destination[i] = (int16_t) sample; + } +} + +void Oboe_convertPcm16ToFloat(const int16_t *source, float *destination, int32_t numSamples) { + for (int i = 0; i < numSamples; i++) { + destination[i] = source[i] * (1.0f / 32768.0f); + } +} + +int32_t Oboe_convertFormatToSizeInBytes(oboe_audio_format_t format) { + int32_t size = OBOE_ERROR_ILLEGAL_ARGUMENT; + switch (format) { + case OBOE_AUDIO_FORMAT_PCM_I16: + size = sizeof(int16_t); + break; + case OBOE_AUDIO_FORMAT_PCM_FLOAT: + size = sizeof(float); + break; + default: + break; + } + return size; +} + +const char *Oboe_convertResultToText(oboe_result_t returnCode) { + switch (returnCode) { + OBOE_CASE_ENUM(OBOE_OK); + OBOE_CASE_ENUM(OBOE_ERROR_DISCONNECTED); + OBOE_CASE_ENUM(OBOE_ERROR_ILLEGAL_ARGUMENT); + // reserved + OBOE_CASE_ENUM(OBOE_ERROR_INTERNAL); + OBOE_CASE_ENUM(OBOE_ERROR_INVALID_STATE); + // reserved + // reserved + OBOE_CASE_ENUM(OBOE_ERROR_INVALID_HANDLE); + // reserved + OBOE_CASE_ENUM(OBOE_ERROR_UNIMPLEMENTED); + OBOE_CASE_ENUM(OBOE_ERROR_UNAVAILABLE); + OBOE_CASE_ENUM(OBOE_ERROR_NO_FREE_HANDLES); + OBOE_CASE_ENUM(OBOE_ERROR_NO_MEMORY); + OBOE_CASE_ENUM(OBOE_ERROR_NULL); + OBOE_CASE_ENUM(OBOE_ERROR_TIMEOUT); + OBOE_CASE_ENUM(OBOE_ERROR_WOULD_BLOCK); + OBOE_CASE_ENUM(OBOE_ERROR_INVALID_FORMAT); + OBOE_CASE_ENUM(OBOE_ERROR_OUT_OF_RANGE); + OBOE_CASE_ENUM(OBOE_ERROR_NO_SERVICE); + OBOE_CASE_ENUM(OBOE_ERROR_INVALID_RATE); + } + return "Unrecognized Oboe error."; +} + +const char *Oboe_convertAudioFormatToText(oboe_audio_format_t format) { + switch (format) { + OBOE_CASE_ENUM(OBOE_AUDIO_FORMAT_INVALID); + OBOE_CASE_ENUM(OBOE_AUDIO_FORMAT_UNSPECIFIED); + OBOE_CASE_ENUM(OBOE_AUDIO_FORMAT_PCM_I16); + OBOE_CASE_ENUM(OBOE_AUDIO_FORMAT_PCM_FLOAT); + } + return "Unrecognized audio format."; +} + +const char *Oboe_convertPerformanceModeToText(oboe_performance_mode_t mode) { + switch (mode) { + OBOE_CASE_ENUM(OBOE_PERFORMANCE_MODE_LOW_LATENCY); + OBOE_CASE_ENUM(OBOE_PERFORMANCE_MODE_NONE); + OBOE_CASE_ENUM(OBOE_PERFORMANCE_MODE_POWER_SAVING); + } + return "Unrecognised performance mode."; +} + +const char *Oboe_convertSharingModeToText(oboe_sharing_mode_t mode) { + switch (mode) { + OBOE_CASE_ENUM(OBOE_SHARING_MODE_EXCLUSIVE); + OBOE_CASE_ENUM(OBOE_SHARING_MODE_SHARED); + } + return "Unrecognised sharing mode."; +} + +const char *Oboe_convertDataCallbackResultToText(oboe_data_callback_result_t result) { + switch (result) { + OBOE_CASE_ENUM(OBOE_CALLBACK_RESULT_CONTINUE); + OBOE_CASE_ENUM(OBOE_CALLBACK_RESULT_STOP); + } + return "Unrecognised data callback result."; +} + +const char *Oboe_convertDirectionToText(oboe_direction_t direction) { + switch (direction) { + OBOE_CASE_ENUM(OBOE_DIRECTION_INPUT); + OBOE_CASE_ENUM(OBOE_DIRECTION_OUTPUT); + } + return "Unrecognised direction."; +} diff --git a/src/common/Utilities.cpp b/src/common/Utilities.cpp deleted file mode 100644 index 04dc42678..000000000 --- a/src/common/Utilities.cpp +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include -#include "oboe/Definitions.h" -#include "oboe/Utilities.h" - -namespace oboe { - -constexpr float kScaleI16ToFloat = (1.0f / 32768.0f); - -void convertFloatToPcm16(const float *source, int16_t *destination, int32_t numSamples) { - for (int i = 0; i < numSamples; i++) { - float fval = source[i]; - fval += 1.0; // to avoid discontinuity at 0.0 caused by truncation - fval *= 32768.0f; - auto sample = (int32_t) fval; - // clip to 16-bit range - if (sample < 0) sample = 0; - else if (sample > 0x0FFFF) sample = 0x0FFFF; - sample -= 32768; // center at zero - destination[i] = (int16_t) sample; - } -} - -void convertPcm16ToFloat(const int16_t *source, float *destination, int32_t numSamples) { - for (int i = 0; i < numSamples; i++) { - destination[i] = source[i] * kScaleI16ToFloat; - } -} - -int32_t convertFormatToSizeInBytes(AudioFormat format) { - int32_t size = 0; - switch (format) { - case AudioFormat::I16: - size = sizeof(int16_t); - break; - case AudioFormat::Float: - size = sizeof(float); - break; - default: - break; - } - return size; -} - -template<> -const char *convertToText(Result returnCode) { - switch (returnCode) { - case Result::OK: return "OK"; - case Result::ErrorDisconnected: return "ErrorDisconnected"; - case Result::ErrorIllegalArgument: return "ErrorIllegalArgument"; - case Result::ErrorInternal: return "ErrorInternal"; - case Result::ErrorInvalidState: return "ErrorInvalidState"; - case Result::ErrorInvalidHandle: return "ErrorInvalidHandle"; - case Result::ErrorUnimplemented: return "ErrorUnimplemented"; - case Result::ErrorUnavailable: return "ErrorUnavailable"; - case Result::ErrorNoFreeHandles: return "ErrorNoFreeHandles"; - case Result::ErrorNoMemory: return "ErrorNoMemory"; - case Result::ErrorNull: return "ErrorNull"; - case Result::ErrorTimeout: return "ErrorTimeout"; - case Result::ErrorWouldBlock: return "ErrorWouldBlock"; - case Result::ErrorInvalidFormat: return "ErrorInvalidFormat"; - case Result::ErrorOutOfRange: return "ErrorOutOfRange"; - case Result::ErrorNoService: return "ErrorNoService"; - case Result::ErrorInvalidRate: return "ErrorInvalidRate"; - default: return "Unrecognized result"; - } -} - -template<> -const char *convertToText(AudioFormat format) { - switch (format) { - case AudioFormat::Invalid: return "Invalid"; - case AudioFormat::Unspecified: return "Unspecified"; - case AudioFormat::I16: return "I16"; - case AudioFormat::Float: return "Float"; - default: return "Unrecognized format"; - } -} - -template<> -const char *convertToText(PerformanceMode mode) { - switch (mode) { - case PerformanceMode::LowLatency: return "LowLatency"; - case PerformanceMode::None: return "None"; - case PerformanceMode::PowerSaving: return "PowerSaving"; - default: return "Unrecognized performance mode"; - } -} - -template<> -const char *convertToText(SharingMode mode) { - switch (mode) { - case SharingMode::Exclusive: return "Exclusive"; - case SharingMode::Shared: return "Shared"; - default: return "Unrecognized sharing mode"; - } -} - -template<> -const char *convertToText(DataCallbackResult result) { - switch (result) { - case DataCallbackResult::Continue: return "Continue"; - case DataCallbackResult::Stop: return "Stop"; - default: return "Unrecognized data callback result"; - } -} - -template<> -const char *convertToText(Direction direction) { - switch (direction) { - case Direction::Input: return "Input"; - case Direction::Output: return "Output"; - default: return "Unrecognized direction"; - } -} - -template<> -const char *convertToText(StreamState state) { - switch (state) { - case StreamState::Closed: return "Closed"; - case StreamState::Closing: return "Closing"; - case StreamState::Disconnected: return "Disconnected"; - case StreamState::Flushed: return "Flushed"; - case StreamState::Flushing: return "Flushing"; - case StreamState::Open: return "Open"; - case StreamState::Paused: return "Paused"; - case StreamState::Pausing: return "Pausing"; - case StreamState::Started: return "Started"; - case StreamState::Starting: return "Starting"; - case StreamState::Stopped: return "Stopped"; - case StreamState::Stopping: return "Stopping"; - case StreamState::Uninitialized: return "Uninitialized"; - case StreamState::Unknown: return "Unknown"; - default: return "Unrecognized stream state"; - } -} - -template<> -const char *convertToText(AudioApi audioApi) { - - switch (audioApi) { - case AudioApi::Unspecified: return "Unspecified"; - case AudioApi::OpenSLES: return "OpenSLES"; - case AudioApi::AAudio: return "AAudio"; - default: return "Unrecognised audio API"; - } -} - -}// namespace oboe \ No newline at end of file diff --git a/src/fifo/FifoBuffer.cpp b/src/fifo/FifoBuffer.cpp index 030e57df1..24137d4a0 100644 --- a/src/fifo/FifoBuffer.cpp +++ b/src/fifo/FifoBuffer.cpp @@ -16,7 +16,6 @@ #include #include -#include #include "common/OboeDebug.h" #include "fifo/FifoControllerBase.h" @@ -25,8 +24,6 @@ #include "fifo/FifoBuffer.h" #include "common/AudioClock.h" -namespace oboe { - FifoBuffer::FifoBuffer(uint32_t bytesPerFrame, uint32_t capacityInFrames) : mFrameCapacity(capacityInFrames) , mBytesPerFrame(bytesPerFrame) @@ -177,7 +174,7 @@ int64_t FifoBuffer::getNextReadTime(int frameRate) { if (mReadAtNanoseconds == 0) { return 0; } - int64_t nanosPerBuffer = (kNanosPerSecond * mLastReadSize) / frameRate; + int64_t nanosPerBuffer = (OBOE_NANOS_PER_SECOND * mLastReadSize) / frameRate; return mReadAtNanoseconds + nanosPerBuffer; } @@ -192,5 +189,3 @@ uint32_t FifoBuffer::getBufferCapacityInFrames() const { void FifoBuffer::setThresholdFrames(uint32_t threshold) { mFifo->setThreshold(threshold); } - -} // namespace oboe \ No newline at end of file diff --git a/src/fifo/FifoBuffer.h b/src/fifo/FifoBuffer.h index e94e53a8e..08fb4e700 100644 --- a/src/fifo/FifoBuffer.h +++ b/src/fifo/FifoBuffer.h @@ -14,17 +14,15 @@ * limitations under the License. */ -#ifndef OBOE_FIFOPROCESSOR_H -#define OBOE_FIFOPROCESSOR_H +#ifndef NATIVEOBOE_FIFOPROCESSOR_H +#define NATIVEOBOE_FIFOPROCESSOR_H #include #include #include "common/OboeDebug.h" #include "FifoControllerBase.h" -#include "oboe/Definitions.h" - -namespace oboe { +#include "oboe/OboeDefinitions.h" class FifoBuffer { public: @@ -92,6 +90,4 @@ class FifoBuffer { uint32_t mLastReadSize; }; -} // namespace oboe - -#endif //OBOE_FIFOPROCESSOR_H +#endif //NATIVEOBOE_FIFOPROCESSOR_H diff --git a/src/fifo/FifoController.cpp b/src/fifo/FifoController.cpp index c35ea877e..e3ba37346 100644 --- a/src/fifo/FifoController.cpp +++ b/src/fifo/FifoController.cpp @@ -19,8 +19,6 @@ #include "FifoControllerBase.h" #include "FifoController.h" -namespace oboe { - FifoController::FifoController(uint32_t numFrames, uint32_t threshold) : FifoControllerBase(numFrames, threshold) { @@ -30,6 +28,3 @@ FifoController::FifoController(uint32_t numFrames, uint32_t threshold) FifoController::~FifoController() { } - -} // namespace oboe - diff --git a/src/fifo/FifoController.h b/src/fifo/FifoController.h index 200a585cf..d427f4723 100644 --- a/src/fifo/FifoController.h +++ b/src/fifo/FifoController.h @@ -21,8 +21,6 @@ #include "FifoControllerBase.h" #include -namespace oboe { - /** * A FifoControllerBase with counters contained in the class. */ @@ -51,6 +49,5 @@ class FifoController : public FifoControllerBase std::atomic mWriteCounter; }; -} // namespace oboe #endif //NATIVEOBOE_FIFOCONTROLLER_H diff --git a/src/fifo/FifoControllerBase.cpp b/src/fifo/FifoControllerBase.cpp index 8e50331ba..9aedac5a5 100644 --- a/src/fifo/FifoControllerBase.cpp +++ b/src/fifo/FifoControllerBase.cpp @@ -22,8 +22,6 @@ #include "common/OboeDebug.h" -namespace oboe { - FifoControllerBase::FifoControllerBase(uint32_t totalFrames, uint32_t threshold) : mTotalFrames(totalFrames) , mThreshold(threshold) @@ -71,5 +69,3 @@ void FifoControllerBase::advanceWriteIndex(uint32_t numFrames) { void FifoControllerBase::setThreshold(uint32_t threshold) { mThreshold = threshold; } - -} // namespace oboe \ No newline at end of file diff --git a/src/fifo/FifoControllerBase.h b/src/fifo/FifoControllerBase.h index 5e5a0cab4..feb34c2b7 100644 --- a/src/fifo/FifoControllerBase.h +++ b/src/fifo/FifoControllerBase.h @@ -17,9 +17,8 @@ #ifndef NATIVEOBOE_FIFOCONTROLLERBASE_H #define NATIVEOBOE_FIFOCONTROLLERBASE_H -#include -namespace oboe { +#include /** * Manage the read/write indices of a circular buffer. @@ -90,6 +89,5 @@ class FifoControllerBase { // uint32_t mSmallMask; }; -} // namespace oboe #endif //NATIVEOBOE_FIFOCONTROLLERBASE_H diff --git a/src/fifo/FifoControllerIndirect.cpp b/src/fifo/FifoControllerIndirect.cpp index 6ef080c79..f52d831c9 100644 --- a/src/fifo/FifoControllerIndirect.cpp +++ b/src/fifo/FifoControllerIndirect.cpp @@ -17,8 +17,6 @@ #include "FifoControllerIndirect.h" -namespace oboe { - FifoControllerIndirect::FifoControllerIndirect(uint32_t numFrames, uint32_t threshold, int64_t * readCounterAddress, @@ -31,5 +29,3 @@ FifoControllerIndirect::FifoControllerIndirect(uint32_t numFrames, FifoControllerIndirect::~FifoControllerIndirect() { } - -} \ No newline at end of file diff --git a/src/fifo/FifoControllerIndirect.h b/src/fifo/FifoControllerIndirect.h index aa5d0ae47..a8bc7fd1b 100644 --- a/src/fifo/FifoControllerIndirect.h +++ b/src/fifo/FifoControllerIndirect.h @@ -20,8 +20,6 @@ #include "FifoControllerBase.h" #include -namespace oboe { - /** * A FifoControllerBase with counters external to the class. */ @@ -55,6 +53,4 @@ class FifoControllerIndirect : public FifoControllerBase { }; -} // namespace oboe - #endif //NATIVEOBOE_FIFOCONTROLLERINDIRECT_H diff --git a/src/opensles/AudioInputStreamOpenSLES.cpp b/src/opensles/AudioInputStreamOpenSLES.cpp deleted file mode 100644 index 3ff10e64f..000000000 --- a/src/opensles/AudioInputStreamOpenSLES.cpp +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include -#include - -#include "oboe/AudioStreamBuilder.h" -#include "AudioInputStreamOpenSLES.h" -#include "AudioStreamOpenSLES.h" -#include "OpenSLESUtilities.h" - -using namespace oboe; - -AudioInputStreamOpenSLES::AudioInputStreamOpenSLES(const AudioStreamBuilder &builder) - : AudioStreamOpenSLES(builder) { -} - -AudioInputStreamOpenSLES::~AudioInputStreamOpenSLES() { -} - -#define AUDIO_CHANNEL_COUNT_MAX 30u -#define SL_ANDROID_UNKNOWN_CHANNELMASK 0 - -int AudioInputStreamOpenSLES::chanCountToChanMask(int channelCount) { - // from internal sles_channel_in_mask_from_count(chanCount); - switch (channelCount) { - case 1: - return SL_SPEAKER_FRONT_LEFT; - case 2: - return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; - default: { - if (channelCount > AUDIO_CHANNEL_COUNT_MAX) { - return SL_ANDROID_UNKNOWN_CHANNELMASK; - } else { - SLuint32 bitfield = (1 << channelCount) - 1; - return SL_ANDROID_MAKE_INDEXED_CHANNEL_MASK(bitfield); - } - } - } -} - -Result AudioInputStreamOpenSLES::open() { - - Result oboeResult = AudioStreamOpenSLES::open(); - if (Result::OK != oboeResult) return oboeResult; - - SLuint32 bitsPerSample = getBytesPerSample() * kBitsPerByte; - - // configure audio sink - SLDataLocator_AndroidSimpleBufferQueue loc_bufq = { - SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, // locatorType - static_cast(mBurstsPerBuffer)}; // numBuffers - - // Define the audio data format. - SLDataFormat_PCM format_pcm = { - SL_DATAFORMAT_PCM, // formatType - (SLuint32) mChannelCount, // numChannels - (SLuint32) (mSampleRate * kMillisPerSecond), // milliSamplesPerSec - bitsPerSample, // bitsPerSample - bitsPerSample, // containerSize; - (SLuint32) chanCountToChanMask(mChannelCount), // channelMask - getDefaultByteOrder(), - }; - - SLDataSink audioSink = {&loc_bufq, &format_pcm}; - - /** - * API 21 (Lollipop) introduced support for floating-point data representation and an extended - * data format type: SLAndroidDataFormat_PCM_EX. If running on API 21+ use this newer format - * type, creating it from our original format. - */ - SLAndroidDataFormat_PCM_EX format_pcm_ex; - if (__ANDROID_API__ >= __ANDROID_API_L__) { - SLuint32 representation = OpenSLES_ConvertFormatToRepresentation(getFormat()); - // Fill in the format structure. - format_pcm_ex = OpenSLES_createExtendedFormat(format_pcm, representation); - // Use in place of the previous format. - audioSink.pFormat = &format_pcm_ex; - } - - // configure audio source - SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE, - SL_IODEVICE_AUDIOINPUT, - SL_DEFAULTDEVICEID_AUDIOINPUT, - NULL }; - SLDataSource audioSrc = {&loc_dev, NULL }; - - SLresult result = EngineOpenSLES::getInstance().createAudioRecorder(&mObjectInterface, - &audioSrc, - &audioSink); - if (SL_RESULT_SUCCESS != result) { - LOGE("createAudioRecorder() result:%s", getSLErrStr(result)); - goto error; - } - - // Configure the voice recognition preset, which has no - // signal processing, for lower latency. - SLAndroidConfigurationItf inputConfig; - result = (*mObjectInterface)->GetInterface(mObjectInterface, - SL_IID_ANDROIDCONFIGURATION, - &inputConfig); - if (SL_RESULT_SUCCESS == result) { - SLuint32 presetValue = SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION; - (*inputConfig)->SetConfiguration(inputConfig, - SL_ANDROID_KEY_RECORDING_PRESET, - &presetValue, - sizeof(SLuint32)); - } - - result = (*mObjectInterface)->Realize(mObjectInterface, SL_BOOLEAN_FALSE); - if (SL_RESULT_SUCCESS != result) { - LOGE("Realize recorder object result:%s", getSLErrStr(result)); - goto error; - } - - result = (*mObjectInterface)->GetInterface(mObjectInterface, SL_IID_RECORD, &mRecordInterface); - if (SL_RESULT_SUCCESS != result) { - LOGE("GetInterface RECORD result:%s", getSLErrStr(result)); - goto error; - } - - result = AudioStreamOpenSLES::registerBufferQueueCallback(); - if (SL_RESULT_SUCCESS != result) { - goto error; - } - - return Result::OK; - -error: - return Result::ErrorInternal; // TODO convert error from SLES to OBOE -} - -Result AudioInputStreamOpenSLES::close() { - requestStop(); - mRecordInterface = NULL; - return AudioStreamOpenSLES::close(); -} - -Result AudioInputStreamOpenSLES::setRecordState(SLuint32 newState) { - Result result = Result::OK; - LOGD("AudioInputStreamOpenSLES::setRecordState(%d)", newState); - if (mRecordInterface == NULL) { - return Result::ErrorInvalidState; - } - SLresult slResult = (*mRecordInterface)->SetRecordState(mRecordInterface, newState); - if(SL_RESULT_SUCCESS != slResult) { - LOGD("AudioInputStreamOpenSLES::setPlayState() returned %s", getSLErrStr(slResult)); - result = Result::ErrorInvalidState; // TODO review - } else { - setState(StreamState::Pausing); - } - return result; -} - -Result AudioInputStreamOpenSLES::requestStart() -{ - LOGD("AudioInputStreamOpenSLES::requestStart()"); - Result result = setRecordState(SL_RECORDSTATE_RECORDING); - if(result != Result::OK) { - result = Result::ErrorInvalidState; // TODO review - } else { - // Enqueue the first buffer so that we have data ready in the callback. - enqueueCallbackBuffer(mSimpleBufferQueueInterface); - setState(StreamState::Starting); - } - return result; -} - - -Result AudioInputStreamOpenSLES::requestPause() { - LOGD("AudioInputStreamOpenSLES::requestStop()"); - Result result = setRecordState(SL_RECORDSTATE_PAUSED); - if(result != Result::OK) { - result = Result::ErrorInvalidState; // TODO review - } else { - setState(StreamState::Pausing); - } - return result; -} - -Result AudioInputStreamOpenSLES::requestFlush() { - return Result::ErrorUnimplemented; // TODO -} - -Result AudioInputStreamOpenSLES::requestStop() { - LOGD("AudioInputStreamOpenSLES::requestStop()"); - Result result = setRecordState(SL_RECORDSTATE_STOPPED); - if(result != Result::OK) { - result = Result::ErrorInvalidState; // TODO review - } else { - setState(StreamState::Stopping); - } - return result; -} - -Result AudioInputStreamOpenSLES::waitForStateChange(StreamState currentState, - StreamState *nextState, - int64_t timeoutNanoseconds) { - LOGD("AudioInputStreamOpenSLES::waitForStateChange()"); - if (mRecordInterface == NULL) { - return Result::ErrorInvalidState; - } - return Result::ErrorUnimplemented; // TODO -} diff --git a/src/opensles/AudioInputStreamOpenSLES.h b/src/opensles/AudioInputStreamOpenSLES.h deleted file mode 100644 index 89c03d07d..000000000 --- a/src/opensles/AudioInputStreamOpenSLES.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef AUDIO_INPUT_STREAM_OPENSL_ES_H_ -#define AUDIO_INPUT_STREAM_OPENSL_ES_H_ - - -#include -#include - -#include "oboe/Oboe.h" -#include "AudioStreamOpenSLES.h" - -namespace oboe { - -/** - * INTERNAL USE ONLY - */ - -class AudioInputStreamOpenSLES : public AudioStreamOpenSLES { -public: - AudioInputStreamOpenSLES(); - explicit AudioInputStreamOpenSLES(const AudioStreamBuilder &builder); - - virtual ~AudioInputStreamOpenSLES(); - - Result open() override; - Result close() override; - - Result requestStart() override; - Result requestPause() override; - Result requestFlush() override; - Result requestStop() override; - - Result waitForStateChange(StreamState currentState, - StreamState *nextState, - int64_t timeoutNanoseconds) override; - - int chanCountToChanMask(int chanCount); - -private: - - Result setRecordState(SLuint32 newState); - - SLRecordItf mRecordInterface; -}; - -} // namespace oboe - -#endif //AUDIO_INPUT_STREAM_OPENSL_ES_H_ diff --git a/src/opensles/AudioOutputStreamOpenSLES.cpp b/src/opensles/AudioOutputStreamOpenSLES.cpp deleted file mode 100644 index 21297661e..000000000 --- a/src/opensles/AudioOutputStreamOpenSLES.cpp +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include -#include - -#include "oboe/AudioStreamBuilder.h" -#include "AudioOutputStreamOpenSLES.h" -#include "AudioStreamOpenSLES.h" -#include "OpenSLESUtilities.h" -#include "OutputMixerOpenSLES.h" - -using namespace oboe; - -AudioOutputStreamOpenSLES::AudioOutputStreamOpenSLES(const AudioStreamBuilder &builder) - : AudioStreamOpenSLES(builder) { -} - -AudioOutputStreamOpenSLES::~AudioOutputStreamOpenSLES() { -} - -// These will wind up in -constexpr int SL_ANDROID_SPEAKER_STEREO = (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT); - -constexpr int SL_ANDROID_SPEAKER_QUAD = (SL_ANDROID_SPEAKER_STEREO - | SL_SPEAKER_BACK_LEFT | SL_SPEAKER_BACK_RIGHT); - -constexpr int SL_ANDROID_SPEAKER_5DOT1 = (SL_ANDROID_SPEAKER_QUAD - | SL_SPEAKER_FRONT_CENTER | SL_SPEAKER_LOW_FREQUENCY); - -constexpr int SL_ANDROID_SPEAKER_7DOT1 = (SL_ANDROID_SPEAKER_5DOT1 | SL_SPEAKER_SIDE_LEFT - | SL_SPEAKER_SIDE_RIGHT); - -int AudioOutputStreamOpenSLES::chanCountToChanMask(int chanCount) { - int channelMask = 0; - - switch (chanCount) { - case 1: - channelMask = SL_SPEAKER_FRONT_CENTER; - break; - - case 2: - channelMask = SL_ANDROID_SPEAKER_STEREO; - break; - - case 4: // Quad - channelMask = SL_ANDROID_SPEAKER_QUAD; - break; - - case 6: // 5.1 - channelMask = SL_ANDROID_SPEAKER_5DOT1; - break; - - case 8: // 7.1 - channelMask = SL_ANDROID_SPEAKER_7DOT1; - break; - } - return channelMask; -} - -Result AudioOutputStreamOpenSLES::open() { - Result oboeResult = AudioStreamOpenSLES::open(); - if (Result::OK != oboeResult) return oboeResult; - - SLresult result = OutputMixerOpenSL::getInstance().open(); - if (SL_RESULT_SUCCESS != result) { - AudioStreamOpenSLES::close(); - return Result::ErrorInternal; - } - - SLuint32 bitsPerSample = getBytesPerSample() * kBitsPerByte; - - // configure audio source - SLDataLocator_AndroidSimpleBufferQueue loc_bufq = { - SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, // locatorType - static_cast(mBurstsPerBuffer)}; // numBuffers - - // Define the audio data format. - SLDataFormat_PCM format_pcm = { - SL_DATAFORMAT_PCM, // formatType - (SLuint32) mChannelCount, // numChannels - (SLuint32) (mSampleRate * kMillisPerSecond), // milliSamplesPerSec - bitsPerSample, // bitsPerSample - bitsPerSample, // containerSize; - (SLuint32) chanCountToChanMask(mChannelCount), // channelMask - getDefaultByteOrder(), - }; - - SLDataSource audioSrc = {&loc_bufq, &format_pcm}; - - /** - * API 21 (Lollipop) introduced support for floating-point data representation and an extended - * data format type: SLAndroidDataFormat_PCM_EX. If running on API 21+ use this newer format - * type, creating it from our original format. - */ - SLAndroidDataFormat_PCM_EX format_pcm_ex; - if (__ANDROID_API__ >= __ANDROID_API_L__) { - SLuint32 representation = OpenSLES_ConvertFormatToRepresentation(getFormat()); - // Fill in the format structure. - format_pcm_ex = OpenSLES_createExtendedFormat(format_pcm, representation); - // Use in place of the previous format. - audioSrc.pFormat = &format_pcm_ex; - } - - result = OutputMixerOpenSL::getInstance().createAudioPlayer(&mObjectInterface, - &audioSrc); - if (SL_RESULT_SUCCESS != result) { - LOGE("createAudioPlayer() result:%s", getSLErrStr(result)); - goto error; - } - - result = (*mObjectInterface)->Realize(mObjectInterface, SL_BOOLEAN_FALSE); - if (SL_RESULT_SUCCESS != result) { - LOGE("Realize player object result:%s", getSLErrStr(result)); - goto error; - } - - result = (*mObjectInterface)->GetInterface(mObjectInterface, SL_IID_PLAY, &mPlayInterface); - if (SL_RESULT_SUCCESS != result) { - LOGE("GetInterface PLAY result:%s", getSLErrStr(result)); - goto error; - } - - result = AudioStreamOpenSLES::registerBufferQueueCallback(); - if (SL_RESULT_SUCCESS != result) { - goto error; - } - - return Result::OK; -error: - return Result::ErrorInternal; // TODO convert error from SLES to OBOE -} - -Result AudioOutputStreamOpenSLES::close() { - requestPause(); - // invalidate any interfaces - mPlayInterface = NULL; - OutputMixerOpenSL::getInstance().close(); - return AudioStreamOpenSLES::close(); -} - -Result AudioOutputStreamOpenSLES::setPlayState(SLuint32 newState) { - Result result = Result::OK; - LOGD("AudioOutputStreamOpenSLES(): setPlayState()"); - if (mPlayInterface == NULL) { - return Result::ErrorInvalidState; - } - SLresult slResult = (*mPlayInterface)->SetPlayState(mPlayInterface, newState); - if(SL_RESULT_SUCCESS != slResult) { - LOGD("AudioOutputStreamOpenSLES(): setPlayState() returned %s", getSLErrStr(slResult)); - result = Result::ErrorInvalidState; // TODO review - } else { - setState(StreamState::Pausing); - } - return result; -} - -Result AudioOutputStreamOpenSLES::requestStart() { - LOGD("AudioOutputStreamOpenSLES(): requestStart()"); - Result result = setPlayState(SL_PLAYSTATE_PLAYING); - if(result != Result::OK) { - result = Result::ErrorInvalidState; // TODO review - } else { - processBufferCallback(mSimpleBufferQueueInterface); - setState(StreamState::Starting); - } - return result; -} - -Result AudioOutputStreamOpenSLES::requestPause() { - LOGD("AudioOutputStreamOpenSLES(): requestPause()"); - Result result = setPlayState(SL_PLAYSTATE_PAUSED); - if(result != Result::OK) { - result = Result::ErrorInvalidState; // TODO review - } else { - setState(StreamState::Pausing); - } - return result; -} - -Result AudioOutputStreamOpenSLES::requestFlush() { - LOGD("AudioOutputStreamOpenSLES(): requestFlush()"); - if (mPlayInterface == NULL) { - return Result::ErrorInvalidState; - } - return Result::ErrorUnimplemented; // TODO -} - -Result AudioOutputStreamOpenSLES::requestStop() { - LOGD("AudioOutputStreamOpenSLES(): requestStop()"); - Result result = setPlayState(SL_PLAYSTATE_STOPPED); - if(result != Result::OK) { - result = Result::ErrorInvalidState; // TODO review - } else { - setState(StreamState::Stopping); - } - return result; -} - -Result AudioOutputStreamOpenSLES::waitForStateChange(StreamState currentState, - StreamState *nextState, - int64_t timeoutNanoseconds) { - LOGD("AudioOutputStreamOpenSLES::waitForStateChange()"); - if (mPlayInterface == NULL) { - return Result::ErrorInvalidState; - } - return Result::ErrorUnimplemented; // TODO -} diff --git a/src/opensles/AudioOutputStreamOpenSLES.h b/src/opensles/AudioOutputStreamOpenSLES.h deleted file mode 100644 index f8b83de9f..000000000 --- a/src/opensles/AudioOutputStreamOpenSLES.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef AUDIO_OUTPUT_STREAM_OPENSL_ES_H_ -#define AUDIO_OUTPUT_STREAM_OPENSL_ES_H_ - - -#include -#include - -#include "oboe/Oboe.h" -#include "AudioStreamOpenSLES.h" - -namespace oboe { - -/** - * INTERNAL USE ONLY - */ -class AudioOutputStreamOpenSLES : public AudioStreamOpenSLES { -public: - AudioOutputStreamOpenSLES(); - explicit AudioOutputStreamOpenSLES(const AudioStreamBuilder &builder); - - virtual ~AudioOutputStreamOpenSLES(); - - Result open() override; - Result close() override; - - Result requestStart() override; - Result requestPause() override; - Result requestFlush() override; - Result requestStop() override; - - Result waitForStateChange(StreamState currentState, - StreamState *nextState, - int64_t timeoutNanoseconds) override; - - int chanCountToChanMask(int chanCount); - -private: - - /** - * Set OpenSL ES PLAYSTATE. - * - * @param newState SL_PLAYSTATE_PAUSED, SL_PLAYSTATE_PLAYING, SL_PLAYSTATE_STOPPED - * @return - */ - Result setPlayState(SLuint32 newState); - - SLPlayItf mPlayInterface = nullptr; - -}; - -} // namespace oboe - -#endif //AUDIO_OUTPUT_STREAM_OPENSL_ES_H_ diff --git a/src/opensles/AudioStreamOpenSLES.cpp b/src/opensles/AudioStreamOpenSLES.cpp deleted file mode 100644 index 3830eeb7c..000000000 --- a/src/opensles/AudioStreamOpenSLES.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/* Copyright 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include -#include - - -#include -#include - -#include "common/OboeDebug.h" -#include "oboe/AudioStreamBuilder.h" -#include "AudioStreamOpenSLES.h" -#include "OpenSLESUtilities.h" - -#ifndef NULL -#define NULL 0 -#endif - -#define DEFAULT_FRAMES_PER_CALLBACK 192 // 4 msec at 48000 Hz -#define DEFAULT_SAMPLE_RATE 48000 // very common rate for mobile audio and video -#define DEFAULT_CHANNEL_COUNT 2 // stereo - -using namespace oboe; - -AudioStreamOpenSLES::AudioStreamOpenSLES(const AudioStreamBuilder &builder) - : AudioStreamBuffered(builder) { - mSimpleBufferQueueInterface = NULL; - mFramesPerBurst = builder.getDefaultFramesPerBurst(); - LOGD("AudioStreamOpenSLES(): after OpenSLContext()"); -} - -AudioStreamOpenSLES::~AudioStreamOpenSLES() { - delete[] mCallbackBuffer; -} - -static bool s_isLittleEndian() { - static uint32_t value = 1; - return *((uint8_t *) &value) == 1; // Does address point to LSB? -} - -SLuint32 AudioStreamOpenSLES::getDefaultByteOrder() { - return s_isLittleEndian() ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN; -} - -Result AudioStreamOpenSLES::open() { - - LOGI("AudioStreamOpenSLES::open(chans:%d, rate:%d)", - mChannelCount, mSampleRate); - - if (__ANDROID_API__ < __ANDROID_API_L__ && mFormat == AudioFormat::Float){ - // TODO: Allow floating point format on API <21 using float->int16 converter - return Result::ErrorInvalidFormat; - } - - SLresult result = EngineOpenSLES::getInstance().open(); - if (SL_RESULT_SUCCESS != result) { - return Result::ErrorInternal; - } - - // If audio format is unspecified then choose a suitable default. - // API 21+: FLOAT - // API <21: INT16 - if (mFormat == AudioFormat::Unspecified){ - mFormat = (__ANDROID_API__ < __ANDROID_API_L__) ? - AudioFormat::I16 : AudioFormat::Float; - } - - Result oboeResult = AudioStreamBuffered::open(); - if (oboeResult != Result::OK) { - return oboeResult; - } - // Convert to defaults if UNSPECIFIED - if (mSampleRate == kUnspecified) { - mSampleRate = DEFAULT_SAMPLE_RATE; - } - if (mChannelCount == kUnspecified) { - mChannelCount = DEFAULT_CHANNEL_COUNT; - } - - // Decide frames per burst based hints from caller. - // TODO Can we query this from OpenSL ES? - if (mFramesPerCallback != kUnspecified) { - mFramesPerBurst = mFramesPerCallback; - } else if (mFramesPerBurst != kUnspecified) { // set from defaultFramesPerBurst - mFramesPerCallback = mFramesPerBurst; - } else { - mFramesPerBurst = mFramesPerCallback = DEFAULT_FRAMES_PER_CALLBACK; - } - - mBytesPerCallback = mFramesPerCallback * getBytesPerFrame(); - delete[] mCallbackBuffer; // to prevent memory leaks - mCallbackBuffer = new uint8_t[mBytesPerCallback]; - LOGD("AudioStreamOpenSLES(): mFramesPerCallback = %d", mFramesPerCallback); - LOGD("AudioStreamOpenSLES(): mBytesPerCallback = %d", mBytesPerCallback); - - mSharingMode = SharingMode::Shared; - mBufferCapacityInFrames = mFramesPerBurst * mBurstsPerBuffer; - - return Result::OK; -} - -Result AudioStreamOpenSLES::close() { - if (mObjectInterface != NULL) { - (*mObjectInterface)->Destroy(mObjectInterface); - mObjectInterface = NULL; - - } - mSimpleBufferQueueInterface = NULL; - EngineOpenSLES::getInstance().close(); - return Result::OK; -} - -SLresult AudioStreamOpenSLES::enqueueCallbackBuffer(SLAndroidSimpleBufferQueueItf bq) { - return (*bq)->Enqueue(bq, mCallbackBuffer, mBytesPerCallback); -} - -SLresult AudioStreamOpenSLES::processBufferCallback(SLAndroidSimpleBufferQueueItf bq) { - // Ask the callback to fill the output buffer with data. - Result result = fireCallback(mCallbackBuffer, mFramesPerCallback); - if (result != Result::OK) { - LOGE("Oboe callback returned %d", result); - return SL_RESULT_INTERNAL_ERROR; - } else { - // Pass the data to OpenSLES. - return enqueueCallbackBuffer(bq); - } -} - -// this callback handler is called every time a buffer needs processing -static void bqCallbackGlue(SLAndroidSimpleBufferQueueItf bq, void *context) { - ((AudioStreamOpenSLES *) context)->processBufferCallback(bq); -} - -SLresult AudioStreamOpenSLES::registerBufferQueueCallback() { - // The BufferQueue - SLresult result = (*mObjectInterface)->GetInterface(mObjectInterface, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, - &mSimpleBufferQueueInterface); - if (SL_RESULT_SUCCESS != result) { - LOGE("get buffer queue interface:%p result:%s", - mSimpleBufferQueueInterface, - getSLErrStr(result)); - } else { - // Register the BufferQueue callback - result = (*mSimpleBufferQueueInterface)->RegisterCallback(mSimpleBufferQueueInterface, - bqCallbackGlue, this); - if (SL_RESULT_SUCCESS != result) { - LOGE("RegisterCallback result:%s", getSLErrStr(result)); - } - } - return result; -} - -int32_t AudioStreamOpenSLES::getFramesPerBurst() { - return mFramesPerBurst; -} diff --git a/src/opensles/AudioStreamOpenSLES.h b/src/opensles/AudioStreamOpenSLES.h deleted file mode 100644 index 7056e57b4..000000000 --- a/src/opensles/AudioStreamOpenSLES.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef OBOE_AUDIO_STREAM_OPENSL_ES_H_ -#define OBOE_AUDIO_STREAM_OPENSL_ES_H_ - -#include -#include - -#include "oboe/Oboe.h" -#include "AudioStreamBuffered.h" -#include "EngineOpenSLES.h" - -namespace oboe { - -constexpr int kBitsPerByte = 8; - -/** - * INTERNAL USE ONLY - * - * A stream that wraps OpenSL ES. - * - * Do not instantiate this class directly. - * Use an OboeStreamBuilder to create one. - */ - -class AudioStreamOpenSLES : public AudioStreamBuffered { -public: - - AudioStreamOpenSLES(); - explicit AudioStreamOpenSLES(const AudioStreamBuilder &builder); - - virtual ~AudioStreamOpenSLES(); - - virtual Result open() override; - virtual Result close() override; - - /** - * Query the current state, eg. OBOE_STREAM_STATE_PAUSING - * - * @return state or a negative error. - */ - StreamState getState() override { return mState; } - - int32_t getFramesPerBurst() override; - - /** - * Process next OpenSL ES buffer. - * Called by by OpenSL ES framework. - * - * This is public, but don't call it directly. - */ - SLresult processBufferCallback(SLAndroidSimpleBufferQueueItf bq); - -protected: - - static SLuint32 getDefaultByteOrder(); - - SLresult registerBufferQueueCallback(); - - SLresult enqueueCallbackBuffer(SLAndroidSimpleBufferQueueItf bq); - - /** - * Internal use only. - * Use this instead of directly setting the internal state variable. - */ - void setState(StreamState state) { - mState = state; - } - - // OpenSLES stuff - SLObjectItf mObjectInterface = nullptr; - SLAndroidSimpleBufferQueueItf mSimpleBufferQueueInterface = nullptr; - - uint8_t *mCallbackBuffer = nullptr; - int32_t mBytesPerCallback = oboe::kUnspecified; - int32_t mFramesPerBurst = 0; - int32_t mBurstsPerBuffer = 2; // Double buffered - StreamState mState = StreamState::Uninitialized; -}; - -} // namespace oboe - -#endif // OBOE_AUDIO_STREAM_OPENSL_ES_H_ diff --git a/src/opensles/EngineOpenSLES.cpp b/src/opensles/EngineOpenSLES.cpp deleted file mode 100644 index 8df3fed91..000000000 --- a/src/opensles/EngineOpenSLES.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "common/OboeDebug.h" -#include "EngineOpenSLES.h" -#include "OpenSLESUtilities.h" - -using namespace oboe; - -EngineOpenSLES &EngineOpenSLES::getInstance() { - static EngineOpenSLES sInstance; - return sInstance; -} - -SLresult EngineOpenSLES::open() { - std::lock_guard lock(mLock); - - SLresult result = SL_RESULT_SUCCESS; - if (mOpenCount++ == 0) { - - // create engine - result = slCreateEngine(&mEngineObject, 0, NULL, 0, NULL, NULL); - if (SL_RESULT_SUCCESS != result) { - LOGE("EngineOpenSLES - slCreateEngine() result:%s", getSLErrStr(result)); - goto error; - } - - // realize the engine - result = (*mEngineObject)->Realize(mEngineObject, SL_BOOLEAN_FALSE); - if (SL_RESULT_SUCCESS != result) { - LOGE("EngineOpenSLES - Realize() engine result:%s", getSLErrStr(result)); - goto error; - } - - // get the engine interface, which is needed in order to create other objects - result = (*mEngineObject)->GetInterface(mEngineObject, SL_IID_ENGINE, &mEngineInterface); - if (SL_RESULT_SUCCESS != result) { - LOGE("EngineOpenSLES - GetInterface() engine result:%s", getSLErrStr(result)); - goto error; - } - } - - return result; - -error: - close(); - return result; -} - -void EngineOpenSLES::close() { - std::lock_guard lock(mLock); - if (--mOpenCount == 0) { - if (mEngineObject != NULL) { - (*mEngineObject)->Destroy(mEngineObject); - mEngineObject = NULL; - mEngineInterface = NULL; - } - } -} - -SLresult EngineOpenSLES::createOutputMix(SLObjectItf *objectItf) { - return (*mEngineInterface)->CreateOutputMix(mEngineInterface, objectItf, 0, 0, 0); -} - -SLresult EngineOpenSLES::createAudioPlayer(SLObjectItf *objectItf, - SLDataSource *audioSource, - SLDataSink *audioSink) { - - const SLInterfaceID ids[] = {SL_IID_BUFFERQUEUE}; - const SLboolean reqs[] = {SL_BOOLEAN_TRUE}; - - return (*mEngineInterface)->CreateAudioPlayer(mEngineInterface, objectItf, audioSource, - audioSink, - sizeof(ids) / sizeof(ids[0]), ids, reqs); -} - -SLresult EngineOpenSLES::createAudioRecorder(SLObjectItf *objectItf, - SLDataSource *audioSource, - SLDataSink *audioSink) { - - const SLInterfaceID ids[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, - SL_IID_ANDROIDCONFIGURATION }; - const SLboolean reqs[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; - - return (*mEngineInterface)->CreateAudioRecorder(mEngineInterface, objectItf, audioSource, - audioSink, - sizeof(ids) / sizeof(ids[0]), ids, reqs); -} - diff --git a/src/opensles/EngineOpenSLES.h b/src/opensles/EngineOpenSLES.h deleted file mode 100644 index 7658c955a..000000000 --- a/src/opensles/EngineOpenSLES.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef OBOE_ENGINE_OPENSLES_H -#define OBOE_ENGINE_OPENSLES_H - -#include -#include - -#include -#include - -namespace oboe { - -/** - * INTERNAL USE ONLY - */ -class EngineOpenSLES { -public: - static EngineOpenSLES &getInstance(); - - SLresult open(); - - void close(); - - SLresult createOutputMix(SLObjectItf *objectItf); - - SLresult createAudioPlayer(SLObjectItf *objectItf, - SLDataSource *audioSource, - SLDataSink *audioSink); - SLresult createAudioRecorder(SLObjectItf *objectItf, - SLDataSource *audioSource, - SLDataSink *audioSink); - -private: - // Make this a safe Singleton - EngineOpenSLES()= default; - ~EngineOpenSLES()= default; - EngineOpenSLES(const EngineOpenSLES&)= delete; - EngineOpenSLES& operator=(const EngineOpenSLES&)= delete; - - std::mutex mLock; - int32_t mOpenCount = 0; - - SLObjectItf mEngineObject = 0; - SLEngineItf mEngineInterface; -}; - -} // namespace oboe - - -#endif //OBOE_ENGINE_OPENSLES_H diff --git a/src/opensles/AudioStreamBuffered.cpp b/src/opensles/OboeStreamBuffered.cpp similarity index 56% rename from src/opensles/AudioStreamBuffered.cpp rename to src/opensles/OboeStreamBuffered.cpp index ceb4c850c..b31229638 100644 --- a/src/opensles/AudioStreamBuffered.cpp +++ b/src/opensles/OboeStreamBuffered.cpp @@ -16,52 +16,53 @@ #include "oboe/Oboe.h" -#include "opensles/AudioStreamBuffered.h" +#include "opensles/OboeStreamBuffered.h" #include "common/AudioClock.h" -namespace oboe { - /* - * AudioStream with a FifoBuffer + * OboeStream with a FifoBuffer */ -AudioStreamBuffered::AudioStreamBuffered(const AudioStreamBuilder &builder) - : AudioStream(builder) - , mFifoBuffer(nullptr) +OboeStreamBuffered::OboeStreamBuffered(const OboeStreamBuilder &builder) + : OboeStream(builder) + , mFifoBuffer(NULL) + , mInternalCallback(NULL) { } -Result AudioStreamBuffered::open() { +oboe_result_t OboeStreamBuffered::open() { - Result result = AudioStream::open(); - if (result != Result::OK) { + oboe_result_t result = OboeStream::open(); + if (result < 0) { return result; } // If the caller does not provide a callback use our own internal // callback that reads data from the FIFO. - if (getCallback() == nullptr) { - LOGD("AudioStreamBuffered(): new FifoBuffer"); - // TODO: Fix memory leak here + if (getCallback() == NULL) { + LOGD("OboeStreamBuffered(): new FifoBuffer"); mFifoBuffer = new FifoBuffer(getBytesPerFrame(), 1024); // TODO size? // Create a callback that reads from the FIFO - mInternalCallback = std::unique_ptr(new AudioStreamBufferedCallback(this)); - mStreamCallback = mInternalCallback.get(); - LOGD("AudioStreamBuffered(): mStreamCallback = %p", mStreamCallback); + mInternalCallback = new AudioStreamBufferedCallback(this); + mStreamCallback = mInternalCallback; + LOGD("OboeStreamBuffered(): mInternalCallback = %p", mInternalCallback); } - return Result::OK; + return OBOE_OK; +} + +OboeStreamBuffered::~OboeStreamBuffered() { + delete mInternalCallback; } -// TODO: This method should return a tuple of Result,int32_t where the 2nd return param is the frames written -int32_t AudioStreamBuffered::write(const void *buffer, - int32_t numFrames, - int64_t timeoutNanoseconds) +oboe_result_t OboeStreamBuffered::write(const void *buffer, + int32_t numFrames, + int64_t timeoutNanoseconds) { - int32_t result = 0; + oboe_result_t result = OBOE_OK; uint8_t *source = (uint8_t *)buffer; int32_t framesLeft = numFrames; while(framesLeft > 0 && result >= 0) { - result = mFifoBuffer->write(source, numFrames); - LOGD("AudioStreamBuffered::writeNow(): wrote %d/%d frames", result, numFrames); + oboe_result_t result = mFifoBuffer->write(source, numFrames); + LOGD("OboeStreamBuffered::writeNow(): wrote %d/%d frames", result, numFrames); if (result > 0) { source += mFifoBuffer->convertFramesToBytes(result); incrementFramesWritten(result); @@ -76,34 +77,32 @@ int32_t AudioStreamBuffered::write(const void *buffer, return result; } -Result AudioStreamBuffered::setBufferSizeInFrames(int32_t requestedFrames) +oboe_result_t OboeStreamBuffered::setBufferSizeInFrames(int32_t requestedFrames) { if (mFifoBuffer != nullptr) { if (requestedFrames > mFifoBuffer->getBufferCapacityInFrames()) { requestedFrames = mFifoBuffer->getBufferCapacityInFrames(); } mFifoBuffer->setThresholdFrames(requestedFrames); - return Result::OK; + return OBOE_OK; } else { - return Result::ErrorUnimplemented; + return OBOE_ERROR_UNIMPLEMENTED; } } -int32_t AudioStreamBuffered::getBufferSizeInFrames() const { +int32_t OboeStreamBuffered::getBufferSizeInFrames() const { if (mFifoBuffer != nullptr) { return mFifoBuffer->getThresholdFrames(); } else { - return AudioStream::getBufferSizeInFrames(); + return OboeStream::getBufferSizeInFrames(); } } -int32_t AudioStreamBuffered::getBufferCapacityInFrames() const { +int32_t OboeStreamBuffered::getBufferCapacityInFrames() const { if (mFifoBuffer != nullptr) { return mFifoBuffer->getBufferCapacityInFrames(); // Maybe set mBufferCapacity in constructor } else { - return AudioStream::getBufferCapacityInFrames(); + return OboeStream::getBufferCapacityInFrames(); } } - -} // namespace oboe \ No newline at end of file diff --git a/src/opensles/AudioStreamBuffered.h b/src/opensles/OboeStreamBuffered.h similarity index 55% rename from src/opensles/AudioStreamBuffered.h rename to src/opensles/OboeStreamBuffered.h index c4c291448..118f170b9 100644 --- a/src/opensles/AudioStreamBuffered.h +++ b/src/opensles/OboeStreamBuffered.h @@ -14,30 +14,29 @@ * limitations under the License. */ -#ifndef OBOE_STREAM_BUFFERED_H -#define OBOE_STREAM_BUFFERED_H +#ifndef OBOE_OBOE_STREAM_BUFFERED_H +#define OBOE_OBOE_STREAM_BUFFERED_H #include "common/OboeDebug.h" -#include "oboe/AudioStream.h" -#include "oboe/AudioStreamCallback.h" +#include "oboe/OboeStream.h" +#include "oboe/OboeStreamCallback.h" #include "fifo/FifoBuffer.h" -namespace oboe { - // A stream that contains a FIFO buffer. -class AudioStreamBuffered : public AudioStream { +class OboeStreamBuffered : public OboeStream { public: - AudioStreamBuffered(); - explicit AudioStreamBuffered(const AudioStreamBuilder &builder); + OboeStreamBuffered(); + explicit OboeStreamBuffered(const OboeStreamBuilder &builder); + virtual ~OboeStreamBuffered(); - Result open() override; + oboe_result_t open() override; - int32_t write(const void *buffer, - int32_t numFrames, - int64_t timeoutNanoseconds) override; + oboe_result_t write(const void *buffer, + int32_t numFrames, + int64_t timeoutNanoseconds) override; - Result setBufferSizeInFrames(int32_t requestedFrames) override; + oboe_result_t setBufferSizeInFrames(int32_t requestedFrames) override; int32_t getBufferSizeInFrames() const override; @@ -45,34 +44,33 @@ class AudioStreamBuffered : public AudioStream { protected: - class AudioStreamBufferedCallback : public AudioStreamCallback { + class AudioStreamBufferedCallback : public OboeStreamCallback { public: - AudioStreamBufferedCallback(AudioStreamBuffered *bufferedStream) + AudioStreamBufferedCallback(OboeStreamBuffered *bufferedStream) : mBufferedStream(bufferedStream) { } virtual ~AudioStreamBufferedCallback() {} - virtual DataCallbackResult onAudioReady( - AudioStream *audioStream, + virtual oboe_result_t onAudioReady( + OboeStream *audioStream, void *audioData, int numFrames) { int32_t framesRead = mBufferedStream->mFifoBuffer->readNow(audioData, numFrames); //LOGD("AudioStreamBufferedCallback(): read %d / %d frames", framesRead, numFrames); - return (framesRead >= 0) ? DataCallbackResult::Continue : DataCallbackResult::Stop; + return (framesRead >= 0) ? OBOE_OK : OBOE_ERROR_INTERNAL; } - virtual void onExit(Result reason) {} + virtual void onExit(oboe_result_t reason) {} private: - AudioStreamBuffered *mBufferedStream; + OboeStreamBuffered *mBufferedStream; }; private: FifoBuffer *mFifoBuffer; - std::unique_ptr mInternalCallback; + AudioStreamBufferedCallback *mInternalCallback; }; -} // namespace oboe -#endif //OBOE_STREAM_BUFFERED_H +#endif //OBOE_OBOE_STREAM_BUFFERED_H diff --git a/src/opensles/OboeStreamOpenSLES.cpp b/src/opensles/OboeStreamOpenSLES.cpp new file mode 100644 index 000000000..d3b62aa23 --- /dev/null +++ b/src/opensles/OboeStreamOpenSLES.cpp @@ -0,0 +1,449 @@ +/* Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include + + +#include +#include + +#include "common/OboeDebug.h" +#include "oboe/OboeStreamBuilder.h" +#include "OboeStreamOpenSLES.h" +#include "OpenSLESUtilities.h" + +#ifndef NULL +#define NULL 0 +#endif + +#define DEFAULT_FRAMES_PER_CALLBACK 192 // 4 msec at 48000 Hz +#define DEFAULT_SAMPLE_RATE 48000 // very common rate for mobile audio and video +#define DEFAULT_CHANNEL_COUNT 2 // stereo + +#define OBOE_BITS_PER_BYTE 8 // common value + +/* + * OSLES Helpers + */ +static const char *errStrings[] = { + "SL_RESULT_SUCCESS", // 0 + "SL_RESULT_PRECONDITIONS_VIOLATE", // 1 + "SL_RESULT_PARAMETER_INVALID", // 2 + "SL_RESULT_MEMORY_FAILURE", // 3 + "SL_RESULT_RESOURCE_ERROR", // 4 + "SL_RESULT_RESOURCE_LOST", // 5 + "SL_RESULT_IO_ERROR", // 6 + "SL_RESULT_BUFFER_INSUFFICIENT", // 7 + "SL_RESULT_CONTENT_CORRUPTED", // 8 + "SL_RESULT_CONTENT_UNSUPPORTED", // 9 + "SL_RESULT_CONTENT_NOT_FOUND", // 10 + "SL_RESULT_PERMISSION_DENIED", // 11 + "SL_RESULT_FEATURE_UNSUPPORTED", // 12 + "SL_RESULT_INTERNAL_ERROR", // 13 + "SL_RESULT_UNKNOWN_ERROR", // 14 + "SL_RESULT_OPERATION_ABORTED", // 15 + "SL_RESULT_CONTROL_LOST" // 16 +}; + +const char *getSLErrStr(int code) { + return errStrings[code]; +} + +// These will wind up in +#define SL_ANDROID_SPEAKER_QUAD (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT \ + | SL_SPEAKER_BACK_LEFT | SL_SPEAKER_BACK_RIGHT) + +#define SL_ANDROID_SPEAKER_5DOT1 (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT \ + | SL_SPEAKER_FRONT_CENTER | SL_SPEAKER_LOW_FREQUENCY| SL_SPEAKER_BACK_LEFT \ + | SL_SPEAKER_BACK_RIGHT) + +#define SL_ANDROID_SPEAKER_7DOT1 (SL_ANDROID_SPEAKER_5DOT1 | SL_SPEAKER_SIDE_LEFT \ + |SL_SPEAKER_SIDE_RIGHT) + +int chanCountToChanMask(int chanCount) { + int channelMask = 0; + + switch (chanCount) { + case 1: + channelMask = SL_SPEAKER_FRONT_CENTER; + break; + + case 2: + channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; + break; + + case 4: // Quad + channelMask = SL_ANDROID_SPEAKER_QUAD; + break; + + case 6: // 5.1 + channelMask = SL_ANDROID_SPEAKER_5DOT1; + break; + + case 8: // 7.1 + channelMask = SL_ANDROID_SPEAKER_7DOT1; + break; + } + return channelMask; +} + +static const char *TAG = "AAudioStreamOpenSLES"; + +// engine interfaces +static int32_t sOpenCount = 0; +static SLObjectItf sEngineObject = 0; +static SLEngineItf sEngineEngine; + +// output mix interfaces +static SLObjectItf sOutputMixObject = 0; + +static void CloseSLEngine(); + +SLresult OboeStreamOpenSLES::enqueueBuffer() { + // Ask the callback to fill the output buffer with data. + oboe_result_t result = fireCallback(mCallbackBuffer, mFramesPerCallback); + if (result != OBOE_OK) { + LOGE("Oboe callback returned %d", result); + return SL_RESULT_INTERNAL_ERROR; + } else { + // Pass the data to OpenSLES. + return (*bq_)->Enqueue(bq_, mCallbackBuffer, mBytesPerCallback); + } +} + +// this callback handler is called every time a buffer finishes playing +static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) { + ((OboeStreamOpenSLES *) context)->enqueueBuffer(); +} + +static SLresult OpenSLEngine() { + SLresult result = SL_RESULT_SUCCESS; + if (sOpenCount > 0) { + ++sOpenCount; + return SL_RESULT_SUCCESS; + } + + // create engine + result = slCreateEngine(&sEngineObject, 0, NULL, 0, NULL, NULL); + if (SL_RESULT_SUCCESS != result) { + LOGE("OpenSLEngine() - slCreateEngine() result:%s", getSLErrStr(result)); + return result; + } + + // realize the engine + result = (*sEngineObject)->Realize(sEngineObject, SL_BOOLEAN_FALSE); + if (SL_RESULT_SUCCESS != result) { + LOGE("OpenSLEngine() - Realize() engine result:%s", getSLErrStr(result)); + goto error; + } + + // get the engine interface, which is needed in order to create other objects + result = (*sEngineObject)->GetInterface(sEngineObject, SL_IID_ENGINE, &sEngineEngine); + if (SL_RESULT_SUCCESS != result) { + LOGE("OpenSLEngine() - GetInterface() engine result:%s", getSLErrStr(result)); + goto error; + } + + // get the output mixer + result = (*sEngineEngine)->CreateOutputMix(sEngineEngine, &sOutputMixObject, 0, 0, 0); + if (SL_RESULT_SUCCESS != result) { + LOGE("OpenSLEngine() - CreateOutputMix() result:%s", getSLErrStr(result)); + goto error; + } + + // realize the output mix + result = (*sOutputMixObject)->Realize(sOutputMixObject, SL_BOOLEAN_FALSE); + if (SL_RESULT_SUCCESS != result) { + LOGE("OpenSLEngine() - Realize() sOutputMixObject result:%s", getSLErrStr(result)); + goto error; + } + + ++sOpenCount; + return result; + +error: + CloseSLEngine(); + return result; +} + +static void CloseSLEngine() { +// __android_log_print(ANDROID_LOG_INFO, TAG, "CloseSLEngine()"); + --sOpenCount; + if (sOpenCount > 0) { + return; + } + // destroy output mix object, and invalidate all associated interfaces + if (sOutputMixObject != NULL) { + (*sOutputMixObject)->Destroy(sOutputMixObject); + sOutputMixObject = NULL; + } + + if (sEngineObject != NULL) { + (*sEngineObject)->Destroy(sEngineObject); + sEngineObject = NULL; + sEngineEngine = NULL; + } +} + +OboeStreamOpenSLES::OboeStreamOpenSLES(const OboeStreamBuilder &builder) + : OboeStreamBuffered(builder) { + bqPlayerObject_ = NULL; + bq_ = NULL; + bqPlayerPlay_ = NULL; + mFramesPerBurst = builder.getDefaultFramesPerBurst(); + OpenSLEngine(); + LOGD("OboeStreamOpenSLES(): after OpenSLEngine()"); +} + +OboeStreamOpenSLES::~OboeStreamOpenSLES() { + CloseSLEngine(); + delete[] mCallbackBuffer; +} + +static SLuint32 ConvertFormatToRepresentation(oboe_audio_format_t format) { + switch(format) { + case OBOE_AUDIO_FORMAT_INVALID: + case OBOE_AUDIO_FORMAT_UNSPECIFIED: + return 0; + case OBOE_AUDIO_FORMAT_PCM_I16: + return SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT; + case OBOE_AUDIO_FORMAT_PCM_FLOAT: + return SL_ANDROID_PCM_REPRESENTATION_FLOAT; + } + return 0; +} + +static bool s_isLittleEndian() { + static uint32_t value = 1; + return *((uint8_t *) &value) == 1; // Does address point to LSB? +} + +static SLuint32 s_getDefaultByteOrder() { + return s_isLittleEndian() ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN; +} + +oboe_result_t OboeStreamOpenSLES::open() { + + SLresult result; + + __android_log_print(ANDROID_LOG_INFO, TAG, "AudioPlayerOpenSLES::Open(chans:%d, rate:%d)", + mChannelCount, mSampleRate); + + if (__ANDROID_API__ < __ANDROID_API_L__ && mFormat == OBOE_AUDIO_FORMAT_PCM_FLOAT){ + // TODO: Allow floating point format on API <21 using float->int16 converter + return OBOE_ERROR_INVALID_FORMAT; + } + + // If audio format is unspecified then choose a suitable default. + // API 21+: FLOAT + // API <21: INT16 + if (mFormat == OBOE_AUDIO_FORMAT_UNSPECIFIED){ + mFormat = (__ANDROID_API__ < __ANDROID_API_L__) ? + OBOE_AUDIO_FORMAT_PCM_I16 : OBOE_AUDIO_FORMAT_PCM_FLOAT; + } + + oboe_result_t oboeResult = OboeStreamBuffered::open(); + if (oboeResult < 0) { + return oboeResult; + } + // Convert to defaults if UNSPECIFIED + if (mSampleRate == OBOE_UNSPECIFIED) { + mSampleRate = DEFAULT_SAMPLE_RATE; + } + if (mChannelCount == OBOE_UNSPECIFIED) { + mChannelCount = DEFAULT_CHANNEL_COUNT; + } + + // Decide frames per burst based hints from caller. + // TODO Can we query this from OpenSL ES? + if (mFramesPerCallback != OBOE_UNSPECIFIED) { + mFramesPerBurst = mFramesPerCallback; + } else if (mFramesPerBurst != OBOE_UNSPECIFIED) { // set from defaultFramesPerBurst + mFramesPerCallback = mFramesPerBurst; + } else { + mFramesPerBurst = mFramesPerCallback = DEFAULT_FRAMES_PER_CALLBACK; + } + + mBytesPerCallback = mFramesPerCallback * getBytesPerFrame(); + mCallbackBuffer = new uint8_t[mBytesPerCallback]; + LOGD("OboeStreamOpenSLES(): mFramesPerCallback = %d", mFramesPerCallback); + LOGD("OboeStreamOpenSLES(): mBytesPerCallback = %d", mBytesPerCallback); + + SLuint32 bitsPerSample = getBytesPerSample() * OBOE_BITS_PER_BYTE; + + // configure audio source + SLDataLocator_AndroidSimpleBufferQueue loc_bufq = { + SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, // locatorType + static_cast(mBurstsPerBuffer)}; // numBuffers + + // SLuint32 chanMask = SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT; + + // Define the audio data format. + SLDataFormat_PCM format_pcm = { + SL_DATAFORMAT_PCM, // formatType + (SLuint32) mChannelCount, // numChannels + (SLuint32) (mSampleRate * OBOE_MILLIS_PER_SECOND), // milliSamplesPerSec + bitsPerSample, // bitsPerSample + bitsPerSample, // containerSize; + (SLuint32) chanCountToChanMask(mChannelCount), // channelMask + s_getDefaultByteOrder(), + }; + + SLDataSource audioSrc = {&loc_bufq, &format_pcm}; + + /** + * API 21 (Lollipop) introduced support for floating-point data representation and an extended + * data format type: SLAndroidDataFormat_PCM_EX. If running on API 21+ use this newer format + * type, creating it from our original format. + */ + if (__ANDROID_API__ >= __ANDROID_API_L__) { + SLuint32 representation = ConvertFormatToRepresentation(getFormat()); + SLAndroidDataFormat_PCM_EX format_pcm_ex = OpenSLES_createExtendedFormat(format_pcm, + representation); + // Overwrite the previous format. + audioSrc.pFormat = &format_pcm_ex; + } + + // configure audio sink + SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, sOutputMixObject}; + SLDataSink audioSnk = {&loc_outmix, NULL}; + + const SLInterfaceID ids[] = {SL_IID_BUFFERQUEUE}; + const SLboolean req[] = {SL_BOOLEAN_TRUE}; + + // The Player + result = (*sEngineEngine)->CreateAudioPlayer(sEngineEngine, &bqPlayerObject_, &audioSrc, + &audioSnk, + sizeof(ids) / sizeof(ids[0]), ids, req); + LOGD("CreateAudioPlayer() result:%s", getSLErrStr(result)); + assert(SL_RESULT_SUCCESS == result); + + result = (*bqPlayerObject_)->Realize(bqPlayerObject_, SL_BOOLEAN_FALSE); + LOGD("Realize player object result:%s", getSLErrStr(result)); + assert(SL_RESULT_SUCCESS == result); + + result = (*bqPlayerObject_)->GetInterface(bqPlayerObject_, SL_IID_PLAY, &bqPlayerPlay_); + LOGD("get player interface result:%s", getSLErrStr(result)); + assert(SL_RESULT_SUCCESS == result); + + // The BufferQueue + result = (*bqPlayerObject_)->GetInterface(bqPlayerObject_, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, + &bq_); + LOGD("get bufferqueue interface:%p result:%s", bq_, getSLErrStr(result)); + assert(SL_RESULT_SUCCESS == result); + + // The register BufferQueue callback + result = (*bq_)->RegisterCallback(bq_, bqPlayerCallback, this); + LOGD("register callback result:%s", getSLErrStr(result)); + assert(SL_RESULT_SUCCESS == result); + + mSharingMode = OBOE_SHARING_MODE_SHARED; + mBufferCapacityInFrames = mFramesPerBurst * mBurstsPerBuffer; + + return OBOE_OK; +} + +oboe_result_t OboeStreamOpenSLES::close() { +// __android_log_write(ANDROID_LOG_INFO, TAG, "OboeStreamOpenSLES()"); + // TODO make sure callback is no longer being called + if (bqPlayerObject_ != NULL) { + (*bqPlayerObject_)->Destroy(bqPlayerObject_); + bqPlayerObject_ = NULL; + + // invalidate any interfaces + bqPlayerPlay_ = NULL; + bq_ = NULL; + } + return OBOE_OK; +} + +oboe_result_t OboeStreamOpenSLES::setPlayState(SLuint32 newState) +{ + oboe_result_t result = OBOE_OK; + LOGD("OboeStreamOpenSLES(): setPlayState()"); + if (bqPlayerPlay_ == NULL) { + return OBOE_ERROR_INVALID_STATE; + } + SLresult slResult = (*bqPlayerPlay_)->SetPlayState(bqPlayerPlay_, newState); + if(SL_RESULT_SUCCESS != slResult) { + LOGD("OboeStreamOpenSLES(): setPlayState() returned %s", getSLErrStr(result)); + result = OBOE_ERROR_INVALID_STATE; // TODO review + } else { + setState(OBOE_STREAM_STATE_PAUSING); + } + return result; +} + +oboe_result_t OboeStreamOpenSLES::requestStart() +{ + LOGD("OboeStreamOpenSLES(): requestStart()"); + oboe_result_t result = setPlayState(SL_PLAYSTATE_PLAYING); + if(result != OBOE_OK) { + result = OBOE_ERROR_INVALID_STATE; // TODO review + } else { + enqueueBuffer(); + setState(OBOE_STREAM_STATE_STARTING); + } + return result; +} + + +oboe_result_t OboeStreamOpenSLES::requestPause() { + LOGD("OboeStreamOpenSLES(): requestPause()"); + oboe_result_t result = setPlayState(SL_PLAYSTATE_PAUSED); + if(result != OBOE_OK) { + result = OBOE_ERROR_INVALID_STATE; // TODO review + } else { + setState(OBOE_STREAM_STATE_PAUSING); + } + return result; +} + +oboe_result_t OboeStreamOpenSLES::requestFlush() { + LOGD("OboeStreamOpenSLES(): requestFlush()"); + if (bqPlayerPlay_ == NULL) { + return OBOE_ERROR_INVALID_STATE; + } + return OBOE_ERROR_UNIMPLEMENTED; // TODO +} + +oboe_result_t OboeStreamOpenSLES::requestStop() +{ + LOGD("OboeStreamOpenSLES(): requestStop()"); + oboe_result_t result = setPlayState(SL_PLAYSTATE_STOPPED); + if(result != OBOE_OK) { + result = OBOE_ERROR_INVALID_STATE; // TODO review + } else { + setState(OBOE_STREAM_STATE_STOPPING); + } + return result; +} + +oboe_result_t OboeStreamOpenSLES::waitForStateChange(oboe_stream_state_t currentState, + oboe_stream_state_t *nextState, + int64_t timeoutNanoseconds) +{ + LOGD("OboeStreamOpenSLES(): waitForStateChange()"); + if (bqPlayerPlay_ == NULL) { + return OBOE_ERROR_INVALID_STATE; + } + return OBOE_ERROR_UNIMPLEMENTED; // TODO +} + +int32_t OboeStreamOpenSLES::getFramesPerBurst() { + return mFramesPerBurst; +} + diff --git a/src/opensles/OboeStreamOpenSLES.h b/src/opensles/OboeStreamOpenSLES.h new file mode 100644 index 000000000..81eb50023 --- /dev/null +++ b/src/opensles/OboeStreamOpenSLES.h @@ -0,0 +1,97 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AUDIO_STREAM_OPENSL_ES_H_ +#define AUDIO_STREAM_OPENSL_ES_H_ + +#include +#include + +#include "oboe/Oboe.h" +#include "OboeStreamBuffered.h" + +/** + * A stream that wraps OpenSL ES. + * + * Do not instantiate this class directly. + * Use an OboeStreamBuilder to create one. + */ +// +class OboeStreamOpenSLES : public OboeStreamBuffered { +public: + + OboeStreamOpenSLES(); + explicit OboeStreamOpenSLES(const OboeStreamBuilder &builder); + + virtual ~OboeStreamOpenSLES(); + + oboe_result_t open() override; + oboe_result_t close() override; + + oboe_result_t requestStart() override; + oboe_result_t requestPause() override; + oboe_result_t requestFlush() override; + oboe_result_t requestStop() override; + + // public, but don't call directly (called by the OSLES callback) + SLresult enqueueBuffer(); + + oboe_result_t waitForStateChange(oboe_stream_state_t currentState, + oboe_stream_state_t *nextState, + int64_t timeoutNanoseconds) override; + + /** + * Query the current state, eg. OBOE_STREAM_STATE_PAUSING + * + * @return state or a negative error. + */ + oboe_stream_state_t getState() override { return mState; } + + int32_t getFramesPerBurst() override; + +protected: +private: + + /** + * Internal use only. + * Use this instead of directly setting the internal state variable. + */ + void setState(oboe_stream_state_t state) { + mState = state; + } + + /** + * Set OpenSL ES state. + * + * @param newState SL_PLAYSTATE_PAUSED, SL_PLAYSTATE_PLAYING, SL_PLAYSTATE_STOPPED + * @return + */ + oboe_result_t setPlayState(SLuint32 newState); + + uint8_t *mCallbackBuffer; + int32_t mBytesPerCallback; + int32_t mFramesPerBurst = 0; + int32_t mBurstsPerBuffer = 2; // Double buffered + oboe_stream_state_t mState = OBOE_STREAM_STATE_UNINITIALIZED; + + // OpenSLES stuff + SLObjectItf bqPlayerObject_; + SLPlayItf bqPlayerPlay_; + SLAndroidSimpleBufferQueueItf bq_; +}; + + +#endif // AUDIO_STREAM_OPENSL_ES_H_ diff --git a/src/opensles/OpenSLESUtilities.cpp b/src/opensles/OpenSLESUtilities.cpp index 45f1c1880..e38b4e56c 100644 --- a/src/opensles/OpenSLESUtilities.cpp +++ b/src/opensles/OpenSLESUtilities.cpp @@ -16,35 +16,6 @@ #include "OpenSLESUtilities.h" -namespace oboe { - -/* - * OSLES Helpers - */ -static const char *errStrings[] = { - "SL_RESULT_SUCCESS", // 0 - "SL_RESULT_PRECONDITIONS_VIOLATE", // 1 - "SL_RESULT_PARAMETER_INVALID", // 2 - "SL_RESULT_MEMORY_FAILURE", // 3 - "SL_RESULT_RESOURCE_ERROR", // 4 - "SL_RESULT_RESOURCE_LOST", // 5 - "SL_RESULT_IO_ERROR", // 6 - "SL_RESULT_BUFFER_INSUFFICIENT", // 7 - "SL_RESULT_CONTENT_CORRUPTED", // 8 - "SL_RESULT_CONTENT_UNSUPPORTED", // 9 - "SL_RESULT_CONTENT_NOT_FOUND", // 10 - "SL_RESULT_PERMISSION_DENIED", // 11 - "SL_RESULT_FEATURE_UNSUPPORTED", // 12 - "SL_RESULT_INTERNAL_ERROR", // 13 - "SL_RESULT_UNKNOWN_ERROR", // 14 - "SL_RESULT_OPERATION_ABORTED", // 15 - "SL_RESULT_CONTROL_LOST" // 16 -}; - -const char *getSLErrStr(SLresult code) { - return errStrings[code]; -} - SLAndroidDataFormat_PCM_EX OpenSLES_createExtendedFormat( SLDataFormat_PCM format, SLuint32 representation) { SLAndroidDataFormat_PCM_EX format_pcm_ex; @@ -58,18 +29,3 @@ SLAndroidDataFormat_PCM_EX OpenSLES_createExtendedFormat( format_pcm_ex.representation = representation; return format_pcm_ex; } - -SLuint32 OpenSLES_ConvertFormatToRepresentation(AudioFormat format) { - switch(format) { - case AudioFormat::I16: - return SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT; - case AudioFormat::Float: - return SL_ANDROID_PCM_REPRESENTATION_FLOAT; - case AudioFormat::Invalid: - case AudioFormat::Unspecified: - default: - return 0; - } -} - -} // namespace oboe \ No newline at end of file diff --git a/src/opensles/OpenSLESUtilities.h b/src/opensles/OpenSLESUtilities.h index 20f5498e5..41fd644ae 100644 --- a/src/opensles/OpenSLESUtilities.h +++ b/src/opensles/OpenSLESUtilities.h @@ -18,11 +18,6 @@ #define OBOE_OPENSLES_OPENSLESUTILITIES_H #include -#include "oboe/Oboe.h" - -namespace oboe { - -const char *getSLErrStr(SLresult code); /** * Creates an extended PCM format from the supplied format and data representation. This method @@ -37,9 +32,4 @@ const char *getSLErrStr(SLresult code); SLAndroidDataFormat_PCM_EX OpenSLES_createExtendedFormat(SLDataFormat_PCM format, SLuint32 representation); - -SLuint32 OpenSLES_ConvertFormatToRepresentation(AudioFormat format); - -} // namespace oboe - #endif //OBOE_OPENSLES_OPENSLESUTILITIES_H diff --git a/src/opensles/OutputMixerOpenSLES.cpp b/src/opensles/OutputMixerOpenSLES.cpp deleted file mode 100644 index 901e9a75e..000000000 --- a/src/opensles/OutputMixerOpenSLES.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include "common/OboeDebug.h" -#include "EngineOpenSLES.h" -#include "OpenSLESUtilities.h" -#include "OutputMixerOpenSLES.h" - -using namespace oboe; - -OutputMixerOpenSL &OutputMixerOpenSL::getInstance() { - static OutputMixerOpenSL sInstance; - return sInstance; -} - -SLresult OutputMixerOpenSL::open() { - std::lock_guard lock(mLock); - - SLresult result = SL_RESULT_SUCCESS; - if (mOpenCount++ == 0) { - // get the output mixer - result = EngineOpenSLES::getInstance().createOutputMix(&mOutputMixObject); - if (SL_RESULT_SUCCESS != result) { - LOGE("OutputMixerOpenSL() - createOutputMix() result:%s", getSLErrStr(result)); - goto error; - } - - // realize the output mix - result = (*mOutputMixObject)->Realize(mOutputMixObject, SL_BOOLEAN_FALSE); - if (SL_RESULT_SUCCESS != result) { - LOGE("OutputMixerOpenSL() - Realize() mOutputMixObject result:%s", getSLErrStr(result)); - goto error; - } - } - - return result; - -error: - close(); - return result; -} - -void OutputMixerOpenSL::close() { - std::lock_guard lock(mLock); - - if (--mOpenCount == 0) { - // destroy output mix object, and invalidate all associated interfaces - if (mOutputMixObject != NULL) { - (*mOutputMixObject)->Destroy(mOutputMixObject); - mOutputMixObject = NULL; - } - } -} - -SLresult OutputMixerOpenSL::createAudioPlayer(SLObjectItf *objectItf, - SLDataSource *audioSource) { - SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, mOutputMixObject}; - SLDataSink audioSink = {&loc_outmix, NULL}; - return EngineOpenSLES::getInstance().createAudioPlayer(objectItf, audioSource, &audioSink); -} diff --git a/src/opensles/OutputMixerOpenSLES.h b/src/opensles/OutputMixerOpenSLES.h deleted file mode 100644 index 1fc77e512..000000000 --- a/src/opensles/OutputMixerOpenSLES.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef OBOE_OUTPUT_MIXER_OPENSLES_H -#define OBOE_OUTPUT_MIXER_OPENSLES_H - -#include -#include - -#include -#include - -namespace oboe { - -/** - * INTERNAL USE ONLY - */ - -class OutputMixerOpenSL { -public: - static OutputMixerOpenSL &getInstance(); - - SLresult open(); - - void close(); - - SLresult createAudioPlayer(SLObjectItf *objectItf, - SLDataSource *audioSource); - -private: - // Make this a safe Singleton - OutputMixerOpenSL()= default; - ~OutputMixerOpenSL()= default; - OutputMixerOpenSL(const OutputMixerOpenSL&)= delete; - OutputMixerOpenSL& operator=(const OutputMixerOpenSL&)= delete; - - std::mutex mLock; - int32_t mOpenCount = 0; - - SLObjectItf mOutputMixObject = 0; -}; - -} // namespace oboe - -#endif //OBOE_OUTPUT_MIXER_OPENSLES_H diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt deleted file mode 100644 index 303bf1a56..000000000 --- a/tests/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -cmake_minimum_required(VERSION 3.4.1) - -set(GOOGLETEST_ROOT ${ANDROID_NDK}/sources/third_party/googletest/googletest) -add_library(gtest STATIC ${GOOGLETEST_ROOT}/src/gtest_main.cc ${GOOGLETEST_ROOT}/src/gtest-all.cc) -target_include_directories(gtest PRIVATE ${GOOGLETEST_ROOT}) -target_include_directories(gtest PUBLIC ${GOOGLETEST_ROOT}/include) - -include_directories(../include) -add_executable(testOboe testUtilities.cpp ../src/common/Utilities.cpp) -target_link_libraries(testOboe gtest) \ No newline at end of file diff --git a/tests/run_tests.sh b/tests/run_tests.sh deleted file mode 100755 index c4a8a81e7..000000000 --- a/tests/run_tests.sh +++ /dev/null @@ -1,110 +0,0 @@ -# Copyright 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -################################################ -# Script to build and run the Oboe tests on an attached Android device or emulator -# -# Prerequisites: -# - CMake on PATH -# - ANDROID_NDK environment variable is set to your Android NDK location -# e.g. /Library/Android/sdk/ndk-bundle -# - Android device or emulator attached and accessible via adb -# -# Instructions: -# Run this script from within the oboe/tests directory. A directory 'build' will be -# created containing the test binary. This binary will then be copied to the device/emulator -# and executed. -# -# The initial run may take some time as GTest is built, subsequent runs should be much, much -# faster. -# -# If you want to perform a clean build just delete the 'build' folder and re-run this script -# -################################################ - -#!/bin/bash - -# Directories, paths and filenames -BUILD_DIR=build -CMAKE=cmake -TEST_BINARY_FILENAME=testOboe -REMOTE_DIR=/data/local/tmp/oboe - -# Check prerequisites -if [ -z "$ANDROID_NDK" ]; then - echo "Please set ANDROID_NDK to the Android NDK folder" - exit 1 -fi - -if [ ! $(type -P ${CMAKE}) ]; then - echo "${CMAKE} was not found on your path. You can install it using Android Studio using Tools->Android->SDK Manager->SDK Tools." - exit 1 -fi - -# Get the device ABI -ABI=$(adb shell getprop ro.product.cpu.abi | tr -d '\n\r') - -if [ -z "$ABI" ]; then - echo "No device ABI was set. Please ensure a device or emulator is running" - exit 1 -fi - -echo "Device/emulator architecture is $ABI" - -if [ ${ABI} == "arm64-v8a" ] || [ ${ABI} == "x86_64" ]; then - PLATFORM=android-21 -elif [ ${ABI} == "armeabi-v7a" ] || [ ${ABI} == "x86" ]; then - PLATFORM=android-16 -else - echo "Unrecognised ABI: ${ABI}. Supported ABIs are: arm64-v8a, armeabi-v7a, x86_64, x86. If you feel ${ABI} should be supported please file an issue on github.com/google/oboe" - exit 1 -fi - -# Configure the build -echo "Building tests for ${ABI} using ${PLATFORM}" - -CMAKE_ARGS="-H. \ - -B${BUILD_DIR} \ - -DANDROID_ABI=${ABI} \ - -DANDROID_PLATFORM=${PLATFORM} \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DCMAKE_CXX_FLAGS=-std=c++11 \ - -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK}/build/cmake/android.toolchain.cmake \ - -DCMAKE_VERBOSE_MAKEFILE=1" - -mkdir -p ${BUILD_DIR} - -cmake ${CMAKE_ARGS} - -# Perform the build -pushd ${BUILD_DIR} - make -j5 - - if [ $? -eq 0 ]; then - echo "Tests built successfully" - else - echo "Building tests FAILED" - exit 1 - fi - -popd - - -# Push the test binary to the device and run it -echo "Pushing test binary to device/emulator" -adb shell mkdir -p ${REMOTE_DIR} -adb push ${BUILD_DIR}/${TEST_BINARY_FILENAME} ${REMOTE_DIR} - -echo "Running test binary" -adb shell ${REMOTE_DIR}/${TEST_BINARY_FILENAME} \ No newline at end of file diff --git a/tests/testUtilities.cpp b/tests/testUtilities.cpp deleted file mode 100644 index 99451453f..000000000 --- a/tests/testUtilities.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include - -/** - * Tests needing to be written: - * - * - oboe::convertFormatToSizeInBytes() - */ - -using namespace oboe; - -class UtilityFunctions : public ::testing::Test { - - - -}; - -TEST_F(UtilityFunctions, Converts16BitIntegerToSizeOf2Bytes){ - int32_t sizeInBytes = oboe::convertFormatToSizeInBytes(AudioFormat::I16); - ASSERT_EQ(sizeInBytes, 2); -} - -TEST_F(UtilityFunctions, ConvertsFloatToSizeOf4Bytes){ - int32_t sizeInBytes = oboe::convertFormatToSizeInBytes(AudioFormat::Float); - ASSERT_EQ(sizeInBytes, 4); -} \ No newline at end of file