mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2025-07-22 05:45:46 +00:00
Move dead submodules in-tree
Signed-off-by: swurl <swurl@swurl.xyz>
This commit is contained in:
parent
c0cceff365
commit
6c655321e6
4081 changed files with 1185566 additions and 45 deletions
24
externals/oboe/samples/hello-oboe/src/main/AndroidManifest.xml
vendored
Normal file
24
externals/oboe/samples/hello-oboe/src/main/AndroidManifest.xml
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-feature android:name="android.hardware.audio.output" android:required="true" />
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:fullBackupContent="false"
|
||||
android:supportsRtl="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme" >
|
||||
<activity
|
||||
android:name="com.google.oboe.samples.hellooboe.MainActivity"
|
||||
android:label="@string/app_name"
|
||||
android:screenOrientation="portrait"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
60
externals/oboe/samples/hello-oboe/src/main/cpp/CMakeLists.txt
vendored
Normal file
60
externals/oboe/samples/hello-oboe/src/main/cpp/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,60 @@
|
|||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.4.1)
|
||||
|
||||
### INCLUDE OBOE LIBRARY ###
|
||||
|
||||
# Set the path to the Oboe library directory
|
||||
set (OBOE_DIR ../../../../../)
|
||||
|
||||
# Add the Oboe library as a subproject. Since Oboe is an out-of-tree source library we must also
|
||||
# specify a binary directory
|
||||
add_subdirectory(${OBOE_DIR} ./oboe-bin)
|
||||
|
||||
# Include the Oboe headers and shared sample code
|
||||
include_directories(${OBOE_DIR}/include ${OBOE_DIR}/samples/shared)
|
||||
|
||||
# Debug utilities
|
||||
set (DEBUG_UTILS_PATH "${OBOE_DIR}/samples/debug-utils")
|
||||
set (DEBUG_UTILS_SOURCES ${DEBUG_UTILS_PATH}/trace.cpp)
|
||||
include_directories(${DEBUG_UTILS_PATH})
|
||||
|
||||
|
||||
### END OBOE INCLUDE SECTION ###
|
||||
|
||||
|
||||
# App specific sources
|
||||
set (APP_SOURCES
|
||||
jni_bridge.cpp
|
||||
HelloOboeEngine.cpp
|
||||
SoundGenerator.cpp
|
||||
LatencyTuningCallback.cpp
|
||||
)
|
||||
|
||||
# Build the libhello-oboe library
|
||||
add_library(hello-oboe SHARED
|
||||
${DEBUG_UTILS_SOURCES}
|
||||
${APP_SOURCES}
|
||||
)
|
||||
|
||||
# Specify the libraries needed for hello-oboe
|
||||
target_link_libraries(hello-oboe android log oboe)
|
||||
target_link_options(hello-oboe PRIVATE "-Wl,-z,max-page-size=16384")
|
||||
|
||||
# Enable optimization flags: if having problems with source level debugging,
|
||||
# disable -Ofast ( and debug ), re-enable after done debugging.
|
||||
target_compile_options(hello-oboe PRIVATE -Wall -Werror "$<$<CONFIG:RELEASE>:-Ofast>")
|
175
externals/oboe/samples/hello-oboe/src/main/cpp/HelloOboeEngine.cpp
vendored
Normal file
175
externals/oboe/samples/hello-oboe/src/main/cpp/HelloOboeEngine.cpp
vendored
Normal file
|
@ -0,0 +1,175 @@
|
|||
/**
|
||||
* 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 <inttypes.h>
|
||||
#include <memory>
|
||||
|
||||
#include <Oscillator.h>
|
||||
|
||||
#include "HelloOboeEngine.h"
|
||||
#include "SoundGenerator.h"
|
||||
|
||||
|
||||
/**
|
||||
* Main audio engine for the HelloOboe sample. It is responsible for:
|
||||
*
|
||||
* - Creating a callback object which is supplied when constructing the audio stream, and will be
|
||||
* called when the stream starts
|
||||
* - Restarting the stream when user-controllable properties (Audio API, channel count etc) are
|
||||
* changed, and when the stream is disconnected (e.g. when headphones are attached)
|
||||
* - Calculating the audio latency of the stream
|
||||
*
|
||||
*/
|
||||
HelloOboeEngine::HelloOboeEngine()
|
||||
: mLatencyCallback(std::make_shared<LatencyTuningCallback>()),
|
||||
mErrorCallback(std::make_shared<DefaultErrorCallback>(*this)) {
|
||||
}
|
||||
|
||||
double HelloOboeEngine::getCurrentOutputLatencyMillis() {
|
||||
if (!mIsLatencyDetectionSupported) return -1.0;
|
||||
|
||||
std::lock_guard<std::mutex> lock(mLock);
|
||||
if (!mStream) return -1.0;
|
||||
|
||||
oboe::ResultWithValue<double> latencyResult = mStream->calculateLatencyMillis();
|
||||
if (latencyResult) {
|
||||
return latencyResult.value();
|
||||
} else {
|
||||
LOGE("Error calculating latency: %s", oboe::convertToText(latencyResult.error()));
|
||||
return -1.0;
|
||||
}
|
||||
}
|
||||
|
||||
void HelloOboeEngine::setBufferSizeInBursts(int32_t numBursts) {
|
||||
std::lock_guard<std::mutex> lock(mLock);
|
||||
if (!mStream) return;
|
||||
|
||||
mLatencyCallback->setBufferTuneEnabled(numBursts == kBufferSizeAutomatic);
|
||||
auto result = mStream->setBufferSizeInFrames(
|
||||
numBursts * mStream->getFramesPerBurst());
|
||||
if (result) {
|
||||
LOGD("Buffer size successfully changed to %d", result.value());
|
||||
} else {
|
||||
LOGW("Buffer size could not be changed, %d", result.error());
|
||||
}
|
||||
}
|
||||
|
||||
bool HelloOboeEngine::isLatencyDetectionSupported() {
|
||||
return mIsLatencyDetectionSupported;
|
||||
}
|
||||
|
||||
bool HelloOboeEngine::isAAudioRecommended() {
|
||||
return oboe::AudioStreamBuilder::isAAudioRecommended();
|
||||
}
|
||||
|
||||
void HelloOboeEngine::tap(bool isDown) {
|
||||
if (mAudioSource) {
|
||||
mAudioSource->tap(isDown);
|
||||
}
|
||||
}
|
||||
|
||||
oboe::Result HelloOboeEngine::openPlaybackStream() {
|
||||
oboe::AudioStreamBuilder builder;
|
||||
oboe::Result result = builder.setSharingMode(oboe::SharingMode::Exclusive)
|
||||
->setPerformanceMode(oboe::PerformanceMode::LowLatency)
|
||||
->setFormat(oboe::AudioFormat::Float)
|
||||
->setFormatConversionAllowed(true)
|
||||
->setDataCallback(mLatencyCallback)
|
||||
->setErrorCallback(mErrorCallback)
|
||||
->setAudioApi(mAudioApi)
|
||||
->setChannelCount(mChannelCount)
|
||||
->setDeviceId(mDeviceId)
|
||||
->openStream(mStream);
|
||||
if (result == oboe::Result::OK) {
|
||||
mChannelCount = mStream->getChannelCount();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void HelloOboeEngine::restart() {
|
||||
// The stream will have already been closed by the error callback.
|
||||
mLatencyCallback->reset();
|
||||
start();
|
||||
}
|
||||
|
||||
oboe::Result HelloOboeEngine::start(oboe::AudioApi audioApi, int deviceId, int channelCount) {
|
||||
mAudioApi = audioApi;
|
||||
mDeviceId = deviceId;
|
||||
mChannelCount = channelCount;
|
||||
return start();
|
||||
}
|
||||
|
||||
oboe::Result HelloOboeEngine::start() {
|
||||
std::lock_guard<std::mutex> lock(mLock);
|
||||
oboe::Result result = oboe::Result::OK;
|
||||
// It is possible for a stream's device to become disconnected during the open or between
|
||||
// the Open and the Start.
|
||||
// So if it fails to start, close the old stream and try again.
|
||||
int tryCount = 0;
|
||||
do {
|
||||
if (tryCount > 0) {
|
||||
usleep(20 * 1000); // Sleep between tries to give the system time to settle.
|
||||
}
|
||||
mIsLatencyDetectionSupported = false;
|
||||
result = openPlaybackStream();
|
||||
if (result == oboe::Result::OK) {
|
||||
mAudioSource = std::make_shared<SoundGenerator>(mStream->getSampleRate(),
|
||||
mStream->getChannelCount());
|
||||
mLatencyCallback->setSource(
|
||||
std::dynamic_pointer_cast<IRenderableAudio>(mAudioSource));
|
||||
|
||||
LOGD("Stream opened: AudioAPI = %d, channelCount = %d, deviceID = %d",
|
||||
mStream->getAudioApi(),
|
||||
mStream->getChannelCount(),
|
||||
mStream->getDeviceId());
|
||||
|
||||
result = mStream->requestStart();
|
||||
if (result != oboe::Result::OK) {
|
||||
LOGE("Error starting playback stream. Error: %s", oboe::convertToText(result));
|
||||
mStream->close();
|
||||
mStream.reset();
|
||||
} else {
|
||||
mIsLatencyDetectionSupported = (mStream->getTimestamp((CLOCK_MONOTONIC)) !=
|
||||
oboe::Result::ErrorUnimplemented);
|
||||
}
|
||||
} else {
|
||||
LOGE("Error creating playback stream. Error: %s", oboe::convertToText(result));
|
||||
}
|
||||
} while (result != oboe::Result::OK && tryCount++ < 3);
|
||||
return result;
|
||||
}
|
||||
|
||||
oboe::Result HelloOboeEngine::stop() {
|
||||
oboe::Result result = oboe::Result::OK;
|
||||
// Stop, close and delete in case not already closed.
|
||||
std::lock_guard<std::mutex> lock(mLock);
|
||||
if (mStream) {
|
||||
result = mStream->stop();
|
||||
mStream->close();
|
||||
mStream.reset();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
oboe::Result HelloOboeEngine::reopenStream() {
|
||||
if (mStream) {
|
||||
stop();
|
||||
return start();
|
||||
} else {
|
||||
return oboe::Result::OK;
|
||||
}
|
||||
}
|
95
externals/oboe/samples/hello-oboe/src/main/cpp/HelloOboeEngine.h
vendored
Normal file
95
externals/oboe/samples/hello-oboe/src/main/cpp/HelloOboeEngine.h
vendored
Normal file
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* 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_HELLO_OBOE_ENGINE_H
|
||||
#define OBOE_HELLO_OBOE_ENGINE_H
|
||||
|
||||
#include <oboe/Oboe.h>
|
||||
|
||||
#include "SoundGenerator.h"
|
||||
#include "LatencyTuningCallback.h"
|
||||
#include "IRestartable.h"
|
||||
#include "DefaultErrorCallback.h"
|
||||
|
||||
constexpr int32_t kBufferSizeAutomatic = 0;
|
||||
|
||||
class HelloOboeEngine : public IRestartable {
|
||||
|
||||
public:
|
||||
HelloOboeEngine();
|
||||
|
||||
virtual ~HelloOboeEngine() = default;
|
||||
|
||||
void tap(bool isDown);
|
||||
|
||||
/**
|
||||
* Open and start a stream.
|
||||
* @param deviceId the audio device id, can be obtained through an {@link AudioDeviceInfo} object
|
||||
* using Java/JNI.
|
||||
* @return error or OK
|
||||
*/
|
||||
oboe::Result start(oboe::AudioApi audioApi, int deviceId, int channelCount);
|
||||
/* Start using current settings. */
|
||||
oboe::Result start();
|
||||
|
||||
/**
|
||||
* Stop and close the stream.
|
||||
*/
|
||||
oboe::Result stop();
|
||||
|
||||
// From IRestartable
|
||||
void restart() override;
|
||||
|
||||
void setBufferSizeInBursts(int32_t numBursts);
|
||||
|
||||
/**
|
||||
* Calculate the current latency between writing a frame to the output stream and
|
||||
* the same frame being presented to the audio hardware.
|
||||
*
|
||||
* Here's how the calculation works:
|
||||
*
|
||||
* 1) Get the time a particular frame was presented to the audio hardware
|
||||
* @see AudioStream::getTimestamp
|
||||
* 2) From this extrapolate the time which the *next* audio frame written to the stream
|
||||
* will be presented
|
||||
* 3) Assume that the next audio frame is written at the current time
|
||||
* 4) currentLatency = nextFramePresentationTime - nextFrameWriteTime
|
||||
*
|
||||
* @return Output Latency in Milliseconds
|
||||
*/
|
||||
double getCurrentOutputLatencyMillis();
|
||||
|
||||
bool isLatencyDetectionSupported();
|
||||
|
||||
bool isAAudioRecommended();
|
||||
|
||||
private:
|
||||
oboe::Result reopenStream();
|
||||
oboe::Result openPlaybackStream();
|
||||
|
||||
std::shared_ptr<oboe::AudioStream> mStream;
|
||||
std::shared_ptr<LatencyTuningCallback> mLatencyCallback;
|
||||
std::shared_ptr<DefaultErrorCallback> mErrorCallback;
|
||||
std::shared_ptr<SoundGenerator> mAudioSource;
|
||||
bool mIsLatencyDetectionSupported = false;
|
||||
|
||||
int32_t mDeviceId = oboe::Unspecified;
|
||||
int32_t mChannelCount = oboe::Unspecified;
|
||||
oboe::AudioApi mAudioApi = oboe::AudioApi::Unspecified;
|
||||
std::mutex mLock;
|
||||
};
|
||||
|
||||
#endif //OBOE_HELLO_OBOE_ENGINE_H
|
45
externals/oboe/samples/hello-oboe/src/main/cpp/LatencyTuningCallback.cpp
vendored
Normal file
45
externals/oboe/samples/hello-oboe/src/main/cpp/LatencyTuningCallback.cpp
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* 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 "LatencyTuningCallback.h"
|
||||
|
||||
oboe::DataCallbackResult LatencyTuningCallback::onAudioReady(
|
||||
oboe::AudioStream *oboeStream, void *audioData, int32_t numFrames) {
|
||||
if (oboeStream != mStream) {
|
||||
mStream = oboeStream;
|
||||
mLatencyTuner = std::make_unique<oboe::LatencyTuner>(*oboeStream);
|
||||
}
|
||||
if (mBufferTuneEnabled
|
||||
&& mLatencyTuner
|
||||
&& oboeStream->getAudioApi() == oboe::AudioApi::AAudio) {
|
||||
mLatencyTuner->tune();
|
||||
}
|
||||
auto underrunCountResult = oboeStream->getXRunCount();
|
||||
int bufferSize = oboeStream->getBufferSizeInFrames();
|
||||
/**
|
||||
* The following output can be seen by running a systrace. Tracing is preferable to logging
|
||||
* inside the callback since tracing does not block.
|
||||
*
|
||||
* See https://developer.android.com/studio/profile/systrace-commandline.html
|
||||
*/
|
||||
if (Trace::isEnabled()) Trace::beginSection("numFrames %d, Underruns %d, buffer size %d",
|
||||
numFrames, underrunCountResult.value(), bufferSize);
|
||||
auto result = DefaultDataCallback::onAudioReady(oboeStream, audioData, numFrames);
|
||||
if (Trace::isEnabled()) Trace::endSection();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
67
externals/oboe/samples/hello-oboe/src/main/cpp/LatencyTuningCallback.h
vendored
Normal file
67
externals/oboe/samples/hello-oboe/src/main/cpp/LatencyTuningCallback.h
vendored
Normal file
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
* 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 SAMPLES_LATENCY_TUNING_CALLBACK_H
|
||||
#define SAMPLES_LATENCY_TUNING_CALLBACK_H
|
||||
|
||||
#include <oboe/Oboe.h>
|
||||
#include <oboe/LatencyTuner.h>
|
||||
|
||||
#include <TappableAudioSource.h>
|
||||
#include <DefaultDataCallback.h>
|
||||
#include <trace.h>
|
||||
|
||||
/**
|
||||
* This callback object extends the functionality of `DefaultDataCallback` by automatically
|
||||
* tuning the latency of the audio stream. @see onAudioReady for more details on this.
|
||||
*
|
||||
* It also demonstrates how to use tracing functions for logging inside the audio callback without
|
||||
* blocking.
|
||||
*/
|
||||
class LatencyTuningCallback: public DefaultDataCallback {
|
||||
public:
|
||||
LatencyTuningCallback() : DefaultDataCallback() {
|
||||
|
||||
// Initialize the trace functions, this enables you to output trace statements without
|
||||
// blocking. See https://developer.android.com/studio/profile/systrace-commandline.html
|
||||
Trace::initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Every time the playback stream requires data this method will be called.
|
||||
*
|
||||
* @param audioStream the audio stream which is requesting data, this is the mPlayStream object
|
||||
* @param audioData an empty buffer into which we can write our audio data
|
||||
* @param numFrames the number of audio frames which are required
|
||||
* @return Either oboe::DataCallbackResult::Continue if the stream should continue requesting data
|
||||
* or oboe::DataCallbackResult::Stop if the stream should stop.
|
||||
*/
|
||||
oboe::DataCallbackResult onAudioReady(oboe::AudioStream *oboeStream, void *audioData, int32_t numFrames) override;
|
||||
|
||||
void setBufferTuneEnabled(bool enabled) {mBufferTuneEnabled = enabled;}
|
||||
|
||||
void useStream(std::shared_ptr<oboe::AudioStream> stream);
|
||||
|
||||
private:
|
||||
bool mBufferTuneEnabled = true;
|
||||
|
||||
// This will be used to automatically tune the buffer size of the stream, obtaining optimal latency
|
||||
std::unique_ptr<oboe::LatencyTuner> mLatencyTuner;
|
||||
oboe::AudioStream *mStream = nullptr;
|
||||
};
|
||||
|
||||
#endif //SAMPLES_LATENCY_TUNING_CALLBACK_H
|
51
externals/oboe/samples/hello-oboe/src/main/cpp/SoundGenerator.cpp
vendored
Normal file
51
externals/oboe/samples/hello-oboe/src/main/cpp/SoundGenerator.cpp
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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 "SoundGenerator.h"
|
||||
|
||||
SoundGenerator::SoundGenerator(int32_t sampleRate, int32_t channelCount) :
|
||||
TappableAudioSource(sampleRate, channelCount)
|
||||
, mOscillators(std::make_unique<Oscillator[]>(channelCount)){
|
||||
|
||||
double frequency = 440.0;
|
||||
constexpr double interval = 110.0;
|
||||
constexpr float amplitude = 1.0;
|
||||
|
||||
// Set up the oscillators
|
||||
for (int i = 0; i < mChannelCount; ++i) {
|
||||
mOscillators[i].setFrequency(frequency);
|
||||
mOscillators[i].setSampleRate(mSampleRate);
|
||||
mOscillators[i].setAmplitude(amplitude);
|
||||
frequency += interval;
|
||||
}
|
||||
}
|
||||
|
||||
void SoundGenerator::renderAudio(float *audioData, int32_t numFrames) {
|
||||
// Render each oscillator into its own channel
|
||||
std::fill_n(mBuffer.get(), kSharedBufferSize, 0);
|
||||
for (int i = 0; i < mChannelCount; ++i) {
|
||||
mOscillators[i].renderAudio(mBuffer.get(), numFrames);
|
||||
for (int j = 0; j < numFrames; ++j) {
|
||||
audioData[(j * mChannelCount) + i] = mBuffer[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoundGenerator::tap(bool isOn) {
|
||||
for (int i = 0; i < mChannelCount; ++i) {
|
||||
mOscillators[i].setWaveOn(isOn);
|
||||
}
|
||||
}
|
58
externals/oboe/samples/hello-oboe/src/main/cpp/SoundGenerator.h
vendored
Normal file
58
externals/oboe/samples/hello-oboe/src/main/cpp/SoundGenerator.h
vendored
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef SAMPLES_SOUNDGENERATOR_H
|
||||
#define SAMPLES_SOUNDGENERATOR_H
|
||||
|
||||
|
||||
#include <Oscillator.h>
|
||||
#include <TappableAudioSource.h>
|
||||
|
||||
/**
|
||||
* Generates a fixed frequency tone for each channel.
|
||||
* Implements RenderableTap (sound source with toggle) which is required for AudioEngines.
|
||||
*/
|
||||
class SoundGenerator : public TappableAudioSource {
|
||||
static constexpr size_t kSharedBufferSize = 1024;
|
||||
public:
|
||||
/**
|
||||
* Create a new SoundGenerator object.
|
||||
*
|
||||
* @param sampleRate - The output sample rate.
|
||||
* @param maxFrames - The maximum number of audio frames which will be rendered, this is used to
|
||||
* calculate this object's internal buffer size.
|
||||
* @param channelCount - The number of channels in the output, one tone will be created for each
|
||||
* channel, the output will be interlaced.
|
||||
*
|
||||
*/
|
||||
SoundGenerator(int32_t sampleRate, int32_t channelCount);
|
||||
~SoundGenerator() = default;
|
||||
|
||||
SoundGenerator(SoundGenerator&& other) = default;
|
||||
SoundGenerator& operator= (SoundGenerator&& other) = default;
|
||||
|
||||
// Switch the tones on
|
||||
void tap(bool isOn) override;
|
||||
|
||||
void renderAudio(float *audioData, int32_t numFrames) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<Oscillator[]> mOscillators;
|
||||
std::unique_ptr<float[]> mBuffer = std::make_unique<float[]>(kSharedBufferSize);
|
||||
};
|
||||
|
||||
|
||||
#endif //SAMPLES_SOUNDGENERATOR_H
|
90
externals/oboe/samples/hello-oboe/src/main/cpp/jni_bridge.cpp
vendored
Normal file
90
externals/oboe/samples/hello-oboe/src/main/cpp/jni_bridge.cpp
vendored
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* 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 <jni.h>
|
||||
#include <oboe/Oboe.h>
|
||||
#include "HelloOboeEngine.h"
|
||||
#include "logging_macros.h"
|
||||
|
||||
extern "C" {
|
||||
/* We only need one HelloOboeEngine and it is needed for the entire time.
|
||||
* So just allocate one statically. */
|
||||
static HelloOboeEngine sEngine;
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_com_google_oboe_samples_hellooboe_PlaybackEngine_startEngine(
|
||||
JNIEnv *env,
|
||||
jclass,
|
||||
int audioApi, int deviceId, int channelCount) {
|
||||
return static_cast<jint>(sEngine.start((oboe::AudioApi)audioApi, deviceId, channelCount));
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_com_google_oboe_samples_hellooboe_PlaybackEngine_stopEngine(
|
||||
JNIEnv *env,
|
||||
jclass) {
|
||||
return static_cast<jint>(sEngine.stop());
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_oboe_samples_hellooboe_PlaybackEngine_setToneOn(
|
||||
JNIEnv *env,
|
||||
jclass,
|
||||
jboolean isToneOn) {
|
||||
|
||||
sEngine.tap(isToneOn);
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_oboe_samples_hellooboe_PlaybackEngine_setBufferSizeInBursts(
|
||||
JNIEnv *env,
|
||||
jclass,
|
||||
jint bufferSizeInBursts) {
|
||||
sEngine.setBufferSizeInBursts(bufferSizeInBursts);
|
||||
}
|
||||
|
||||
JNIEXPORT jdouble JNICALL
|
||||
Java_com_google_oboe_samples_hellooboe_PlaybackEngine_getCurrentOutputLatencyMillis(
|
||||
JNIEnv *env,
|
||||
jclass) {
|
||||
return static_cast<jdouble>(sEngine.getCurrentOutputLatencyMillis());
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_oboe_samples_hellooboe_PlaybackEngine_isLatencyDetectionSupported(
|
||||
JNIEnv *env,
|
||||
jclass) {
|
||||
return (sEngine.isLatencyDetectionSupported() ? JNI_TRUE : JNI_FALSE);
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_oboe_samples_hellooboe_PlaybackEngine_isAAudioRecommended(
|
||||
JNIEnv *env,
|
||||
jclass) {
|
||||
return (sEngine.isAAudioRecommended() ? JNI_TRUE : JNI_FALSE);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_oboe_samples_hellooboe_PlaybackEngine_setDefaultStreamValues(
|
||||
JNIEnv *env,
|
||||
jclass,
|
||||
jint sampleRate,
|
||||
jint framesPerBurst) {
|
||||
oboe::DefaultStreamValues::SampleRate = (int32_t) sampleRate;
|
||||
oboe::DefaultStreamValues::FramesPerBurst = (int32_t) framesPerBurst;
|
||||
}
|
||||
} // extern "C"
|
|
@ -0,0 +1,56 @@
|
|||
package com.google.oboe.samples.hellooboe;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Message;
|
||||
|
||||
/** Run Audio function in a background thread.
|
||||
* This will avoid ANRs that can occur if the AudioServer or HAL crashes
|
||||
* and takes a long time to recover.
|
||||
*
|
||||
* This thread will run for the lifetime of the app so only make one BackgroundRunner
|
||||
* and reuse it.
|
||||
*/
|
||||
public abstract class BackgroundRunner {
|
||||
private Handler mHandler;
|
||||
private HandlerThread mThread = new HandlerThread("BackgroundRunner");
|
||||
|
||||
public BackgroundRunner() {
|
||||
mThread.start();
|
||||
mHandler = new Handler(mThread.getLooper()) {
|
||||
public void handleMessage(Message message) {
|
||||
super.handleMessage(message);
|
||||
handleMessageInBackground(message);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param message
|
||||
* @return true if the message was successfully queued
|
||||
*/
|
||||
public boolean sendMessage(Message message) {
|
||||
return mHandler.sendMessage(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param what command code for background operation
|
||||
* @param arg1 optional argument
|
||||
* @return true if the message was successfully queued
|
||||
*/
|
||||
public boolean sendMessage(int what, int arg1) {
|
||||
Message message = mHandler.obtainMessage();
|
||||
message.what = what;
|
||||
message.arg1 = arg1;
|
||||
return sendMessage(message);
|
||||
}
|
||||
|
||||
public boolean sendMessage(int what) {
|
||||
return sendMessage(what, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this in your app.
|
||||
*/
|
||||
abstract void handleMessageInBackground(Message message);
|
||||
}
|
456
externals/oboe/samples/hello-oboe/src/main/java/com/google/oboe/samples/hellooboe/MainActivity.java
vendored
Normal file
456
externals/oboe/samples/hello-oboe/src/main/java/com/google/oboe/samples/hellooboe/MainActivity.java
vendored
Normal file
|
@ -0,0 +1,456 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.oboe.samples.hellooboe;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.media.AudioDeviceInfo;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.SimpleAdapter;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.oboe.samples.audio_device.AudioDeviceListEntry;
|
||||
import com.google.oboe.samples.audio_device.AudioDeviceSpinner;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
public class MainActivity extends Activity {
|
||||
private static final String TAG = "HelloOboe";
|
||||
private static final long UPDATE_LATENCY_EVERY_MILLIS = 1000;
|
||||
private static final Integer[] CHANNEL_COUNT_OPTIONS = {1, 2, 3, 4, 5, 6, 7, 8};
|
||||
// Default to Stereo (OPTIONS is zero-based array so index 1 = 2 channels)
|
||||
private static final int CHANNEL_COUNT_DEFAULT_OPTION_INDEX = 1;
|
||||
private static final int[] BUFFER_SIZE_OPTIONS = {0, 1, 2, 4, 8};
|
||||
private static final String[] AUDIO_API_OPTIONS = {"Unspecified", "OpenSL ES", "AAudio"};
|
||||
private static final int OBOE_API_OPENSL_ES = 1;
|
||||
// Default all other spinners to the first option on the list
|
||||
private static final int SPINNER_DEFAULT_OPTION_INDEX = 0;
|
||||
|
||||
private Spinner mAudioApiSpinner;
|
||||
private AudioDeviceSpinner mPlaybackDeviceSpinner;
|
||||
private Spinner mChannelCountSpinner;
|
||||
private Spinner mBufferSizeSpinner;
|
||||
private TextView mLatencyText;
|
||||
private Timer mLatencyUpdater;
|
||||
private boolean mScoStarted = false;
|
||||
|
||||
/** Commands for background thread. */
|
||||
private static final int WHAT_START = 100;
|
||||
private static final int WHAT_STOP = 101;
|
||||
private static final int WHAT_SET_DEVICE_ID = 102;
|
||||
private static final int WHAT_SET_AUDIO_API = 103;
|
||||
private static final int WHAT_SET_CHANNEL_COUNT = 104;
|
||||
private BackgroundRunner mRunner = new MyBackgroundRunner();
|
||||
|
||||
private class MyBackgroundRunner extends BackgroundRunner {
|
||||
// These are initialized to zero by Java.
|
||||
// Zero matches the oboe::Unspecified value.
|
||||
int audioApi;
|
||||
int deviceId;
|
||||
int channelCount;
|
||||
|
||||
@Override
|
||||
/* Execute this in a background thread to avoid ANRs. */
|
||||
void handleMessageInBackground(Message message) {
|
||||
int what = message.what;
|
||||
int arg1 = message.arg1;
|
||||
Log.i(MainActivity.TAG, "got background message, what = " + what + ", arg1 = " + arg1);
|
||||
int result = 0;
|
||||
boolean restart = false;
|
||||
switch (what) {
|
||||
case WHAT_START:
|
||||
result = PlaybackEngine.startEngine(audioApi, deviceId, channelCount);
|
||||
break;
|
||||
case WHAT_STOP:
|
||||
result = PlaybackEngine.stopEngine();
|
||||
break;
|
||||
case WHAT_SET_AUDIO_API:
|
||||
if (audioApi != arg1) {
|
||||
audioApi = arg1;
|
||||
restart = true;
|
||||
}
|
||||
break;
|
||||
case WHAT_SET_DEVICE_ID:
|
||||
if (deviceId != arg1) {
|
||||
deviceId = arg1;
|
||||
restart = true;
|
||||
}
|
||||
break;
|
||||
case WHAT_SET_CHANNEL_COUNT:
|
||||
if (channelCount != arg1) {
|
||||
channelCount = arg1;
|
||||
restart = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (restart) {
|
||||
int result1 = PlaybackEngine.stopEngine();
|
||||
int result2 = PlaybackEngine.startEngine(audioApi, deviceId, channelCount);
|
||||
result = (result2 != 0) ? result2 : result1;
|
||||
}
|
||||
if (result != 0) {
|
||||
Log.e(TAG, "audio error " + result);
|
||||
showToast("Error in audio =" + result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Hook to user control to start / stop audio playback:
|
||||
* touch-down: start, and keeps on playing
|
||||
* touch-up: stop.
|
||||
* simply pass the events to native side.
|
||||
*/
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
switch (event.getAction()) {
|
||||
case (MotionEvent.ACTION_DOWN):
|
||||
PlaybackEngine.setToneOn(true);
|
||||
break;
|
||||
case (MotionEvent.ACTION_UP):
|
||||
PlaybackEngine.setToneOn(false);
|
||||
break;
|
||||
}
|
||||
return super.onTouchEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
mLatencyText = findViewById(R.id.latencyText);
|
||||
setupAudioApiSpinner();
|
||||
setupPlaybackDeviceSpinner();
|
||||
setupChannelCountSpinner();
|
||||
setupBufferSizeSpinner();
|
||||
}
|
||||
|
||||
/*
|
||||
* Creating engine in onResume() and destroying in onPause() so the stream retains exclusive
|
||||
* mode only while in focus. This allows other apps to reclaim exclusive stream mode.
|
||||
*/
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
PlaybackEngine.setDefaultStreamValues(this);
|
||||
setupLatencyUpdater();
|
||||
|
||||
// Return the spinner states to their default value
|
||||
mChannelCountSpinner.setSelection(CHANNEL_COUNT_DEFAULT_OPTION_INDEX);
|
||||
mPlaybackDeviceSpinner.setSelection(SPINNER_DEFAULT_OPTION_INDEX);
|
||||
mBufferSizeSpinner.setSelection(SPINNER_DEFAULT_OPTION_INDEX);
|
||||
if (PlaybackEngine.isAAudioRecommended()) {
|
||||
mAudioApiSpinner.setSelection(SPINNER_DEFAULT_OPTION_INDEX);
|
||||
} else {
|
||||
mAudioApiSpinner.setSelection(OBOE_API_OPENSL_ES);
|
||||
mAudioApiSpinner.setEnabled(false);
|
||||
}
|
||||
|
||||
startAudioAsync();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
if (mLatencyUpdater != null) mLatencyUpdater.cancel();
|
||||
stopAudioAsync();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
clearCommunicationDevice();
|
||||
} else {
|
||||
if (mScoStarted) {
|
||||
stopBluetoothSco();
|
||||
mScoStarted = false;
|
||||
}
|
||||
}
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
private void setupChannelCountSpinner() {
|
||||
mChannelCountSpinner = findViewById(R.id.channelCountSpinner);
|
||||
|
||||
ArrayAdapter<Integer> channelCountAdapter = new ArrayAdapter<>(this, R.layout.channel_counts_spinner, CHANNEL_COUNT_OPTIONS);
|
||||
mChannelCountSpinner.setAdapter(channelCountAdapter);
|
||||
mChannelCountSpinner.setSelection(CHANNEL_COUNT_DEFAULT_OPTION_INDEX);
|
||||
|
||||
mChannelCountSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
setChannelCountAsync(CHANNEL_COUNT_OPTIONS[mChannelCountSpinner.getSelectedItemPosition()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> adapterView) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupBufferSizeSpinner() {
|
||||
mBufferSizeSpinner = findViewById(R.id.bufferSizeSpinner);
|
||||
mBufferSizeSpinner.setAdapter(new SimpleAdapter(
|
||||
this,
|
||||
createBufferSizeOptionsList(), // list of buffer size options
|
||||
R.layout.buffer_sizes_spinner, // the xml layout
|
||||
new String[]{getString(R.string.buffer_size_description_key)}, // field to display
|
||||
new int[]{R.id.bufferSizeOption} // View to show field in
|
||||
));
|
||||
|
||||
mBufferSizeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
PlaybackEngine.setBufferSizeInBursts(getBufferSizeInBursts());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> adapterView) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupPlaybackDeviceSpinner() {
|
||||
mPlaybackDeviceSpinner = findViewById(R.id.playbackDevicesSpinner);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
mPlaybackDeviceSpinner.setDirectionType(AudioManager.GET_DEVICES_OUTPUTS);
|
||||
mPlaybackDeviceSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
// To use Bluetooth SCO, setCommunicationDevice() or startBluetoothSco() must
|
||||
// be called. The AudioManager.startBluetoothSco() is deprecated in Android T.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
if (isScoDevice(getPlaybackDeviceId())){
|
||||
setCommunicationDevice(getPlaybackDeviceId());
|
||||
} else {
|
||||
clearCommunicationDevice();
|
||||
}
|
||||
} else {
|
||||
// Start Bluetooth SCO if needed.
|
||||
if (isScoDevice(getPlaybackDeviceId()) && !mScoStarted) {
|
||||
startBluetoothSco();
|
||||
mScoStarted = true;
|
||||
} else if (!isScoDevice(getPlaybackDeviceId()) && mScoStarted) {
|
||||
stopBluetoothSco();
|
||||
mScoStarted = false;
|
||||
}
|
||||
}
|
||||
setAudioDeviceIdAsync(getPlaybackDeviceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> adapterView) {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
mPlaybackDeviceSpinner.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void setupAudioApiSpinner() {
|
||||
mAudioApiSpinner = findViewById(R.id.audioApiSpinner);
|
||||
mAudioApiSpinner.setAdapter(new SimpleAdapter(
|
||||
this,
|
||||
createAudioApisOptionsList(),
|
||||
R.layout.audio_apis_spinner, // the xml layout
|
||||
new String[]{getString(R.string.audio_api_description_key)}, // field to display
|
||||
new int[]{R.id.audioApiOption} // View to show field in
|
||||
));
|
||||
|
||||
mAudioApiSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
setAudioApiAsync(i);
|
||||
if (i == OBOE_API_OPENSL_ES) {
|
||||
mPlaybackDeviceSpinner.setSelection(SPINNER_DEFAULT_OPTION_INDEX);
|
||||
mPlaybackDeviceSpinner.setEnabled(false);
|
||||
} else {
|
||||
mPlaybackDeviceSpinner.setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> adapterView) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private int getPlaybackDeviceId() {
|
||||
return ((AudioDeviceListEntry) mPlaybackDeviceSpinner.getSelectedItem()).getId();
|
||||
}
|
||||
|
||||
private int getBufferSizeInBursts() {
|
||||
@SuppressWarnings("unchecked")
|
||||
HashMap<String, String> selectedOption = (HashMap<String, String>)
|
||||
mBufferSizeSpinner.getSelectedItem();
|
||||
|
||||
String valueKey = getString(R.string.buffer_size_value_key);
|
||||
|
||||
// parseInt will throw a NumberFormatException if the string doesn't contain a valid integer
|
||||
// representation. We don't need to worry about this because the values are derived from
|
||||
// the BUFFER_SIZE_OPTIONS int array.
|
||||
return Integer.parseInt(Objects.requireNonNull(selectedOption.get(valueKey)));
|
||||
}
|
||||
|
||||
private void setupLatencyUpdater() {
|
||||
//Update the latency every 1s
|
||||
TimerTask latencyUpdateTask = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
final String latencyStr;
|
||||
if (PlaybackEngine.isLatencyDetectionSupported()) {
|
||||
double latency = PlaybackEngine.getCurrentOutputLatencyMillis();
|
||||
if (latency >= 0) {
|
||||
latencyStr = String.format(Locale.getDefault(), "%.2fms", latency);
|
||||
} else {
|
||||
latencyStr = "Unknown";
|
||||
}
|
||||
} else {
|
||||
latencyStr = getString(R.string.only_supported_on_api_26);
|
||||
}
|
||||
|
||||
runOnUiThread(() -> mLatencyText.setText(getString(R.string.latency, latencyStr)));
|
||||
}
|
||||
};
|
||||
mLatencyUpdater = new Timer();
|
||||
mLatencyUpdater.schedule(latencyUpdateTask, 0, UPDATE_LATENCY_EVERY_MILLIS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a list of buffer size options which can be used to populate a SimpleAdapter.
|
||||
* Each option has a description and a value. The description is always equal to the value,
|
||||
* except when the value is zero as this indicates that the buffer size should be set
|
||||
* automatically by the audio engine
|
||||
*
|
||||
* @return list of buffer size options
|
||||
*/
|
||||
private List<HashMap<String, String>> createBufferSizeOptionsList() {
|
||||
|
||||
ArrayList<HashMap<String, String>> bufferSizeOptions = new ArrayList<>();
|
||||
|
||||
for (int i : BUFFER_SIZE_OPTIONS) {
|
||||
HashMap<String, String> option = new HashMap<>();
|
||||
String strValue = String.valueOf(i);
|
||||
String description = (i == 0) ? getString(R.string.automatic) : strValue;
|
||||
option.put(getString(R.string.buffer_size_description_key), description);
|
||||
option.put(getString(R.string.buffer_size_value_key), strValue);
|
||||
|
||||
bufferSizeOptions.add(option);
|
||||
}
|
||||
|
||||
return bufferSizeOptions;
|
||||
}
|
||||
|
||||
private List<HashMap<String, String>> createAudioApisOptionsList() {
|
||||
|
||||
ArrayList<HashMap<String, String>> audioApiOptions = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < AUDIO_API_OPTIONS.length; i++) {
|
||||
HashMap<String, String> option = new HashMap<>();
|
||||
option.put(getString(R.string.buffer_size_description_key), AUDIO_API_OPTIONS[i]);
|
||||
option.put(getString(R.string.buffer_size_value_key), String.valueOf(i));
|
||||
audioApiOptions.add(option);
|
||||
}
|
||||
return audioApiOptions;
|
||||
}
|
||||
|
||||
protected void showToast(final String message) {
|
||||
runOnUiThread(() -> Toast.makeText(MainActivity.this,
|
||||
message,
|
||||
Toast.LENGTH_SHORT).show());
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.S)
|
||||
private void setCommunicationDevice(int deviceId) {
|
||||
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
|
||||
final AudioDeviceInfo[] devices;
|
||||
devices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
|
||||
for (AudioDeviceInfo device : devices) {
|
||||
if (device.getId() == deviceId) {
|
||||
audioManager.setCommunicationDevice(device);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.S)
|
||||
private void clearCommunicationDevice() {
|
||||
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
|
||||
audioManager.clearCommunicationDevice();
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
private boolean isScoDevice(int deviceId) {
|
||||
if (deviceId == 0) return false; // Unspecified
|
||||
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
|
||||
final AudioDeviceInfo[] devices;
|
||||
devices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
|
||||
for (AudioDeviceInfo device : devices) {
|
||||
if (device.getId() == deviceId) {
|
||||
return device.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_SCO;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void startBluetoothSco() {
|
||||
AudioManager myAudioMgr = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
|
||||
myAudioMgr.startBluetoothSco();
|
||||
}
|
||||
|
||||
private void stopBluetoothSco() {
|
||||
AudioManager myAudioMgr = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
|
||||
myAudioMgr.stopBluetoothSco();
|
||||
}
|
||||
|
||||
|
||||
void startAudioAsync() {
|
||||
mRunner.sendMessage(WHAT_START);
|
||||
}
|
||||
|
||||
void stopAudioAsync() {
|
||||
mRunner.sendMessage(WHAT_STOP);
|
||||
}
|
||||
|
||||
void setAudioApiAsync(int audioApi){
|
||||
mRunner.sendMessage(WHAT_SET_AUDIO_API, audioApi);
|
||||
}
|
||||
|
||||
void setAudioDeviceIdAsync(int deviceId){
|
||||
mRunner.sendMessage(WHAT_SET_DEVICE_ID, deviceId);
|
||||
}
|
||||
|
||||
void setChannelCountAsync(int channelCount) {
|
||||
mRunner.sendMessage(WHAT_SET_CHANNEL_COUNT, channelCount);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package com.google.oboe.samples.hellooboe;
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
|
||||
public class PlaybackEngine {
|
||||
// Load native library
|
||||
static {
|
||||
System.loadLibrary("hello-oboe");
|
||||
}
|
||||
|
||||
static void setDefaultStreamValues(Context context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1){
|
||||
AudioManager myAudioMgr = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
String sampleRateStr = myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
|
||||
int defaultSampleRate = Integer.parseInt(sampleRateStr);
|
||||
String framesPerBurstStr = myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
|
||||
int defaultFramesPerBurst = Integer.parseInt(framesPerBurstStr);
|
||||
|
||||
setDefaultStreamValues(defaultSampleRate, defaultFramesPerBurst);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Native methods that require audioserver calls and might take several seconds.
|
||||
static native int startEngine(int audioApi, int deviceId, int channelCount);
|
||||
static native int stopEngine();
|
||||
|
||||
// Native methods that only talk to the native client code.
|
||||
static native void setToneOn(boolean isToneOn);
|
||||
static native void setBufferSizeInBursts(int bufferSizeInBursts);
|
||||
static native double getCurrentOutputLatencyMillis();
|
||||
static native boolean isLatencyDetectionSupported();
|
||||
static native boolean isAAudioRecommended();
|
||||
static native void setDefaultStreamValues(int sampleRate, int framesPerBurst);
|
||||
}
|
154
externals/oboe/samples/hello-oboe/src/main/res/layout/activity_main.xml
vendored
Normal file
154
externals/oboe/samples/hello-oboe/src/main/res/layout/activity_main.xml
vendored
Normal file
|
@ -0,0 +1,154 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 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.
|
||||
-->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<!-- Audio API container -->
|
||||
<LinearLayout
|
||||
android:id="@+id/audioApisContainer"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_marginTop="@dimen/activity_vertical_margin"
|
||||
android:orientation="horizontal"
|
||||
>
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/audio_api"
|
||||
android:layout_marginStart="@dimen/activity_horizontal_margin"
|
||||
android:id="@+id/audioApisTitleText"
|
||||
android:layout_marginLeft="@dimen/activity_horizontal_margin"
|
||||
android:layout_gravity="center_vertical"
|
||||
/>
|
||||
<Spinner
|
||||
android:id="@+id/audioApiSpinner"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/activity_horizontal_margin"
|
||||
android:layout_marginLeft="@dimen/activity_horizontal_margin"
|
||||
android:layout_gravity="center_vertical"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Playback device container -->
|
||||
<LinearLayout
|
||||
android:id="@+id/playbackDevicesContainer"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintTop_toBottomOf="@id/audioApisContainer"
|
||||
app:layout_constraintLeft_toLeftOf="parent">
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/playback_device"
|
||||
android:layout_marginStart="@dimen/activity_horizontal_margin"
|
||||
android:id="@+id/playbackDeviceTitleText"
|
||||
android:layout_marginLeft="@dimen/activity_horizontal_margin"
|
||||
android:layout_gravity="center_vertical"/>
|
||||
|
||||
<com.google.oboe.samples.audio_device.AudioDeviceSpinner
|
||||
android:id="@+id/playbackDevicesSpinner"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/activity_horizontal_margin"
|
||||
android:layout_marginLeft="@dimen/activity_horizontal_margin"
|
||||
android:layout_gravity="center_vertical"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Channel count container -->
|
||||
<LinearLayout
|
||||
android:id="@+id/channelCountContainer"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintTop_toBottomOf="@id/playbackDevicesContainer"
|
||||
app:layout_constraintLeft_toLeftOf="parent">
|
||||
<TextView
|
||||
android:id="@+id/channelCountTitleText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/activity_horizontal_margin"
|
||||
android:text="Channel count"
|
||||
android:layout_marginStart="@dimen/activity_horizontal_margin"
|
||||
android:layout_gravity="center_vertical"/>
|
||||
<Spinner
|
||||
android:id="@+id/channelCountSpinner"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/activity_horizontal_margin"
|
||||
android:layout_marginStart="@dimen/activity_horizontal_margin"
|
||||
android:layout_gravity="center_vertical"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Buffer size container -->
|
||||
<LinearLayout
|
||||
android:id="@+id/bufferSizeContainer"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintTop_toBottomOf="@id/channelCountContainer"
|
||||
app:layout_constraintLeft_toLeftOf="parent">
|
||||
<TextView
|
||||
android:id="@+id/bufferSizeTitleText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/activity_horizontal_margin"
|
||||
android:text="@string/buffer_size_title"
|
||||
android:layout_marginStart="@dimen/activity_horizontal_margin"
|
||||
android:layout_gravity="center_vertical"/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/bufferSizeSpinner"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/activity_horizontal_margin"
|
||||
android:layout_marginStart="@dimen/activity_horizontal_margin"
|
||||
android:layout_gravity="center_vertical"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Audio stream latency -->
|
||||
<TextView
|
||||
android:id="@+id/latencyText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/activity_horizontal_margin"
|
||||
android:layout_marginTop="12dp"
|
||||
android:text="@string/latency"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/bufferSizeContainer"
|
||||
android:layout_marginStart="@dimen/activity_horizontal_margin" />
|
||||
|
||||
<!-- User instructions -->
|
||||
<TextView
|
||||
android:id="@+id/userInstructionView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/activity_horizontal_margin"
|
||||
android:layout_marginTop="20dp"
|
||||
android:lines="3"
|
||||
android:text="@string/init_status"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/latencyText"
|
||||
android:layout_marginStart="@dimen/activity_horizontal_margin" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
23
externals/oboe/samples/hello-oboe/src/main/res/layout/audio_apis_spinner.xml
vendored
Normal file
23
externals/oboe/samples/hello-oboe/src/main/res/layout/audio_apis_spinner.xml
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 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.
|
||||
-->
|
||||
<TextView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/audioApiOption"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/default_padding"
|
||||
/>
|
23
externals/oboe/samples/hello-oboe/src/main/res/layout/buffer_sizes_spinner.xml
vendored
Normal file
23
externals/oboe/samples/hello-oboe/src/main/res/layout/buffer_sizes_spinner.xml
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 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.
|
||||
-->
|
||||
<TextView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/bufferSizeOption"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/default_padding"
|
||||
/>
|
23
externals/oboe/samples/hello-oboe/src/main/res/layout/channel_counts_spinner.xml
vendored
Normal file
23
externals/oboe/samples/hello-oboe/src/main/res/layout/channel_counts_spinner.xml
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 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.
|
||||
-->
|
||||
<TextView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/channelCountOption"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/default_padding"
|
||||
/>
|
BIN
externals/oboe/samples/hello-oboe/src/main/res/mipmap-hdpi/ic_launcher.png
vendored
Normal file
BIN
externals/oboe/samples/hello-oboe/src/main/res/mipmap-hdpi/ic_launcher.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.3 KiB |
BIN
externals/oboe/samples/hello-oboe/src/main/res/mipmap-mdpi/ic_launcher.png
vendored
Normal file
BIN
externals/oboe/samples/hello-oboe/src/main/res/mipmap-mdpi/ic_launcher.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
BIN
externals/oboe/samples/hello-oboe/src/main/res/mipmap-xhdpi/ic_launcher.png
vendored
Normal file
BIN
externals/oboe/samples/hello-oboe/src/main/res/mipmap-xhdpi/ic_launcher.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.7 KiB |
BIN
externals/oboe/samples/hello-oboe/src/main/res/mipmap-xxhdpi/ic_launcher.png
vendored
Normal file
BIN
externals/oboe/samples/hello-oboe/src/main/res/mipmap-xxhdpi/ic_launcher.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.5 KiB |
5
externals/oboe/samples/hello-oboe/src/main/res/values-v21/styles.xml
vendored
Normal file
5
externals/oboe/samples/hello-oboe/src/main/res/values-v21/styles.xml
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="AppTheme" parent="android:Theme.Material.Light">
|
||||
</style>
|
||||
</resources>
|
6
externals/oboe/samples/hello-oboe/src/main/res/values-w820dp/dimens.xml
vendored
Normal file
6
externals/oboe/samples/hello-oboe/src/main/res/values-w820dp/dimens.xml
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
<resources>
|
||||
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
|
||||
(such as screen margins) for screens with more than 820dp of available width. This
|
||||
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
|
||||
<dimen name="activity_horizontal_margin">64dp</dimen>
|
||||
</resources>
|
5
externals/oboe/samples/hello-oboe/src/main/res/values/dimens.xml
vendored
Normal file
5
externals/oboe/samples/hello-oboe/src/main/res/values/dimens.xml
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
<resources>
|
||||
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||
</resources>
|
16
externals/oboe/samples/hello-oboe/src/main/res/values/strings.xml
vendored
Normal file
16
externals/oboe/samples/hello-oboe/src/main/res/values/strings.xml
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
<resources>
|
||||
<string name="app_name">Hello Oboe</string>
|
||||
<string name="init_status">Touch anywhere on the screen to play audio</string>
|
||||
<string name="action_settings">Settings</string>
|
||||
<string name="need_record_audio_permission">"This sample needs RECORD_AUDIO permission"</string>
|
||||
<string name="playback_device">Playback device</string>
|
||||
<string name="latency">Latency: %s</string>
|
||||
<string name="buffer_size_title">Buffer size</string>
|
||||
<string name="automatic">Automatic</string>
|
||||
<string name="buffer_size_description_key">description</string>
|
||||
<string name="buffer_size_value_key">value</string>
|
||||
<string name="audio_api_description_key">description</string>
|
||||
<string name="audio_api_value_key">value</string>
|
||||
<string name="only_supported_on_api_26">Only supported in AAudio (API 26+)</string>
|
||||
<string name="audio_api">Audio API</string>
|
||||
</resources>
|
8
externals/oboe/samples/hello-oboe/src/main/res/values/styles.xml
vendored
Normal file
8
externals/oboe/samples/hello-oboe/src/main/res/values/styles.xml
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
|
||||
</resources>
|
Loading…
Add table
Add a link
Reference in a new issue