Signed-off-by: falkTX <falktx@falktx.com>tags/2020-12-27
@@ -2,33 +2,33 @@ | |||
if linux_embed | |||
juce_current_srcs = [ | |||
'source/juce_audio_basics/juce_audio_basics.cpp', | |||
'source/juce_audio_devices/juce_audio_devices.cpp', | |||
'source/juce_audio_formats/juce_audio_formats.cpp', | |||
'source/juce_audio_processors/juce_audio_processors.cpp', | |||
'source/juce_audio_utils/juce_audio_utils.cpp', | |||
'source/juce_core/juce_core.cpp', | |||
'source/juce_cryptography/juce_cryptography.cpp', | |||
'source/juce_data_structures/juce_data_structures.cpp', | |||
'source/juce_dsp/juce_dsp.cpp', | |||
'source/juce_events/juce_events.cpp', | |||
'source/modules/juce_audio_basics/juce_audio_basics.cpp', | |||
'source/modules/juce_audio_devices/juce_audio_devices.cpp', | |||
'source/modules/juce_audio_formats/juce_audio_formats.cpp', | |||
'source/modules/juce_audio_processors/juce_audio_processors.cpp', | |||
'source/modules/juce_audio_utils/juce_audio_utils.cpp', | |||
'source/modules/juce_core/juce_core.cpp', | |||
'source/modules/juce_cryptography/juce_cryptography.cpp', | |||
'source/modules/juce_data_structures/juce_data_structures.cpp', | |||
'source/modules/juce_dsp/juce_dsp.cpp', | |||
'source/modules/juce_events/juce_events.cpp', | |||
] | |||
else | |||
juce_current_srcs = [ | |||
'source/juce_audio_basics/juce_audio_basics.cpp', | |||
'source/juce_audio_devices/juce_audio_devices.cpp', | |||
'source/juce_audio_formats/juce_audio_formats.cpp', | |||
'source/juce_audio_processors/juce_audio_processors.cpp', | |||
'source/juce_audio_utils/juce_audio_utils.cpp', | |||
'source/juce_core/juce_core.cpp', | |||
'source/juce_cryptography/juce_cryptography.cpp', | |||
'source/juce_data_structures/juce_data_structures.cpp', | |||
'source/juce_dsp/juce_dsp.cpp', | |||
'source/juce_events/juce_events.cpp', | |||
'source/juce_graphics/juce_graphics.cpp', | |||
'source/juce_gui_basics/juce_gui_basics.cpp', | |||
'source/juce_gui_extra/juce_gui_extra.cpp', | |||
'source/juce_opengl/juce_opengl.cpp', | |||
'source/modules/juce_audio_basics/juce_audio_basics.cpp', | |||
'source/modules/juce_audio_devices/juce_audio_devices.cpp', | |||
'source/modules/juce_audio_formats/juce_audio_formats.cpp', | |||
'source/modules/juce_audio_processors/juce_audio_processors.cpp', | |||
'source/modules/juce_audio_utils/juce_audio_utils.cpp', | |||
'source/modules/juce_core/juce_core.cpp', | |||
'source/modules/juce_cryptography/juce_cryptography.cpp', | |||
'source/modules/juce_data_structures/juce_data_structures.cpp', | |||
'source/modules/juce_dsp/juce_dsp.cpp', | |||
'source/modules/juce_events/juce_events.cpp', | |||
'source/modules/juce_graphics/juce_graphics.cpp', | |||
'source/modules/juce_gui_basics/juce_gui_basics.cpp', | |||
'source/modules/juce_gui_extra/juce_gui_extra.cpp', | |||
'source/modules/juce_opengl/juce_opengl.cpp', | |||
] | |||
endif | |||
@@ -37,6 +37,7 @@ lib_juce_current = static_library('juce-current', | |||
include_directories: [ | |||
include_directories('.'), | |||
include_directories('source'), | |||
include_directories('source/modules'), | |||
include_directories('..' / 'juced' / 'source' / 'dependancies' / 'ladspa_sdk' / 'src'), | |||
], | |||
cpp_args: build_flags_cpp + ['-std=gnu++14'], | |||
@@ -1,496 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2020 - Raw Material Software Limited | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
The code included in this file is provided under the terms of the ISC license | |||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission | |||
To use, copy, modify, and/or distribute this software for any purpose with or | |||
without fee is hereby granted provided that the above copyright notice and | |||
this permission notice appear in all copies. | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | |||
STATICMETHOD (getMinBufferSize, "getMinBufferSize", "(III)I") \ | |||
STATICMETHOD (getNativeOutputSampleRate, "getNativeOutputSampleRate", "(I)I") \ | |||
METHOD (constructor, "<init>", "(IIIIII)V") \ | |||
METHOD (getState, "getState", "()I") \ | |||
METHOD (play, "play", "()V") \ | |||
METHOD (stop, "stop", "()V") \ | |||
METHOD (release, "release", "()V") \ | |||
METHOD (flush, "flush", "()V") \ | |||
METHOD (write, "write", "([SII)I") \ | |||
DECLARE_JNI_CLASS (AudioTrack, "android/media/AudioTrack") | |||
#undef JNI_CLASS_MEMBERS | |||
//============================================================================== | |||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | |||
STATICMETHOD (getMinBufferSize, "getMinBufferSize", "(III)I") \ | |||
METHOD (constructor, "<init>", "(IIIII)V") \ | |||
METHOD (getState, "getState", "()I") \ | |||
METHOD (startRecording, "startRecording", "()V") \ | |||
METHOD (stop, "stop", "()V") \ | |||
METHOD (read, "read", "([SII)I") \ | |||
METHOD (release, "release", "()V") \ | |||
DECLARE_JNI_CLASS (AudioRecord, "android/media/AudioRecord") | |||
#undef JNI_CLASS_MEMBERS | |||
//============================================================================== | |||
enum | |||
{ | |||
CHANNEL_OUT_STEREO = 12, | |||
CHANNEL_IN_STEREO = 12, | |||
CHANNEL_IN_MONO = 16, | |||
ENCODING_PCM_16BIT = 2, | |||
STREAM_MUSIC = 3, | |||
MODE_STREAM = 1, | |||
STATE_UNINITIALIZED = 0 | |||
}; | |||
const char* const javaAudioTypeName = "Android Audio"; | |||
//============================================================================== | |||
class AndroidAudioIODevice : public AudioIODevice, | |||
public Thread | |||
{ | |||
public: | |||
//============================================================================== | |||
AndroidAudioIODevice (const String& deviceName) | |||
: AudioIODevice (deviceName, javaAudioTypeName), | |||
Thread ("audio"), | |||
minBufferSizeOut (0), minBufferSizeIn (0), callback (nullptr), sampleRate (0), | |||
numClientInputChannels (0), numDeviceInputChannels (0), numDeviceInputChannelsAvailable (2), | |||
numClientOutputChannels (0), numDeviceOutputChannels (0), | |||
actualBufferSize (0), isRunning (false), | |||
inputChannelBuffer (1, 1), | |||
outputChannelBuffer (1, 1) | |||
{ | |||
JNIEnv* env = getEnv(); | |||
sampleRate = env->CallStaticIntMethod (AudioTrack, AudioTrack.getNativeOutputSampleRate, MODE_STREAM); | |||
minBufferSizeOut = (int) env->CallStaticIntMethod (AudioTrack, AudioTrack.getMinBufferSize, sampleRate, CHANNEL_OUT_STEREO, ENCODING_PCM_16BIT); | |||
minBufferSizeIn = (int) env->CallStaticIntMethod (AudioRecord, AudioRecord.getMinBufferSize, sampleRate, CHANNEL_IN_STEREO, ENCODING_PCM_16BIT); | |||
if (minBufferSizeIn <= 0) | |||
{ | |||
minBufferSizeIn = env->CallStaticIntMethod (AudioRecord, AudioRecord.getMinBufferSize, sampleRate, CHANNEL_IN_MONO, ENCODING_PCM_16BIT); | |||
if (minBufferSizeIn > 0) | |||
numDeviceInputChannelsAvailable = 1; | |||
else | |||
numDeviceInputChannelsAvailable = 0; | |||
} | |||
DBG ("Audio device - min buffers: " << minBufferSizeOut << ", " << minBufferSizeIn << "; " | |||
<< sampleRate << " Hz; input chans: " << numDeviceInputChannelsAvailable); | |||
} | |||
~AndroidAudioIODevice() override | |||
{ | |||
close(); | |||
} | |||
StringArray getOutputChannelNames() override | |||
{ | |||
StringArray s; | |||
s.add ("Left"); | |||
s.add ("Right"); | |||
return s; | |||
} | |||
StringArray getInputChannelNames() override | |||
{ | |||
StringArray s; | |||
if (numDeviceInputChannelsAvailable == 2) | |||
{ | |||
s.add ("Left"); | |||
s.add ("Right"); | |||
} | |||
else if (numDeviceInputChannelsAvailable == 1) | |||
{ | |||
s.add ("Audio Input"); | |||
} | |||
return s; | |||
} | |||
Array<double> getAvailableSampleRates() override | |||
{ | |||
Array<double> r; | |||
r.add ((double) sampleRate); | |||
return r; | |||
} | |||
Array<int> getAvailableBufferSizes() override | |||
{ | |||
Array<int> b; | |||
int n = 16; | |||
for (int i = 0; i < 50; ++i) | |||
{ | |||
b.add (n); | |||
n += n < 64 ? 16 | |||
: (n < 512 ? 32 | |||
: (n < 1024 ? 64 | |||
: (n < 2048 ? 128 : 256))); | |||
} | |||
return b; | |||
} | |||
int getDefaultBufferSize() override { return 2048; } | |||
String open (const BigInteger& inputChannels, | |||
const BigInteger& outputChannels, | |||
double requestedSampleRate, | |||
int bufferSize) override | |||
{ | |||
close(); | |||
if (sampleRate != (int) requestedSampleRate) | |||
return "Sample rate not allowed"; | |||
lastError.clear(); | |||
int preferredBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize; | |||
numDeviceInputChannels = 0; | |||
numDeviceOutputChannels = 0; | |||
activeOutputChans = outputChannels; | |||
activeOutputChans.setRange (2, activeOutputChans.getHighestBit(), false); | |||
numClientOutputChannels = activeOutputChans.countNumberOfSetBits(); | |||
activeInputChans = inputChannels; | |||
activeInputChans.setRange (2, activeInputChans.getHighestBit(), false); | |||
numClientInputChannels = activeInputChans.countNumberOfSetBits(); | |||
actualBufferSize = preferredBufferSize; | |||
inputChannelBuffer.setSize (2, actualBufferSize); | |||
inputChannelBuffer.clear(); | |||
outputChannelBuffer.setSize (2, actualBufferSize); | |||
outputChannelBuffer.clear(); | |||
JNIEnv* env = getEnv(); | |||
if (numClientOutputChannels > 0) | |||
{ | |||
numDeviceOutputChannels = 2; | |||
outputDevice = GlobalRef (LocalRef<jobject>(env->NewObject (AudioTrack, AudioTrack.constructor, | |||
STREAM_MUSIC, sampleRate, CHANNEL_OUT_STEREO, ENCODING_PCM_16BIT, | |||
(jint) (minBufferSizeOut * numDeviceOutputChannels * static_cast<int> (sizeof (int16))), MODE_STREAM))); | |||
const bool supportsUnderrunCount = (getAndroidSDKVersion() >= 24); | |||
getUnderrunCount = supportsUnderrunCount ? env->GetMethodID (AudioTrack, "getUnderrunCount", "()I") : nullptr; | |||
int outputDeviceState = env->CallIntMethod (outputDevice, AudioTrack.getState); | |||
if (outputDeviceState > 0) | |||
{ | |||
isRunning = true; | |||
} | |||
else | |||
{ | |||
// failed to open the device | |||
outputDevice.clear(); | |||
lastError = "Error opening audio output device: android.media.AudioTrack failed with state = " + String (outputDeviceState); | |||
} | |||
} | |||
if (numClientInputChannels > 0 && numDeviceInputChannelsAvailable > 0) | |||
{ | |||
if (! RuntimePermissions::isGranted (RuntimePermissions::recordAudio)) | |||
{ | |||
// If you hit this assert, you probably forgot to get RuntimePermissions::recordAudio | |||
// before trying to open an audio input device. This is not going to work! | |||
jassertfalse; | |||
inputDevice.clear(); | |||
lastError = "Error opening audio input device: the app was not granted android.permission.RECORD_AUDIO"; | |||
} | |||
else | |||
{ | |||
numDeviceInputChannels = jmin (numClientInputChannels, numDeviceInputChannelsAvailable); | |||
inputDevice = GlobalRef (LocalRef<jobject>(env->NewObject (AudioRecord, AudioRecord.constructor, | |||
0 /* (default audio source) */, sampleRate, | |||
numDeviceInputChannelsAvailable > 1 ? CHANNEL_IN_STEREO : CHANNEL_IN_MONO, | |||
ENCODING_PCM_16BIT, | |||
(jint) (minBufferSizeIn * numDeviceInputChannels * static_cast<int> (sizeof (int16)))))); | |||
int inputDeviceState = env->CallIntMethod (inputDevice, AudioRecord.getState); | |||
if (inputDeviceState > 0) | |||
{ | |||
isRunning = true; | |||
} | |||
else | |||
{ | |||
// failed to open the device | |||
inputDevice.clear(); | |||
lastError = "Error opening audio input device: android.media.AudioRecord failed with state = " + String (inputDeviceState); | |||
} | |||
} | |||
} | |||
if (isRunning) | |||
{ | |||
if (outputDevice != nullptr) | |||
env->CallVoidMethod (outputDevice, AudioTrack.play); | |||
if (inputDevice != nullptr) | |||
env->CallVoidMethod (inputDevice, AudioRecord.startRecording); | |||
startThread (8); | |||
} | |||
else | |||
{ | |||
closeDevices(); | |||
} | |||
return lastError; | |||
} | |||
void close() override | |||
{ | |||
if (isRunning) | |||
{ | |||
stopThread (2000); | |||
isRunning = false; | |||
closeDevices(); | |||
} | |||
} | |||
int getOutputLatencyInSamples() override { return (minBufferSizeOut * 3) / 4; } | |||
int getInputLatencyInSamples() override { return (minBufferSizeIn * 3) / 4; } | |||
bool isOpen() override { return isRunning; } | |||
int getCurrentBufferSizeSamples() override { return actualBufferSize; } | |||
int getCurrentBitDepth() override { return 16; } | |||
double getCurrentSampleRate() override { return sampleRate; } | |||
BigInteger getActiveOutputChannels() const override { return activeOutputChans; } | |||
BigInteger getActiveInputChannels() const override { return activeInputChans; } | |||
String getLastError() override { return lastError; } | |||
bool isPlaying() override { return isRunning && callback != nullptr; } | |||
int getXRunCount() const noexcept override | |||
{ | |||
if (outputDevice != nullptr && getUnderrunCount != nullptr) | |||
return getEnv()->CallIntMethod (outputDevice, getUnderrunCount); | |||
return -1; | |||
} | |||
void start (AudioIODeviceCallback* newCallback) override | |||
{ | |||
if (isRunning && callback != newCallback) | |||
{ | |||
if (newCallback != nullptr) | |||
newCallback->audioDeviceAboutToStart (this); | |||
const ScopedLock sl (callbackLock); | |||
callback = newCallback; | |||
} | |||
} | |||
void stop() override | |||
{ | |||
if (isRunning) | |||
{ | |||
AudioIODeviceCallback* lastCallback; | |||
{ | |||
const ScopedLock sl (callbackLock); | |||
lastCallback = callback; | |||
callback = nullptr; | |||
} | |||
if (lastCallback != nullptr) | |||
lastCallback->audioDeviceStopped(); | |||
} | |||
} | |||
void run() override | |||
{ | |||
JNIEnv* env = getEnv(); | |||
jshortArray audioBuffer = env->NewShortArray (actualBufferSize * jmax (numDeviceOutputChannels, numDeviceInputChannels)); | |||
while (! threadShouldExit()) | |||
{ | |||
if (inputDevice != nullptr) | |||
{ | |||
jint numRead = env->CallIntMethod (inputDevice, AudioRecord.read, audioBuffer, 0, actualBufferSize * numDeviceInputChannels); | |||
if (numRead < actualBufferSize * numDeviceInputChannels) | |||
{ | |||
DBG ("Audio read under-run! " << numRead); | |||
} | |||
jshort* const src = env->GetShortArrayElements (audioBuffer, nullptr); | |||
for (int chan = 0; chan < inputChannelBuffer.getNumChannels(); ++chan) | |||
{ | |||
AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst> d (inputChannelBuffer.getWritePointer (chan)); | |||
if (chan < numDeviceInputChannels) | |||
{ | |||
AudioData::Pointer <AudioData::Int16, AudioData::NativeEndian, AudioData::Interleaved, AudioData::Const> s (src + chan, numDeviceInputChannels); | |||
d.convertSamples (s, actualBufferSize); | |||
} | |||
else | |||
{ | |||
d.clearSamples (actualBufferSize); | |||
} | |||
} | |||
env->ReleaseShortArrayElements (audioBuffer, src, 0); | |||
} | |||
if (threadShouldExit()) | |||
break; | |||
{ | |||
const ScopedLock sl (callbackLock); | |||
if (callback != nullptr) | |||
{ | |||
callback->audioDeviceIOCallback (inputChannelBuffer.getArrayOfReadPointers(), numClientInputChannels, | |||
outputChannelBuffer.getArrayOfWritePointers(), numClientOutputChannels, | |||
actualBufferSize); | |||
} | |||
else | |||
{ | |||
outputChannelBuffer.clear(); | |||
} | |||
} | |||
if (outputDevice != nullptr) | |||
{ | |||
if (threadShouldExit()) | |||
break; | |||
jshort* const dest = env->GetShortArrayElements (audioBuffer, nullptr); | |||
for (int chan = 0; chan < numDeviceOutputChannels; ++chan) | |||
{ | |||
AudioData::Pointer <AudioData::Int16, AudioData::NativeEndian, AudioData::Interleaved, AudioData::NonConst> d (dest + chan, numDeviceOutputChannels); | |||
const float* const sourceChanData = outputChannelBuffer.getReadPointer (jmin (chan, outputChannelBuffer.getNumChannels() - 1)); | |||
AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const> s (sourceChanData); | |||
d.convertSamples (s, actualBufferSize); | |||
} | |||
env->ReleaseShortArrayElements (audioBuffer, dest, 0); | |||
jint numWritten = env->CallIntMethod (outputDevice, AudioTrack.write, audioBuffer, 0, actualBufferSize * numDeviceOutputChannels); | |||
if (numWritten < actualBufferSize * numDeviceOutputChannels) | |||
{ | |||
DBG ("Audio write underrun! " << numWritten); | |||
} | |||
} | |||
} | |||
} | |||
int minBufferSizeOut, minBufferSizeIn; | |||
private: | |||
//============================================================================== | |||
CriticalSection callbackLock; | |||
AudioIODeviceCallback* callback; | |||
jint sampleRate; | |||
int numClientInputChannels, numDeviceInputChannels, numDeviceInputChannelsAvailable; | |||
int numClientOutputChannels, numDeviceOutputChannels; | |||
int actualBufferSize; | |||
bool isRunning; | |||
String lastError; | |||
BigInteger activeOutputChans, activeInputChans; | |||
GlobalRef outputDevice, inputDevice; | |||
AudioBuffer<float> inputChannelBuffer, outputChannelBuffer; | |||
jmethodID getUnderrunCount = nullptr; | |||
void closeDevices() | |||
{ | |||
if (outputDevice != nullptr) | |||
{ | |||
outputDevice.callVoidMethod (AudioTrack.stop); | |||
outputDevice.callVoidMethod (AudioTrack.release); | |||
outputDevice.clear(); | |||
} | |||
if (inputDevice != nullptr) | |||
{ | |||
inputDevice.callVoidMethod (AudioRecord.stop); | |||
inputDevice.callVoidMethod (AudioRecord.release); | |||
inputDevice.clear(); | |||
} | |||
} | |||
JUCE_DECLARE_NON_COPYABLE (AndroidAudioIODevice) | |||
}; | |||
//============================================================================== | |||
class AndroidAudioIODeviceType : public AudioIODeviceType | |||
{ | |||
public: | |||
AndroidAudioIODeviceType() : AudioIODeviceType (javaAudioTypeName) {} | |||
//============================================================================== | |||
void scanForDevices() {} | |||
StringArray getDeviceNames (bool) const { return StringArray (javaAudioTypeName); } | |||
int getDefaultDeviceIndex (bool) const { return 0; } | |||
int getIndexOfDevice (AudioIODevice* device, bool) const { return device != nullptr ? 0 : -1; } | |||
bool hasSeparateInputsAndOutputs() const { return false; } | |||
AudioIODevice* createDevice (const String& outputDeviceName, | |||
const String& inputDeviceName) | |||
{ | |||
std::unique_ptr<AndroidAudioIODevice> dev; | |||
if (outputDeviceName.isNotEmpty() || inputDeviceName.isNotEmpty()) | |||
{ | |||
dev.reset (new AndroidAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName | |||
: inputDeviceName)); | |||
if (dev->getCurrentSampleRate() <= 0 || dev->getDefaultBufferSize() <= 0) | |||
dev = nullptr; | |||
} | |||
return dev.release(); | |||
} | |||
private: | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidAudioIODeviceType) | |||
}; | |||
//============================================================================== | |||
extern bool isOboeAvailable(); | |||
extern bool isOpenSLAvailable(); | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Android() | |||
{ | |||
#if JUCE_USE_ANDROID_OBOE | |||
if (isOboeAvailable()) | |||
return nullptr; | |||
#endif | |||
#if JUCE_USE_ANDROID_OPENSLES | |||
if (isOpenSLAvailable()) | |||
return nullptr; | |||
#endif | |||
return new AndroidAudioIODeviceType(); | |||
} | |||
} // namespace juce |
@@ -1,131 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2020 - Raw Material Software Limited | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
The code included in this file is provided under the terms of the ISC license | |||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission | |||
To use, copy, modify, and/or distribute this software for any purpose with or | |||
without fee is hereby granted provided that the above copyright notice and | |||
this permission notice appear in all copies. | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
Some shared helpers methods for using the high-performance audio paths on | |||
Android devices (OpenSL and Oboe). | |||
@tags{Audio} | |||
*/ | |||
namespace AndroidHighPerformanceAudioHelpers | |||
{ | |||
//============================================================================== | |||
static double getNativeSampleRate() | |||
{ | |||
return audioManagerGetProperty ("android.media.property.OUTPUT_SAMPLE_RATE").getDoubleValue(); | |||
} | |||
static int getNativeBufferSizeHint() | |||
{ | |||
// This property is a hint of a native buffer size but it does not guarantee the size used. | |||
auto deviceBufferSize = audioManagerGetProperty ("android.media.property.OUTPUT_FRAMES_PER_BUFFER").getIntValue(); | |||
if (deviceBufferSize == 0) | |||
return 192; | |||
return deviceBufferSize; | |||
} | |||
static bool isProAudioDevice() | |||
{ | |||
static bool isSapaSupported = SystemStats::getDeviceManufacturer().containsIgnoreCase ("SAMSUNG") | |||
&& DynamicLibrary().open ("libapa_jni.so"); | |||
return androidHasSystemFeature ("android.hardware.audio.pro") || isSapaSupported; | |||
} | |||
static bool hasLowLatencyAudioPath() | |||
{ | |||
return androidHasSystemFeature ("android.hardware.audio.low_latency"); | |||
} | |||
static bool canUseHighPerformanceAudioPath (int nativeBufferSize, int requestedBufferSize, int requestedSampleRate) | |||
{ | |||
return ((requestedBufferSize % nativeBufferSize) == 0) | |||
&& (requestedSampleRate == getNativeSampleRate()) | |||
&& isProAudioDevice(); | |||
} | |||
//============================================================================== | |||
static int getMinimumBuffersToEnqueue (int nativeBufferSize, double requestedSampleRate) | |||
{ | |||
if (canUseHighPerformanceAudioPath (nativeBufferSize, nativeBufferSize, (int) requestedSampleRate)) | |||
{ | |||
// see https://developer.android.com/ndk/guides/audio/opensl/opensl-prog-notes.html#sandp | |||
// "For Android 4.2 (API level 17) and earlier, a buffer count of two or more is required | |||
// for lower latency. Beginning with Android 4.3 (API level 18), a buffer count of one | |||
// is sufficient for lower latency." | |||
return (getAndroidSDKVersion() >= 18 ? 1 : 2); | |||
} | |||
// not using low-latency path so we can use the absolute minimum number of buffers to queue | |||
return 1; | |||
} | |||
static int buffersToQueueForBufferDuration (int nativeBufferSize, int bufferDurationInMs, double sampleRate) noexcept | |||
{ | |||
auto maxBufferFrames = static_cast<int> (std::ceil (bufferDurationInMs * sampleRate / 1000.0)); | |||
auto maxNumBuffers = static_cast<int> (std::ceil (static_cast<double> (maxBufferFrames) | |||
/ static_cast<double> (nativeBufferSize))); | |||
return jmax (getMinimumBuffersToEnqueue (nativeBufferSize, sampleRate), maxNumBuffers); | |||
} | |||
static int getMaximumBuffersToEnqueue (int nativeBufferSize, double maximumSampleRate) noexcept | |||
{ | |||
static constexpr int maxBufferSizeMs = 200; | |||
return jmax (8, buffersToQueueForBufferDuration (nativeBufferSize, maxBufferSizeMs, maximumSampleRate)); | |||
} | |||
static Array<int> getAvailableBufferSizes (int nativeBufferSize, Array<double> availableSampleRates) | |||
{ | |||
auto minBuffersToQueue = getMinimumBuffersToEnqueue (nativeBufferSize, getNativeSampleRate()); | |||
auto maxBuffersToQueue = getMaximumBuffersToEnqueue (nativeBufferSize, findMaximum (availableSampleRates.getRawDataPointer(), | |||
availableSampleRates.size())); | |||
Array<int> bufferSizes; | |||
for (int i = minBuffersToQueue; i <= maxBuffersToQueue; ++i) | |||
bufferSizes.add (i * nativeBufferSize); | |||
return bufferSizes; | |||
} | |||
static int getDefaultBufferSize (int nativeBufferSize, double currentSampleRate) | |||
{ | |||
static constexpr int defaultBufferSizeForLowLatencyDeviceMs = 40; | |||
static constexpr int defaultBufferSizeForStandardLatencyDeviceMs = 100; | |||
auto defaultBufferLength = (hasLowLatencyAudioPath() ? defaultBufferSizeForLowLatencyDeviceMs | |||
: defaultBufferSizeForStandardLatencyDeviceMs); | |||
auto defaultBuffersToEnqueue = buffersToQueueForBufferDuration (nativeBufferSize, defaultBufferLength, currentSampleRate); | |||
return defaultBuffersToEnqueue * nativeBufferSize; | |||
} | |||
} | |||
} // namespace juce |
@@ -1,706 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2020 - Raw Material Software Limited | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
The code included in this file is provided under the terms of the ISC license | |||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission | |||
To use, copy, modify, and/or distribute this software for any purpose with or | |||
without fee is hereby granted provided that the above copyright notice and | |||
this permission notice appear in all copies. | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
// This byte-code is generated from native/java/com/rmsl/juce/JuceMidiSupport.java with min sdk version 23 | |||
// See juce_core/native/java/README.txt on how to generate this byte-code. | |||
static const uint8 javaMidiByteCode[] = | |||
{31,139,8,8,43,113,161,94,0,3,106,97,118,97,77,105,100,105,66,121,116,101,67,111,100,101,46,100,101,120,0,149,124,11,124,220, | |||
69,181,255,153,223,99,119,179,217,36,155,77,218,164,105,178,217,164,73,179,165,205,171,233,35,109,146,182,121,180,77,218,164,45, | |||
201,182,72,195,5,183,201,182,217,146,236,134,236,166,180,114,189,20,244,210,162,168,40,80,65,177,162,2,242,18,81,65,80,17,81, | |||
80,81,81,122,149,63,214,39,138,112,69,69,64,20,17,229,218,255,247,204,204,110,126,109,3,213,246,243,221,51,191,51,103,206,204, | |||
156,57,115,230,204,111,183,29,141,237,247,54,181,44,167,234,195,55,252,236,231,159,31,184,160,244,71,71,143,188,248,212,248, | |||
199,142,188,62,189,243,225,179,11,151,53,157,77,52,73,68,251,119,44,11,144,254,243,246,109,68,231,10,197,95,15,60,105,19,157,3, | |||
250,188,139,40,4,250,134,151,232,179,76,115,137,114,64,211,133,68,55,174,33,186,22,26,254,86,79,244,119,224,255,0,163,129,200, | |||
6,22,3,13,64,43,176,14,232,1,54,1,219,128,93,192,81,224,105,224,31,192,63,1,163,145,200,13,132,129,173,192,32,240,54,224,66, | |||
224,82,224,125,192,167,128,91,129,59,128,207,2,247,2,95,6,30,2,30,1,190,3,188,4,20,55,17,173,4,118,1,215,0,15,3,127,0,252,205, | |||
68,109,192,249,192,101,192,221,192,143,128,23,129,130,165,68,29,192,46,224,74,224,51,192,47,129,146,22,162,85,192,249,192,101, | |||
192,17,224,46,224,155,192,79,128,63,2,198,50,216,14,120,47,240,16,240,10,16,90,78,148,0,238,5,126,11,204,89,65,180,2,216,9,188, | |||
3,248,24,240,32,112,28,120,9,48,86,162,47,96,49,176,22,216,1,164,129,107,128,219,129,135,1,187,149,168,9,232,1,222,6,76,0,151, | |||
1,71,128,187,128,175,2,79,0,214,42,244,7,132,129,181,192,32,112,29,112,59,112,63,240,75,224,215,192,115,192,239,129,151,129,191, | |||
1,111,0,98,53,214,1,200,7,138,128,82,32,8,212,0,139,129,165,192,10,96,21,208,1,116,2,235,129,56,112,61,240,16,240,35,224,121, | |||
224,85,64,180,17,121,129,2,160,20,88,8,180,0,107,129,141,192,185,192,52,112,37,240,81,224,115,192,35,192,15,128,227,192,239, | |||
129,87,128,215,1,119,59,244,0,149,192,66,160,30,88,1,116,3,3,192,78,96,4,184,8,216,15,92,2,28,2,62,0,220,0,124,10,248,60,240,53, | |||
224,7,192,79,129,231,128,63,1,175,3,86,7,244,3,107,129,62,96,28,120,31,112,45,240,73,224,110,224,94,224,107,192,163,192,49,224, | |||
23,192,235,192,92,236,133,122,160,11,56,15,72,1,239,4,174,6,62,10,220,9,220,15,60,12,252,1,120,5,120,29,48,214,98,46,192,86, | |||
224,0,112,45,112,23,112,31,240,13,224,127,128,223,0,127,4,222,0,242,214,97,254,64,24,88,9,172,3,54,1,219,128,97,224,124,96,4, | |||
136,3,147,192,37,192,97,224,58,224,40,240,105,224,94,224,33,224,49,224,41,224,103,192,111,128,151,128,191,1,255,4,188,157,68, | |||
21,64,61,176,26,88,3,116,3,189,192,22,96,59,112,1,16,3,246,2,83,192,21,192,199,129,251,128,199,129,231,128,215,0,111,23,209,60, | |||
96,17,176,14,56,27,24,3,46,2,46,5,62,8,220,9,124,9,248,54,240,4,240,28,240,119,192,213,13,95,6,26,128,94,224,60,96,28,184,24, | |||
184,12,184,10,184,6,248,56,112,43,240,21,224,199,192,203,192,95,129,55,0,119,15,81,33,48,31,88,12,44,7,122,128,45,192,249,64, | |||
12,216,11,92,14,188,7,56,2,124,10,120,8,248,22,240,4,240,99,224,23,192,51,192,171,128,23,65,178,8,168,0,106,129,197,192,70,224, | |||
108,96,23,144,4,46,5,174,2,62,4,220,0,220,12,124,1,248,10,240,117,224,123,192,83,192,207,129,103,129,151,128,191,1,198,6,216, | |||
11,88,6,108,1,182,3,5,136,185,197,64,53,176,0,168,1,106,129,133,64,29,16,6,22,1,103,1,139,129,37,0,194,49,33,180,18,66,34,33, | |||
252,17,194,28,33,164,17,66,22,33,68,17,194,18,33,244,16,66,11,33,108,16,182,63,97,203,18,182,26,97,59,16,220,154,224,114,132, | |||
37,36,44,5,193,148,212,163,207,7,12,137,54,2,189,64,31,176,9,216,12,244,3,3,192,22,96,43,128,99,133,112,220,208,32,48,4,68,128, | |||
29,192,219,128,97,224,63,128,11,248,252,1,118,1,163,64,12,216,13,140,3,255,9,92,10,28,4,46,3,46,7,222,5,188,155,148,77,50,127, | |||
252,154,78,98,226,133,186,188,31,229,50,80,67,63,115,217,212,229,74,93,158,212,50,150,230,87,233,242,65,205,247,56,228,113,4, | |||
210,85,154,159,171,249,243,129,60,224,90,205,207,119,244,85,224,40,7,28,242,197,90,158,203,165,142,182,101,142,190,202,245,216, | |||
88,38,168,101,42,117,121,82,151,25,55,106,153,106,45,83,161,203,55,47,81,178,92,190,75,203,215,56,218,214,234,182,220,15,251, | |||
208,3,122,12,13,142,113,54,58,198,214,228,24,27,151,31,94,162,242,2,46,63,182,100,134,159,177,103,179,67,79,179,99,252,92,62, | |||
230,40,103,230,184,204,209,87,171,163,47,246,201,227,154,191,90,243,217,47,58,116,121,66,151,185,109,66,151,127,133,114,82,151, | |||
159,95,162,114,26,46,255,5,229,139,116,217,194,230,216,175,203,62,148,167,116,185,20,229,148,46,135,80,222,167,203,75,80,190, | |||
88,151,151,57,202,235,234,103,116,246,59,202,55,58,250,138,56,248,231,57,250,29,117,240,39,29,229,253,142,126,15,58,248,135,29, | |||
109,175,70,249,64,166,47,135,252,109,40,191,67,151,239,113,180,61,230,24,15,175,93,70,254,49,7,127,210,81,126,208,209,215,163, | |||
40,79,103,244,160,124,137,46,31,119,216,234,87,40,167,117,249,133,122,181,111,215,232,53,122,167,46,243,26,253,151,46,179,253, | |||
51,229,135,29,252,140,255,116,234,182,92,238,114,248,67,183,195,31,122,52,127,190,46,95,43,125,190,137,238,39,69,215,10,110, | |||
83,64,87,201,182,205,244,1,73,87,210,135,36,245,80,135,96,31,46,165,247,242,90,163,247,231,37,21,244,71,73,107,169,74,214,47,164, | |||
197,130,227,66,177,148,171,210,252,42,205,95,160,159,153,110,19,188,199,44,250,48,49,245,211,95,36,85,245,53,186,190,86,143,167, | |||
22,145,247,136,164,93,116,167,164,37,244,138,164,203,232,53,93,95,46,20,13,10,181,71,111,39,166,107,232,247,164,227,190,224, | |||
216,95,73,31,228,50,36,95,37,142,117,30,122,84,82,147,190,37,169,77,63,37,142,117,110,250,184,164,213,244,85,77,159,228,117, | |||
192,137,241,49,77,63,43,169,69,223,150,116,11,45,135,126,27,124,55,113,28,236,165,62,193,116,5,13,8,190,3,40,190,55,75,189,116, | |||
189,164,57,180,30,245,62,173,39,79,215,231,129,115,189,164,185,212,45,20,237,17,28,35,243,232,235,196,180,138,126,70,28,199, | |||
213,120,252,136,164,63,144,180,128,74,4,83,63,205,23,28,219,213,184,57,198,63,165,233,207,73,197,215,239,75,58,72,199,37,45,164, | |||
159,104,62,215,23,107,189,197,56,165,214,65,207,28,61,174,18,156,74,223,145,180,137,230,8,166,171,105,174,164,29,212,44,105, | |||
59,237,16,28,167,85,251,82,216,255,168,166,108,175,121,90,79,25,198,255,32,113,60,13,208,151,136,227,176,65,183,72,63,92,47,235, | |||
217,239,20,21,244,136,164,181,244,61,73,183,211,15,37,221,72,66,250,235,98,42,148,116,9,5,36,61,155,106,36,221,68,155,36,221, | |||
64,219,165,95,174,147,250,66,122,92,76,239,149,84,217,39,132,72,254,11,73,7,232,15,186,62,79,182,235,167,34,73,55,83,151,80,252, | |||
94,77,251,165,95,175,149,122,171,180,222,42,173,183,74,235,173,210,250,170,116,251,42,221,190,74,183,175,214,237,170,181,124, | |||
181,150,175,214,242,213,90,190,90,203,47,192,78,231,254,22,32,43,49,228,243,50,50,53,181,36,93,74,182,164,203,201,165,169,91, | |||
243,243,53,45,144,180,153,252,154,22,203,253,214,37,245,214,160,255,143,72,90,77,223,144,212,69,223,37,117,22,62,46,233,89, | |||
180,90,238,51,181,62,181,122,190,181,240,148,251,36,157,71,95,148,116,33,61,36,169,90,191,90,248,205,99,146,238,160,39,36,221,78, | |||
199,52,253,31,73,139,232,71,146,214,208,255,147,116,62,253,88,210,85,228,145,253,181,82,142,166,94,161,248,185,146,182,145,79, | |||
168,120,80,42,233,92,154,39,105,41,149,73,186,149,170,37,109,164,5,146,118,83,139,164,27,40,34,227,68,189,156,199,66,100,94, | |||
247,232,56,241,180,140,15,103,97,230,138,186,37,157,67,95,147,180,140,30,38,62,235,23,75,126,163,150,135,118,26,18,76,43,232, | |||
109,130,207,118,213,174,73,219,167,9,158,254,77,226,51,92,245,211,12,59,255,142,56,183,236,145,114,45,240,124,222,15,203,116, | |||
187,101,144,59,172,159,111,212,207,55,73,90,71,47,232,231,165,66,229,1,27,37,141,208,160,224,28,53,76,239,35,206,83,149,158, | |||
21,186,253,10,200,127,66,210,74,217,207,10,100,191,47,75,26,162,38,161,248,172,111,165,110,183,82,247,191,82,247,179,82,247,179, | |||
82,247,211,138,241,255,146,152,6,233,159,196,121,135,26,215,106,77,219,180,158,54,100,187,107,4,231,199,234,185,93,251,23,159, | |||
77,96,203,119,35,36,227,2,206,50,36,226,55,32,17,62,178,69,229,97,194,53,147,71,113,253,213,168,127,98,139,122,14,233,246,204, | |||
127,251,18,69,111,66,253,31,116,125,149,174,111,114,212,63,128,250,186,173,170,126,129,214,107,59,244,31,67,253,144,174,175,209, | |||
252,118,71,253,175,80,255,30,93,95,171,245,207,1,198,180,254,151,81,255,57,93,191,80,183,115,142,127,29,228,22,109,83,207, | |||
117,142,241,101,234,183,161,190,91,215,115,14,30,197,197,96,108,64,201,165,52,189,124,96,166,238,26,71,249,227,186,254,14,7, | |||
239,11,186,252,16,232,55,29,229,99,3,42,151,103,153,159,1,255,171,219,254,73,83,99,139,162,69,154,134,53,237,208,52,162,105,108, | |||
203,76,95,251,53,239,93,91,88,183,33,203,231,111,80,247,140,73,127,30,158,171,225,59,147,254,79,226,121,216,111,33,234,15,251, | |||
13,26,14,24,56,183,88,158,245,36,55,168,123,66,4,53,23,249,175,32,62,21,19,161,113,172,181,87,222,13,44,45,183,111,131,186,67, | |||
92,36,123,241,137,68,200,192,126,130,172,223,150,207,124,30,152,168,99,217,119,109,80,103,94,36,100,81,164,202,130,204,45,168, | |||
241,138,5,184,224,38,66,183,98,124,62,248,98,143,148,177,101,22,128,188,17,109,230,130,78,249,111,71,159,62,49,229,255,52,183, | |||
49,90,141,60,240,110,67,153,219,248,40,16,72,52,173,130,39,133,95,206,215,35,35,58,186,65,217,129,239,53,46,57,51,220,179,55, | |||
168,123,99,160,112,105,177,77,129,170,150,226,66,140,163,16,253,249,176,127,114,41,210,204,227,226,91,148,207,72,132,62,5,223, | |||
13,116,182,20,87,34,126,205,161,50,227,66,186,40,212,12,222,76,139,192,41,45,110,150,181,150,182,69,59,34,105,190,156,11,247, | |||
253,205,13,234,94,227,180,85,39,180,32,122,106,253,95,208,250,3,162,64,68,154,149,229,133,148,252,79,105,169,240,171,94,104,98, | |||
237,79,109,80,239,56,3,37,1,151,214,7,61,94,42,179,160,199,206,147,122,34,232,59,33,47,151,62,177,74,100,234,124,186,46,252, | |||
74,107,206,114,170,54,188,240,4,182,89,153,101,161,191,38,182,178,149,8,249,113,6,85,155,121,168,11,192,114,137,80,49,178,101, | |||
230,207,193,45,215,103,5,106,185,20,161,5,233,117,232,161,0,173,125,246,128,109,185,46,242,127,68,181,247,23,161,149,207,78, | |||
172,203,165,206,255,14,127,53,17,242,225,6,28,254,18,101,253,203,187,81,221,73,79,246,175,75,225,95,249,200,211,92,202,231,55, | |||
170,123,232,164,191,1,109,134,23,228,208,112,141,139,134,107,189,180,115,161,7,150,63,47,228,150,107,107,75,255,18,84,183,81, | |||
197,146,128,25,233,116,81,171,112,19,211,132,127,49,234,34,157,57,224,228,72,26,233,242,162,175,255,130,157,135,187,161,179,219, | |||
5,45,121,122,5,2,122,5,194,207,171,120,196,186,133,168,199,241,37,228,152,218,209,7,199,206,132,159,51,254,164,255,42,237,95, | |||
25,31,239,217,168,226,104,36,132,126,170,184,159,41,233,215,133,50,142,8,233,151,155,54,170,189,26,128,229,132,230,109,219,56, | |||
227,171,249,152,63,223,221,119,108,84,123,171,35,215,71,67,151,121,200,125,208,125,141,184,89,220,103,125,107,159,167,83,203, | |||
90,250,246,191,219,209,222,208,99,153,218,168,98,98,196,159,163,60,213,15,171,64,98,187,223,45,125,133,159,19,161,37,24,95,192, | |||
127,158,223,125,82,219,75,222,162,109,107,182,109,61,183,165,76,91,30,11,143,225,221,122,109,39,253,124,235,24,22,62,26,54,114, | |||
105,24,222,148,159,93,171,107,29,107,149,171,215,42,23,86,93,32,215,202,167,215,202,135,181,202,203,174,21,244,116,231,254, | |||
27,107,117,123,118,173,250,102,93,171,207,101,215,10,253,84,229,205,186,86,247,103,214,10,158,232,214,51,252,42,120,69,220, | |||
174,89,143,28,52,177,174,134,58,99,51,99,235,20,122,108,175,169,119,50,122,108,222,204,122,63,233,88,175,12,239,167,14,158,41,71, | |||
137,115,110,163,122,143,51,44,10,96,79,182,234,176,145,47,227,180,122,27,244,130,163,77,198,23,254,58,11,207,232,117,198,66, | |||
75,206,41,191,87,197,227,64,168,197,46,128,15,36,224,179,38,172,193,81,195,195,251,23,113,231,157,242,54,51,163,167,162,247,116, | |||
221,139,102,225,45,159,133,215,217,123,242,252,248,207,192,44,188,115,28,60,91,90,14,231,90,47,71,8,182,67,17,236,240,25,105, | |||
7,156,91,102,33,13,91,60,66,75,90,209,164,169,94,245,30,168,220,168,161,10,35,32,134,155,3,88,249,187,81,3,159,109,246,99,189, | |||
10,36,77,192,103,133,46,113,180,97,73,63,158,11,33,225,149,52,225,47,213,252,66,10,26,184,235,137,160,81,39,242,68,248,53,158, | |||
205,60,212,85,202,241,153,50,255,112,73,159,90,116,105,253,146,69,186,108,208,7,122,85,78,90,110,98,44,102,100,16,186,141,5, | |||
196,52,225,159,199,49,83,36,252,213,242,52,11,44,107,233,154,11,110,21,71,123,163,204,186,6,190,216,136,8,204,103,155,137,253, | |||
230,201,158,48,65,179,16,40,195,242,133,223,200,67,169,206,80,249,195,66,180,172,215,190,36,112,255,202,248,239,103,122,85,125, | |||
196,239,207,250,53,215,124,161,55,115,190,171,209,200,248,235,87,111,10,213,249,174,246,233,151,122,249,30,137,57,8,204,65,68, | |||
154,2,24,77,30,69,154,10,249,14,64,252,28,89,10,13,161,125,136,220,65,193,254,31,20,117,164,250,44,146,125,21,99,223,168,179, | |||
252,209,94,245,14,53,96,77,250,107,97,185,225,72,49,13,239,40,134,69,138,169,204,252,51,180,148,224,70,228,51,42,141,18,26,30, | |||
44,1,191,132,86,225,124,42,51,176,163,204,49,185,219,113,115,194,153,181,2,62,240,81,62,19,6,231,225,105,25,158,62,36,159,74, | |||
79,170,43,59,233,105,142,212,151,240,47,100,203,83,149,25,48,150,47,245,210,6,76,51,17,74,35,214,29,55,12,172,173,148,105,90, | |||
68,131,86,248,113,143,62,171,254,212,171,114,207,200,72,41,218,31,225,157,97,113,30,98,145,215,108,53,155,100,30,98,201,113,7, | |||
41,130,160,86,105,250,165,77,205,153,21,54,3,203,91,134,126,123,66,175,176,89,102,171,21,30,202,174,240,79,78,232,21,54,19,161, | |||
247,227,172,101,205,79,156,40,52,2,70,248,159,46,61,142,185,125,234,253,120,164,171,12,250,63,206,243,48,166,252,119,104,250, | |||
105,80,110,53,95,142,199,144,154,27,41,210,13,217,208,81,57,150,74,172,97,194,63,34,71,192,189,12,73,249,103,79,20,138,128,8, | |||
255,147,227,140,242,158,230,62,245,174,189,220,174,165,10,59,146,230,89,95,207,179,181,22,116,117,241,89,144,86,118,192,156,93, | |||
66,102,100,46,212,181,90,115,101,207,46,104,175,52,131,116,28,241,46,130,43,81,165,165,172,193,121,193,128,101,8,33,194,191,13, | |||
218,133,70,158,21,180,235,44,246,139,6,217,107,163,244,19,254,59,214,167,222,209,151,91,232,31,190,50,193,185,150,57,60,52, | |||
151,130,214,204,106,39,66,23,82,140,103,162,86,196,228,113,152,114,28,65,57,14,53,227,133,50,170,207,145,185,203,53,216,81,9, | |||
255,229,188,30,214,69,254,43,117,166,195,220,240,51,121,102,208,170,51,121,158,108,197,5,77,156,145,222,192,86,196,92,144,145, | |||
154,60,78,142,24,43,179,123,108,5,185,244,110,186,70,175,75,185,129,241,26,145,78,101,29,49,51,38,209,42,74,51,99,194,170,4, | |||
229,109,183,82,40,187,200,252,206,95,41,243,187,150,179,159,63,17,52,56,222,4,40,252,127,42,226,168,189,84,39,123,10,19,231,157, | |||
220,239,93,125,234,59,136,114,47,250,244,182,250,139,41,48,47,209,148,162,27,243,124,88,211,45,20,249,250,60,204,224,99,200, | |||
197,185,119,55,86,45,232,45,164,192,162,240,139,131,205,243,105,50,116,17,238,192,62,119,171,123,21,69,246,97,44,46,68,70,87,11, | |||
164,90,225,175,131,205,21,104,59,31,121,183,15,121,118,14,110,11,33,114,127,221,122,118,159,139,223,144,182,98,6,172,189,218, | |||
106,129,158,171,97,197,68,211,77,212,108,5,189,225,99,24,177,183,78,168,246,101,220,222,211,234,249,221,137,106,156,131,147,235, | |||
122,233,225,214,240,51,65,47,102,246,32,201,251,124,55,102,196,223,189,168,44,104,163,244,63,94,255,122,48,249,253,98,192,21, | |||
73,97,55,134,206,194,126,80,190,16,73,205,129,205,110,228,83,42,165,246,128,90,249,59,100,182,201,214,182,165,79,151,72,107,219, | |||
210,3,26,149,44,246,64,145,92,77,222,3,187,32,31,254,157,90,243,128,57,140,250,32,116,207,59,85,163,99,151,151,57,118,249,66, | |||
146,178,208,184,64,106,108,65,187,15,202,118,149,102,53,228,182,177,246,223,12,239,155,7,185,217,34,70,117,70,151,242,5,83,80, | |||
43,34,6,211,74,211,146,39,147,233,120,114,201,167,76,84,41,101,221,63,83,122,175,69,153,179,26,206,190,56,143,89,4,251,45,211, | |||
249,141,192,109,182,64,223,21,70,54,169,247,211,51,123,39,242,212,92,170,218,19,48,197,242,229,211,45,216,107,46,59,225,15,113, | |||
244,241,84,93,9,238,178,229,215,214,32,50,122,144,199,7,101,244,106,249,205,92,170,246,46,204,156,16,185,129,162,150,53,240, | |||
166,162,132,191,130,235,125,147,235,14,210,151,31,225,189,244,73,58,110,90,66,44,11,63,29,48,195,127,58,110,218,66,44,15,255,160, | |||
208,176,245,222,190,114,147,218,43,129,80,185,192,138,194,243,63,192,86,49,86,25,33,120,83,28,62,193,62,135,155,153,63,200,39, | |||
245,210,57,210,139,187,228,141,207,141,179,37,252,87,117,194,76,134,246,106,89,139,185,127,8,138,128,204,3,121,190,243,49,38, | |||
158,77,142,180,67,69,54,167,61,186,73,249,93,192,63,25,74,200,155,99,97,182,238,230,76,93,104,166,46,160,199,124,199,38,245,253, | |||
158,7,188,237,158,114,106,245,108,193,189,75,237,60,15,242,163,72,46,172,249,96,192,35,174,92,254,157,77,176,91,110,78,192, | |||
208,59,218,195,109,6,243,230,83,203,241,85,196,54,134,230,188,170,95,6,60,203,159,111,166,13,86,158,135,45,140,249,187,90,246, | |||
177,117,43,164,135,200,54,5,21,212,242,231,114,240,202,217,83,108,182,19,124,27,251,41,63,147,39,120,202,114,254,33,79,145,235, | |||
81,95,105,55,243,14,183,3,117,225,123,143,123,60,34,124,236,184,39,71,136,43,195,247,7,189,101,72,142,195,127,206,243,96,111, | |||
122,56,131,28,68,235,183,103,227,216,5,217,187,241,156,205,234,59,56,142,172,221,50,42,201,40,230,184,115,23,56,238,220,11,101, | |||
44,133,55,24,45,131,47,159,96,107,241,25,98,105,159,59,107,179,186,187,241,249,199,185,87,160,185,197,111,194,79,35,56,227,3, | |||
34,177,46,76,77,129,240,235,252,78,219,144,57,212,50,200,223,196,241,44,15,86,205,75,139,121,188,91,61,108,69,15,172,19,112,7, | |||
114,248,198,23,241,149,225,182,248,30,62,99,242,217,47,94,128,239,173,242,157,47,123,129,156,47,176,166,229,133,38,142,132,176, | |||
144,135,124,190,178,124,117,206,190,32,45,132,115,214,86,183,115,68,6,75,233,83,254,213,143,182,173,190,118,154,225,29,5,207, | |||
231,174,116,91,14,222,39,192,171,206,173,145,28,15,252,205,99,32,23,61,39,74,185,85,167,141,13,17,241,133,220,234,252,54,236, | |||
183,59,49,235,214,156,38,42,242,29,115,95,246,176,237,231,156,17,59,121,221,93,244,144,63,152,151,159,25,143,143,117,240,14,120, | |||
152,124,222,86,47,188,234,46,65,238,135,43,97,77,55,226,180,71,198,5,183,140,7,110,74,227,60,41,162,96,94,248,215,121,190,96, | |||
94,157,175,200,55,74,225,31,230,249,194,175,241,89,49,141,21,226,239,215,216,155,142,100,115,185,235,197,165,245,71,197,245,130, | |||
243,62,67,230,218,15,108,86,223,195,149,187,97,115,55,159,69,94,156,232,108,115,156,231,70,228,136,154,143,193,235,0,27,93,129, | |||
117,104,117,33,154,30,65,236,9,93,71,183,226,121,149,171,150,78,150,59,10,57,159,171,210,197,81,118,84,230,2,39,215,127,2,245, | |||
172,161,218,19,164,201,166,165,116,155,201,81,227,10,10,186,243,73,143,192,226,213,227,220,162,204,163,86,239,10,25,199,177,122, | |||
162,92,90,75,200,92,85,82,23,91,44,142,213,109,181,225,91,67,42,102,182,154,30,29,69,85,244,228,168,233,165,240,19,121,174, | |||
240,247,243,92,65,119,157,75,157,169,156,87,76,234,120,121,177,220,15,136,82,151,214,167,247,209,62,105,35,142,13,158,126,245, | |||
61,127,57,102,90,33,109,227,37,175,205,231,231,185,242,253,207,64,54,54,71,4,122,23,22,5,138,3,115,90,144,248,4,172,200,117, | |||
234,68,177,229,217,116,135,166,242,140,194,138,190,120,66,159,81,242,68,25,172,155,79,90,187,167,229,208,139,39,100,91,88,179, | |||
86,122,174,58,97,108,89,86,39,12,34,100,113,248,241,86,225,209,55,25,117,139,137,92,199,107,243,97,212,6,93,200,197,237,160, | |||
171,206,86,115,61,87,238,245,157,217,119,99,155,251,79,190,15,242,253,121,168,63,27,31,183,93,66,75,35,62,153,109,24,178,238, | |||
63,250,213,61,148,223,197,5,140,201,193,75,168,219,207,245,57,114,191,27,20,235,87,191,169,8,20,113,236,227,93,195,107,196,223, | |||
90,114,70,102,82,129,161,78,112,126,135,232,134,5,91,109,68,115,43,252,42,238,48,86,157,17,153,154,67,186,149,29,112,241,238, | |||
25,116,69,166,138,193,155,43,179,219,192,156,106,215,2,248,203,14,218,231,78,172,195,9,24,243,33,171,193,138,59,218,201,86,34, | |||
104,137,165,225,239,242,189,245,16,133,255,174,222,121,122,49,83,254,158,117,177,180,65,105,54,70,189,191,95,189,31,200,196, | |||
164,58,196,36,117,15,85,86,250,176,182,71,196,63,79,238,126,126,55,165,252,194,162,155,250,213,111,63,120,142,94,185,51,56,186, | |||
169,157,20,57,162,162,202,173,146,143,123,204,17,21,81,110,149,107,13,255,52,212,170,25,114,213,12,93,255,9,212,243,169,118, | |||
187,244,230,0,234,98,236,41,50,91,177,179,187,140,178,187,136,61,31,178,176,46,124,175,251,164,91,135,43,17,186,24,187,66,101, | |||
11,65,119,248,94,229,245,121,162,204,133,155,148,167,72,222,164,174,192,153,160,222,211,244,192,42,91,244,253,251,108,29,47, | |||
206,209,249,174,65,67,151,214,239,24,202,190,219,249,75,191,243,221,206,185,98,62,157,103,148,211,185,102,133,124,107,165,110, | |||
157,214,128,122,39,31,16,171,16,115,11,112,142,188,79,102,78,76,57,147,111,89,250,218,9,247,54,62,65,6,187,42,104,16,109,91, | |||
150,190,120,98,123,87,57,109,55,203,81,126,254,196,96,215,124,240,113,106,46,125,230,68,160,48,252,116,230,29,31,206,168,1,245, | |||
125,68,53,238,11,131,157,243,233,135,129,131,160,21,84,100,30,164,229,205,69,178,124,79,213,100,232,16,199,88,255,97,121,62, | |||
109,239,196,153,141,157,18,248,211,231,171,10,68,145,184,148,194,47,65,235,223,213,13,25,121,211,128,246,123,216,172,18,8,98,46, | |||
153,119,72,77,3,42,167,80,243,205,215,177,212,160,229,3,250,253,149,152,121,251,106,82,161,208,239,91,113,86,254,227,68,185, | |||
81,135,251,192,46,17,164,85,2,89,180,168,196,154,181,32,35,143,131,19,148,252,240,243,153,28,191,80,234,16,250,187,18,33,51,24, | |||
83,247,181,117,64,125,71,83,70,234,142,108,200,222,44,249,61,113,57,114,161,10,177,11,245,171,136,51,245,90,244,177,7,109,121, | |||
38,65,201,15,191,152,185,147,231,233,62,42,178,125,148,203,223,192,10,34,202,216,130,231,122,83,64,125,199,116,27,232,61,217, | |||
95,217,170,63,15,234,231,140,60,255,198,234,49,240,142,157,194,223,164,249,63,63,165,253,243,167,60,255,37,160,250,204,252,134, | |||
137,223,57,90,69,234,183,69,197,69,234,187,152,242,34,245,155,9,31,232,152,214,27,7,173,193,243,133,160,75,138,212,111,63,150, | |||
129,182,23,157,172,191,247,148,103,65,250,251,44,82,177,140,41,255,150,199,144,180,89,198,202,2,100,203,182,174,43,119,204,73, | |||
144,202,105,12,253,100,106,186,134,102,126,167,149,249,30,201,144,180,73,62,47,212,252,238,172,156,79,183,85,191,50,81,125,173, | |||
205,182,103,152,89,204,149,177,203,208,54,50,29,182,82,60,151,230,185,36,79,149,221,89,93,57,154,250,53,13,104,153,128,214,43, | |||
223,179,211,204,247,103,114,143,201,12,90,217,158,101,249,251,228,133,186,95,254,46,119,161,222,139,181,248,107,105,234,215, | |||
241,97,149,110,179,74,223,73,88,174,93,239,165,53,186,110,173,30,191,149,45,203,89,135,201,12,247,225,14,179,136,106,154,90,186, | |||
90,155,214,47,239,172,95,223,179,190,181,126,89,87,75,75,125,231,202,229,205,245,43,186,215,183,44,91,223,189,172,123,101, | |||
19,76,139,124,173,125,100,60,158,136,167,215,144,171,93,81,99,77,27,89,107,218,22,237,224,79,148,253,93,227,211,177,116,50,153, | |||
30,27,136,38,162,123,98,83,180,250,84,78,40,54,53,149,156,90,29,26,73,78,143,143,134,18,201,116,104,79,44,29,202,74,133,250, | |||
215,135,82,35,209,68,2,109,215,254,107,109,71,99,187,163,211,227,78,29,209,209,232,100,26,10,202,122,166,39,38,14,100,249,27, | |||
163,233,116,119,116,124,124,87,116,228,66,18,125,100,244,245,147,217,215,223,79,149,125,91,67,235,247,143,196,38,211,241,36,130, | |||
249,88,124,60,22,26,25,79,166,226,137,61,161,201,228,84,154,106,251,182,190,89,253,68,124,52,142,33,236,139,143,196,72,108,34, | |||
107,211,246,238,245,84,184,105,122,36,54,128,154,190,196,228,116,122,27,171,8,100,88,91,167,211,25,158,47,195,147,79,197,153, | |||
167,161,233,73,238,181,97,111,116,95,148,68,63,25,253,125,100,246,247,201,15,244,128,15,100,22,24,182,217,143,15,171,191,127, | |||
103,63,213,244,71,19,163,83,201,248,104,227,174,204,108,27,179,243,238,84,230,104,163,5,111,37,213,35,231,208,70,85,111,37,196, | |||
38,108,163,69,103,18,201,88,185,141,26,207,40,58,22,157,138,142,96,120,241,84,58,62,210,70,139,207,212,160,39,150,26,153,138, | |||
79,166,147,83,179,15,100,60,54,35,223,31,27,82,190,52,251,220,33,202,245,51,163,125,19,125,44,180,33,62,142,65,214,116,77,199, | |||
199,71,89,223,108,102,58,73,244,45,69,6,99,41,184,236,236,179,213,34,67,177,116,26,14,150,154,233,242,45,166,144,17,110,163, | |||
121,89,161,145,100,34,29,75,164,27,187,153,238,71,103,149,217,170,137,216,104,60,218,200,174,219,200,14,151,89,250,37,111,45,208, | |||
151,216,157,172,97,87,229,130,115,56,111,42,221,70,181,111,45,52,148,142,166,167,49,234,234,55,19,203,110,32,167,43,157,34,163, | |||
163,67,141,82,57,179,154,43,207,212,96,107,66,53,217,58,25,75,196,70,251,225,129,49,233,43,161,51,52,124,139,185,207,236,110, | |||
231,250,159,34,52,24,27,137,197,247,177,158,162,172,72,50,213,216,53,157,24,29,199,50,20,59,153,189,81,102,66,180,196,201,221, | |||
22,157,26,137,141,111,159,142,143,182,81,32,91,49,157,142,143,55,246,39,247,156,198,219,22,141,79,57,250,202,242,218,104,251, | |||
233,204,246,51,184,201,25,227,3,14,130,166,254,145,228,68,227,212,68,106,188,113,47,162,90,227,41,161,173,230,212,200,222,70, | |||
205,103,104,113,90,68,109,163,165,255,98,19,231,154,44,249,23,219,40,233,254,51,72,207,88,37,235,131,111,122,226,180,81,207,191, | |||
173,109,134,195,46,26,137,166,46,60,179,161,78,211,114,230,73,103,38,188,45,154,30,227,48,241,150,210,188,89,71,163,227,251, | |||
226,23,54,34,180,38,177,129,113,40,54,174,79,232,3,177,123,60,154,194,134,14,206,34,211,199,145,88,215,87,205,82,63,16,155,216, | |||
165,5,98,16,169,152,69,100,40,190,39,129,136,49,133,93,82,54,75,117,100,108,42,121,49,154,206,233,231,179,179,49,158,108,116, | |||
28,220,109,84,168,216,227,209,196,158,70,61,142,34,7,171,15,113,82,218,43,224,96,110,221,181,55,54,146,62,153,55,148,158,194, | |||
76,179,221,72,158,236,58,186,139,247,111,185,131,61,21,219,221,120,78,44,122,225,96,108,119,108,42,150,64,146,80,241,86,181, | |||
188,249,101,181,220,141,157,83,83,209,3,28,150,50,61,157,204,109,163,238,217,216,237,255,206,106,175,225,67,111,86,37,167,77,119, | |||
77,214,8,51,162,169,147,121,189,209,20,118,244,100,198,170,78,222,233,130,56,179,78,19,4,239,100,19,244,225,36,141,202,179,190, | |||
192,193,149,54,241,159,194,104,163,150,83,56,237,103,60,128,215,156,172,87,118,95,232,96,68,226,19,236,16,115,78,101,169,173, | |||
88,120,218,94,163,206,211,88,179,39,173,142,211,36,148,58,128,131,103,34,148,138,77,201,44,50,112,250,174,39,159,115,209,168, | |||
214,121,228,55,116,119,246,247,119,117,118,111,190,32,114,238,182,245,23,12,116,70,186,123,47,232,223,58,20,33,177,131,140,29, | |||
200,26,119,32,207,181,118,244,237,236,35,215,142,77,200,35,55,129,141,236,113,7,210,74,107,7,231,149,246,14,201,5,71,126,176, | |||
116,191,170,68,217,230,207,77,138,32,23,221,177,147,4,210,79,40,51,144,119,26,195,93,84,61,124,230,84,168,126,248,223,74,45,106, | |||
254,5,113,236,221,225,89,246,233,73,204,204,70,205,141,142,140,196,82,169,13,227,209,61,41,242,34,221,156,142,142,203,156,219, | |||
157,185,42,152,209,209,81,126,26,157,130,28,249,116,239,125,137,209,216,126,180,86,79,178,133,55,58,57,169,51,42,114,69,83,202, | |||
19,119,157,146,106,83,89,150,211,191,94,238,61,181,182,219,183,247,245,80,96,215,105,233,169,67,67,198,145,138,103,56,217,105, | |||
167,28,114,23,232,59,71,206,174,116,167,30,181,103,87,90,201,65,76,151,82,124,160,195,4,228,218,149,230,195,136,236,93,156,77, | |||
146,111,68,159,74,145,3,147,49,114,97,20,72,39,40,127,228,164,100,156,236,145,241,88,116,138,73,50,21,35,55,18,202,4,108,76, | |||
185,186,32,21,122,56,205,140,198,19,41,201,150,165,205,177,3,82,88,218,200,167,11,145,228,118,232,176,177,11,18,105,18,163,228, | |||
29,205,230,241,228,210,115,241,40,10,27,101,74,163,148,151,41,41,5,185,163,89,7,72,101,234,50,38,243,170,71,153,236,228,140,198, | |||
167,48,68,132,125,176,227,169,204,208,93,177,139,176,244,41,202,145,155,178,59,57,10,3,198,50,7,4,53,236,142,226,106,55,26, | |||
74,39,67,35,83,177,104,58,22,218,53,61,174,239,148,74,119,104,247,84,114,34,148,113,19,207,238,120,34,58,30,127,71,140,170,80, | |||
26,157,89,168,13,201,41,199,237,75,9,87,178,72,102,67,207,38,96,239,142,79,193,153,124,187,97,162,209,204,130,123,185,67,229, | |||
198,100,237,97,131,231,240,167,50,134,137,72,66,94,124,100,84,228,114,121,92,186,118,138,202,248,65,121,238,105,215,242,249, | |||
51,117,167,199,176,57,92,57,57,57,30,31,145,167,106,198,219,139,192,62,109,208,165,78,166,51,167,151,90,78,191,136,145,7,108,121, | |||
246,82,33,74,61,234,238,158,217,54,57,146,37,125,33,63,91,84,107,237,205,62,167,200,141,178,116,190,69,40,244,78,79,112,56,199, | |||
70,198,225,171,44,53,171,117,33,10,199,146,100,84,106,96,189,84,141,2,31,144,167,25,99,75,116,130,153,125,61,41,170,59,93,70, | |||
102,161,167,9,134,79,23,84,185,231,105,146,243,32,201,213,167,14,19,147,155,171,171,122,178,206,140,233,232,33,179,6,185,200, | |||
188,194,242,33,47,243,48,205,185,19,249,245,35,31,19,220,172,71,218,91,249,131,20,157,74,78,198,166,210,113,244,83,128,199,193, | |||
216,68,50,29,203,4,13,48,134,228,81,164,163,149,236,82,6,136,188,49,121,11,209,247,22,114,143,69,83,91,216,37,60,40,140,201, | |||
93,100,141,37,225,187,57,252,169,124,83,196,201,140,143,238,39,43,206,102,182,227,114,17,115,226,217,247,33,185,241,84,118,242, | |||
252,208,173,118,104,12,19,141,167,214,79,76,166,15,112,65,218,153,171,103,94,164,120,226,58,37,32,15,167,55,189,220,175,111,175, | |||
243,69,138,121,33,2,144,11,31,156,97,120,199,147,136,117,42,144,187,39,180,135,91,124,158,144,119,34,107,102,42,156,56,109, | |||
27,228,79,156,180,10,148,59,225,8,196,198,196,4,153,19,169,61,248,72,79,147,149,224,181,176,249,19,81,33,17,187,152,247,0,140, | |||
146,96,35,153,201,93,123,201,149,220,189,59,133,225,4,146,137,174,104,122,100,108,38,7,73,81,9,246,216,73,129,23,79,137,61,176, | |||
68,241,169,21,236,230,52,231,84,238,57,83,48,137,212,162,108,136,61,43,251,87,106,200,159,76,204,188,51,145,26,10,157,28,213, | |||
58,47,169,239,194,112,68,244,156,159,60,233,106,204,125,58,159,123,98,227,209,3,96,23,100,216,236,72,251,156,114,42,8,100,38, | |||
226,78,38,54,140,79,167,198,200,151,76,12,164,167,51,108,140,140,199,163,188,112,48,149,138,83,41,115,198,227,188,149,229,184, | |||
186,147,19,147,136,192,144,69,75,153,80,200,8,157,121,82,22,132,113,145,13,37,164,189,180,235,166,122,56,230,227,138,13,217, | |||
34,184,124,226,148,24,69,94,102,234,114,30,151,103,28,172,132,31,79,186,106,158,19,79,143,97,43,149,102,42,102,46,148,186,38, | |||
144,169,113,240,242,153,231,120,217,151,195,207,106,39,122,146,153,188,46,39,83,66,128,194,224,248,16,75,206,52,177,147,23,115, | |||
200,44,154,132,251,157,58,129,178,89,152,67,233,216,100,228,226,36,149,156,84,55,19,76,200,154,228,244,209,146,239,52,115,38, | |||
101,186,197,251,194,51,169,51,47,85,146,129,37,63,83,210,17,75,214,200,236,51,47,83,82,27,93,86,200,40,145,159,41,69,146,27,112, | |||
214,145,61,41,103,107,242,22,158,59,21,219,195,239,87,166,78,126,73,67,174,41,233,57,228,85,84,133,6,85,86,249,214,188,41,28, | |||
217,177,84,122,198,183,183,77,197,147,240,141,3,220,86,46,191,123,74,111,36,48,210,251,162,227,100,77,177,47,153,83,211,9,42, | |||
76,101,179,80,253,30,141,138,82,142,236,57,195,116,103,94,58,123,82,35,99,177,81,28,251,228,74,197,144,54,140,146,149,98,223, | |||
42,227,79,245,182,119,44,58,26,234,219,26,154,201,27,60,92,199,102,166,2,236,241,110,103,106,149,11,6,123,234,0,7,201,124,126, | |||
208,153,224,116,124,20,149,99,124,41,192,94,193,68,173,20,39,18,118,74,62,228,72,194,13,41,79,21,211,201,73,249,232,74,169,227, | |||
213,74,129,131,158,51,252,28,120,79,102,149,211,99,113,24,131,63,107,154,80,129,11,11,26,77,76,146,59,157,148,183,54,242,164, | |||
147,58,167,152,51,157,152,205,187,230,157,194,118,248,80,233,116,226,77,214,210,134,237,167,113,58,72,178,117,55,69,196,205, | |||
194,157,111,188,76,109,251,141,171,47,109,171,167,152,120,15,24,116,129,36,135,76,58,34,44,218,73,224,124,85,8,178,172,7,140,213, | |||
163,238,252,19,38,221,111,228,237,180,137,62,33,196,231,89,254,22,97,124,68,220,111,184,243,47,236,55,233,54,97,213,95,111,83, | |||
199,254,126,23,53,28,121,7,196,62,32,164,190,195,82,95,195,254,16,237,21,223,54,220,75,32,251,1,97,54,24,21,23,27,123,42,250, | |||
77,241,65,145,211,240,158,134,157,166,241,160,145,251,225,157,166,249,85,35,127,243,206,142,71,250,182,218,134,109,210,141,74, | |||
201,17,250,190,176,94,23,135,196,103,140,103,240,216,94,143,63,237,244,186,32,119,197,150,183,111,62,80,95,111,76,87,84,154, | |||
244,5,209,64,199,193,204,111,111,167,163,6,207,224,105,126,162,107,101,249,253,134,245,119,113,153,113,139,248,33,198,92,127, | |||
11,221,96,152,234,25,117,207,177,220,163,59,59,232,165,76,225,78,195,84,29,170,238,232,167,198,44,157,221,107,168,206,94,146, | |||
29,124,69,126,126,111,70,237,150,11,140,75,50,162,95,151,149,223,146,159,55,153,6,6,143,65,180,215,211,189,166,241,21,113,35, | |||
143,225,30,211,228,210,227,232,145,62,239,40,127,154,203,143,27,255,132,76,199,230,15,211,23,249,241,211,170,234,1,71,249,33, | |||
46,255,67,149,31,228,242,151,13,89,254,50,119,32,75,119,103,75,223,54,45,250,140,184,77,60,8,157,59,121,118,199,76,140,171,163, | |||
29,139,243,37,99,109,255,206,225,53,91,206,95,83,111,147,177,191,205,69,244,146,172,236,143,155,226,57,81,116,224,17,185,160, | |||
245,231,219,100,139,249,149,171,232,160,197,83,122,183,252,124,15,127,118,28,218,31,44,167,235,45,118,179,10,227,176,213,102, | |||
188,126,201,146,250,71,251,141,252,139,141,125,21,251,247,239,63,16,71,55,162,91,233,91,189,198,22,244,126,151,92,102,17,240, | |||
91,198,171,162,178,243,144,179,171,111,115,79,182,65,71,181,208,92,191,73,183,139,38,200,28,53,106,111,227,74,186,220,195,253, | |||
30,54,141,255,21,221,65,147,158,21,66,184,109,50,5,10,143,88,38,107,20,134,45,92,36,114,109,114,137,74,219,172,151,26,127,105, | |||
137,187,97,142,142,120,229,176,101,220,105,44,31,22,197,126,83,220,110,212,237,55,14,124,131,37,214,187,12,140,245,255,196,122, | |||
186,218,35,238,224,5,16,129,2,139,88,225,175,67,54,205,175,60,139,254,100,153,183,139,95,139,63,112,101,187,153,243,89,67,244, | |||
155,38,84,52,30,50,106,150,24,219,43,108,211,206,89,238,50,93,57,123,45,247,221,104,215,176,217,116,221,38,10,27,224,22,47,137, | |||
5,13,123,77,227,19,198,188,122,12,143,118,217,6,246,206,173,205,152,145,105,187,108,183,113,17,140,143,150,46,151,123,175,233, | |||
249,173,152,35,165,132,233,34,195,87,1,33,136,216,158,74,186,7,54,175,24,238,24,22,115,11,48,118,209,244,77,91,172,110,175,228, | |||
39,227,101,81,7,147,218,70,3,27,22,91,180,178,179,127,255,59,174,180,105,184,157,94,112,203,169,99,222,247,24,203,119,30,49, | |||
177,3,138,238,229,185,7,226,183,236,91,213,191,200,22,219,176,135,239,182,217,228,21,29,123,227,43,140,253,21,135,154,91,227, | |||
13,149,116,92,46,245,143,228,231,119,221,226,86,168,57,96,186,49,241,142,251,228,178,5,77,227,21,33,110,189,213,180,160,13,179, | |||
189,219,16,152,180,121,155,33,246,110,54,237,59,140,165,113,225,181,237,6,151,93,203,38,198,92,45,219,182,93,198,72,133,237,94, | |||
238,18,46,195,101,241,148,141,75,218,80,225,50,74,54,177,20,252,236,117,183,113,183,113,59,59,64,113,129,73,159,54,234,46,196, | |||
0,239,113,161,71,119,254,37,229,244,172,75,60,193,107,185,217,244,96,12,232,246,118,33,130,166,251,43,194,174,52,115,158,23, | |||
107,159,57,16,124,196,180,57,20,109,54,173,235,196,210,91,133,199,118,215,163,159,242,139,121,0,247,154,185,240,166,251,68,161, | |||
223,206,13,26,163,21,24,132,125,139,229,125,141,87,108,111,67,251,218,117,182,119,133,26,40,91,221,206,89,197,99,116,121,92,57, | |||
174,92,35,221,102,231,178,60,125,204,173,198,0,71,68,239,143,30,62,193,219,208,62,98,4,111,182,205,160,177,187,2,61,99,108, | |||
143,152,66,13,131,48,140,67,150,45,251,232,119,153,13,237,93,114,233,237,205,77,210,48,22,61,160,102,215,113,216,34,237,58,240, | |||
229,194,1,83,200,89,89,247,25,107,59,130,188,29,120,161,15,139,194,130,96,53,214,186,125,205,48,154,195,170,193,114,24,232, | |||
105,151,218,241,159,178,13,118,83,148,110,179,5,91,145,224,224,71,132,123,137,26,134,49,103,204,72,84,116,196,141,188,37,198, | |||
190,182,91,132,223,47,59,105,248,72,195,60,250,133,20,204,143,211,29,46,185,216,59,233,25,233,22,249,70,120,204,24,174,232,56, | |||
124,136,227,138,141,142,207,179,13,172,48,236,200,147,131,5,218,113,34,108,182,172,215,102,198,126,73,167,109,194,139,97,72, | |||
172,47,124,27,86,171,52,5,199,199,191,27,230,157,226,179,226,189,58,188,211,251,77,113,208,112,87,180,204,251,98,189,73,55,25, | |||
117,116,136,163,16,182,159,73,191,17,77,159,183,169,29,145,233,81,236,251,138,122,106,103,87,252,130,71,124,10,190,138,16,118, | |||
88,152,134,111,137,113,113,197,133,29,155,247,195,115,110,145,53,249,135,106,141,119,84,40,142,17,22,63,54,74,235,140,69,198, | |||
127,11,43,231,31,162,36,215,88,12,78,185,85,114,126,137,93,50,84,226,81,143,118,137,40,137,130,177,164,36,71,138,214,228,184, | |||
32,91,58,211,108,174,177,132,229,68,233,210,25,94,137,82,94,171,56,6,56,249,51,197,248,140,220,30,227,44,110,107,148,214,148, | |||
46,200,116,119,158,236,189,102,166,127,102,156,93,178,33,195,112,151,156,3,198,26,160,9,82,222,12,147,165,122,75,182,227,179, | |||
39,195,244,96,232,67,37,102,86,54,171,209,144,42,122,51,12,23,24,51,50,114,112,94,12,110,175,26,156,171,180,182,116,97,105,85, | |||
105,168,180,178,180,90,148,88,194,20,57,102,153,129,63,194,104,57,120,208,58,182,112,153,56,88,39,196,109,192,147,192,225,48, | |||
220,30,56,14,60,185,72,136,155,206,18,130,255,145,50,185,182,93,230,33,49,11,200,54,114,92,250,135,56,130,58,46,59,104,189,123, | |||
137,117,185,65,107,196,189,75,132,117,125,189,16,15,212,27,226,231,160,47,3,239,110,16,226,30,224,81,224,96,35,2,188,59,87,182, | |||
235,69,187,7,27,251,196,175,26,133,245,104,147,16,207,2,135,155,133,184,17,248,21,240,151,102,50,132,55,223,16,87,135,118,64, | |||
244,240,210,115,196,109,75,133,120,16,56,6,60,11,92,223,34,196,93,192,195,192,147,192,243,192,27,45,100,9,219,143,201,10,110, | |||
26,69,211,171,151,237,18,15,44,195,8,150,11,241,216,10,104,7,14,175,36,183,59,80,172,196,244,223,61,144,125,114,165,97,92,181, | |||
74,24,215,174,54,141,55,86,11,227,141,54,211,184,171,195,107,220,180,102,204,122,99,173,41,30,239,130,165,186,77,241,100,15, | |||
102,215,99,136,171,214,99,164,27,48,132,141,120,6,158,221,4,221,3,232,99,11,248,192,213,91,13,113,207,86,240,183,193,18,103,195, | |||
186,103,195,2,198,124,193,127,14,10,116,120,253,208,101,152,212,16,38,19,225,159,231,5,189,194,251,46,113,248,160,245,66,132, | |||
107,15,111,23,57,55,1,87,237,200,252,255,74,206,223,244,100,254,239,64,254,173,74,230,255,15,228,223,169,100,254,15,65,254,157, | |||
74,136,212,255,35,200,191,213,201,252,95,130,46,154,249,255,4,77,191,250,29,141,252,61,85,72,253,63,82,219,192,112,133,148,12, | |||
255,123,122,225,87,191,125,231,127,3,111,132,84,191,252,255,15,154,90,158,255,141,182,21,82,191,75,226,127,199,109,135,212,248, | |||
248,223,224,147,214,195,255,38,159,127,204,195,124,254,127,15,255,63,171,27,97,244,48,81,0,0,0,0}; | |||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | |||
STATICMETHOD (getAndroidMidiDeviceManager, "getAndroidMidiDeviceManager", "(Landroid/content/Context;)Lcom/rmsl/juce/JuceMidiSupport$MidiDeviceManager;") \ | |||
STATICMETHOD (getAndroidBluetoothManager, "getAndroidBluetoothManager", "(Landroid/content/Context;)Lcom/rmsl/juce/JuceMidiSupport$BluetoothManager;") | |||
DECLARE_JNI_CLASS_WITH_BYTECODE (JuceMidiSupport, "com/rmsl/juce/JuceMidiSupport", 23, javaMidiByteCode, sizeof (javaMidiByteCode)) | |||
#undef JNI_CLASS_MEMBERS | |||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | |||
METHOD (getJuceAndroidMidiInputDeviceNameAndIDs, "getJuceAndroidMidiInputDeviceNameAndIDs", "()[Ljava/lang/String;") \ | |||
METHOD (getJuceAndroidMidiOutputDeviceNameAndIDs, "getJuceAndroidMidiOutputDeviceNameAndIDs", "()[Ljava/lang/String;") \ | |||
METHOD (openMidiInputPortWithID, "openMidiInputPortWithID", "(IJ)Lcom/rmsl/juce/JuceMidiSupport$JuceMidiPort;") \ | |||
METHOD (openMidiOutputPortWithID, "openMidiOutputPortWithID", "(I)Lcom/rmsl/juce/JuceMidiSupport$JuceMidiPort;") | |||
DECLARE_JNI_CLASS_WITH_MIN_SDK (MidiDeviceManager, "com/rmsl/juce/JuceMidiSupport$MidiDeviceManager", 23) | |||
#undef JNI_CLASS_MEMBERS | |||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | |||
METHOD (start, "start", "()V") \ | |||
METHOD (stop, "stop", "()V") \ | |||
METHOD (close, "close", "()V") \ | |||
METHOD (sendMidi, "sendMidi", "([BII)V") \ | |||
METHOD (getName, "getName", "()Ljava/lang/String;") | |||
DECLARE_JNI_CLASS_WITH_MIN_SDK (JuceMidiPort, "com/rmsl/juce/JuceMidiSupport$JuceMidiPort", 23) | |||
#undef JNI_CLASS_MEMBERS | |||
//============================================================================== | |||
class AndroidMidiInput | |||
{ | |||
public: | |||
AndroidMidiInput (MidiInput* midiInput, int deviceID, juce::MidiInputCallback* midiInputCallback, jobject deviceManager) | |||
: juceMidiInput (midiInput), callback (midiInputCallback), midiConcatenator (2048), | |||
javaMidiDevice (LocalRef<jobject>(getEnv()->CallObjectMethod (deviceManager, MidiDeviceManager.openMidiInputPortWithID, | |||
(jint) deviceID, (jlong) this))) | |||
{ | |||
} | |||
~AndroidMidiInput() | |||
{ | |||
if (jobject d = javaMidiDevice.get()) | |||
{ | |||
getEnv()->CallVoidMethod (d, JuceMidiPort.close); | |||
javaMidiDevice.clear(); | |||
} | |||
} | |||
bool isOpen() const noexcept | |||
{ | |||
return javaMidiDevice != nullptr; | |||
} | |||
void start() | |||
{ | |||
if (jobject d = javaMidiDevice.get()) | |||
getEnv()->CallVoidMethod (d, JuceMidiPort.start); | |||
} | |||
void stop() | |||
{ | |||
if (jobject d = javaMidiDevice.get()) | |||
getEnv()->CallVoidMethod (d, JuceMidiPort.stop); | |||
callback = nullptr; | |||
} | |||
String getName() const noexcept | |||
{ | |||
if (jobject d = javaMidiDevice.get()) | |||
return juceString (LocalRef<jstring> ((jstring) getEnv()->CallObjectMethod (d, JuceMidiPort.getName))); | |||
return {}; | |||
} | |||
void handleMidi (jbyteArray byteArray, jlong offset, jint len, jlong timestamp) | |||
{ | |||
auto* env = getEnv(); | |||
jassert (byteArray != nullptr); | |||
auto* data = env->GetByteArrayElements (byteArray, nullptr); | |||
HeapBlock<uint8> buffer (static_cast<size_t> (len)); | |||
std::memcpy (buffer.get(), data + offset, static_cast<size_t> (len)); | |||
midiConcatenator.pushMidiData (buffer.get(), | |||
len, static_cast<double> (timestamp) * 1.0e-9, | |||
juceMidiInput, *callback); | |||
env->ReleaseByteArrayElements (byteArray, data, 0); | |||
} | |||
static void handleReceive (JNIEnv*, jobject, jlong host, jbyteArray byteArray, | |||
jint offset, jint len, jlong timestamp) | |||
{ | |||
auto* myself = reinterpret_cast<AndroidMidiInput*> (host); | |||
myself->handleMidi (byteArray, offset, len, timestamp); | |||
} | |||
private: | |||
MidiInput* juceMidiInput; | |||
MidiInputCallback* callback; | |||
MidiDataConcatenator midiConcatenator; | |||
GlobalRef javaMidiDevice; | |||
}; | |||
//============================================================================== | |||
class AndroidMidiOutput | |||
{ | |||
public: | |||
AndroidMidiOutput (const LocalRef<jobject>& midiDevice) | |||
: javaMidiDevice (midiDevice) | |||
{ | |||
} | |||
~AndroidMidiOutput() | |||
{ | |||
if (jobject d = javaMidiDevice.get()) | |||
{ | |||
getEnv()->CallVoidMethod (d, JuceMidiPort.close); | |||
javaMidiDevice.clear(); | |||
} | |||
} | |||
void send (jbyteArray byteArray, jint offset, jint len) | |||
{ | |||
if (jobject d = javaMidiDevice.get()) | |||
getEnv()->CallVoidMethod (d, | |||
JuceMidiPort.sendMidi, | |||
byteArray, offset, len); | |||
} | |||
String getName() const noexcept | |||
{ | |||
if (jobject d = javaMidiDevice.get()) | |||
return juceString (LocalRef<jstring> ((jstring) getEnv()->CallObjectMethod (d, JuceMidiPort.getName))); | |||
return {}; | |||
} | |||
private: | |||
GlobalRef javaMidiDevice; | |||
}; | |||
//============================================================================== | |||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | |||
CALLBACK (AndroidMidiInput::handleReceive, "handleReceive", "(J[BIIJ)V" ) | |||
DECLARE_JNI_CLASS_WITH_MIN_SDK (JuceMidiInputPort, "com/rmsl/juce/JuceMidiSupport$JuceMidiInputPort", 23) | |||
#undef JNI_CLASS_MEMBERS | |||
//============================================================================== | |||
class AndroidMidiDeviceManager | |||
{ | |||
public: | |||
AndroidMidiDeviceManager() | |||
: deviceManager (LocalRef<jobject>(getEnv()->CallStaticObjectMethod (JuceMidiSupport, | |||
JuceMidiSupport.getAndroidMidiDeviceManager, | |||
getAppContext().get()))) | |||
{ | |||
} | |||
Array<MidiDeviceInfo> getDevices (bool input) | |||
{ | |||
if (jobject dm = deviceManager.get()) | |||
{ | |||
jobjectArray jDeviceNameAndIDs | |||
= (jobjectArray) getEnv()->CallObjectMethod (dm, input ? MidiDeviceManager.getJuceAndroidMidiInputDeviceNameAndIDs | |||
: MidiDeviceManager.getJuceAndroidMidiOutputDeviceNameAndIDs); | |||
// Create a local reference as converting this to a JUCE string will call into JNI | |||
LocalRef<jobjectArray> localDeviceNameAndIDs (jDeviceNameAndIDs); | |||
auto deviceNameAndIDs = javaStringArrayToJuce (localDeviceNameAndIDs); | |||
deviceNameAndIDs.appendNumbersToDuplicates (false, false, CharPointer_UTF8 ("-"), CharPointer_UTF8 ("")); | |||
Array<MidiDeviceInfo> devices; | |||
for (int i = 0; i < deviceNameAndIDs.size(); i += 2) | |||
devices.add ({ deviceNameAndIDs[i], deviceNameAndIDs[i + 1] }); | |||
return devices; | |||
} | |||
return {}; | |||
} | |||
AndroidMidiInput* openMidiInputPortWithID (int deviceID, MidiInput* juceMidiInput, juce::MidiInputCallback* callback) | |||
{ | |||
if (auto dm = deviceManager.get()) | |||
{ | |||
std::unique_ptr<AndroidMidiInput> androidMidiInput (new AndroidMidiInput (juceMidiInput, deviceID, callback, dm)); | |||
if (androidMidiInput->isOpen()) | |||
return androidMidiInput.release(); | |||
} | |||
return nullptr; | |||
} | |||
AndroidMidiOutput* openMidiOutputPortWithID (int deviceID) | |||
{ | |||
if (auto dm = deviceManager.get()) | |||
if (auto javaMidiPort = getEnv()->CallObjectMethod (dm, MidiDeviceManager.openMidiOutputPortWithID, (jint) deviceID)) | |||
return new AndroidMidiOutput (LocalRef<jobject>(javaMidiPort)); | |||
return nullptr; | |||
} | |||
private: | |||
GlobalRef deviceManager; | |||
}; | |||
//============================================================================== | |||
Array<MidiDeviceInfo> MidiInput::getAvailableDevices() | |||
{ | |||
if (getAndroidSDKVersion() < 23) | |||
return {}; | |||
AndroidMidiDeviceManager manager; | |||
return manager.getDevices (true); | |||
} | |||
MidiDeviceInfo MidiInput::getDefaultDevice() | |||
{ | |||
if (getAndroidSDKVersion() < 23) | |||
return {}; | |||
return getAvailableDevices().getFirst(); | |||
} | |||
std::unique_ptr<MidiInput> MidiInput::openDevice (const String& deviceIdentifier, MidiInputCallback* callback) | |||
{ | |||
if (getAndroidSDKVersion() < 23 || deviceIdentifier.isEmpty()) | |||
return {}; | |||
AndroidMidiDeviceManager manager; | |||
std::unique_ptr<MidiInput> midiInput (new MidiInput ({}, deviceIdentifier)); | |||
if (auto* port = manager.openMidiInputPortWithID (deviceIdentifier.getIntValue(), midiInput.get(), callback)) | |||
{ | |||
midiInput->internal = port; | |||
midiInput->setName (port->getName()); | |||
return midiInput; | |||
} | |||
return {}; | |||
} | |||
StringArray MidiInput::getDevices() | |||
{ | |||
if (getAndroidSDKVersion() < 23) | |||
return {}; | |||
StringArray deviceNames; | |||
for (auto& d : getAvailableDevices()) | |||
deviceNames.add (d.name); | |||
return deviceNames; | |||
} | |||
int MidiInput::getDefaultDeviceIndex() | |||
{ | |||
return (getAndroidSDKVersion() < 23 ? -1 : 0); | |||
} | |||
std::unique_ptr<MidiInput> MidiInput::openDevice (int index, MidiInputCallback* callback) | |||
{ | |||
return openDevice (getAvailableDevices()[index].identifier, callback); | |||
} | |||
MidiInput::MidiInput (const String& deviceName, const String& deviceIdentifier) | |||
: deviceInfo (deviceName, deviceIdentifier) | |||
{ | |||
} | |||
MidiInput::~MidiInput() | |||
{ | |||
delete reinterpret_cast<AndroidMidiInput*> (internal); | |||
} | |||
void MidiInput::start() | |||
{ | |||
if (auto* mi = reinterpret_cast<AndroidMidiInput*> (internal)) | |||
mi->start(); | |||
} | |||
void MidiInput::stop() | |||
{ | |||
if (auto* mi = reinterpret_cast<AndroidMidiInput*> (internal)) | |||
mi->stop(); | |||
} | |||
//============================================================================== | |||
Array<MidiDeviceInfo> MidiOutput::getAvailableDevices() | |||
{ | |||
if (getAndroidSDKVersion() < 23) | |||
return {}; | |||
AndroidMidiDeviceManager manager; | |||
return manager.getDevices (false); | |||
} | |||
MidiDeviceInfo MidiOutput::getDefaultDevice() | |||
{ | |||
if (getAndroidSDKVersion() < 23) | |||
return {}; | |||
return getAvailableDevices().getFirst(); | |||
} | |||
std::unique_ptr<MidiOutput> MidiOutput::openDevice (const String& deviceIdentifier) | |||
{ | |||
if (getAndroidSDKVersion() < 23 || deviceIdentifier.isEmpty()) | |||
return {}; | |||
AndroidMidiDeviceManager manager; | |||
if (auto* port = manager.openMidiOutputPortWithID (deviceIdentifier.getIntValue())) | |||
{ | |||
std::unique_ptr<MidiOutput> midiOutput (new MidiOutput ({}, deviceIdentifier)); | |||
midiOutput->internal = port; | |||
midiOutput->setName (port->getName()); | |||
return midiOutput; | |||
} | |||
return {}; | |||
} | |||
StringArray MidiOutput::getDevices() | |||
{ | |||
if (getAndroidSDKVersion() < 23) | |||
return {}; | |||
StringArray deviceNames; | |||
for (auto& d : getAvailableDevices()) | |||
deviceNames.add (d.name); | |||
return deviceNames; | |||
} | |||
int MidiOutput::getDefaultDeviceIndex() | |||
{ | |||
return (getAndroidSDKVersion() < 23 ? -1 : 0); | |||
} | |||
std::unique_ptr<MidiOutput> MidiOutput::openDevice (int index) | |||
{ | |||
return openDevice (getAvailableDevices()[index].identifier); | |||
} | |||
MidiOutput::~MidiOutput() | |||
{ | |||
stopBackgroundThread(); | |||
delete reinterpret_cast<AndroidMidiOutput*> (internal); | |||
} | |||
void MidiOutput::sendMessageNow (const MidiMessage& message) | |||
{ | |||
if (auto* androidMidi = reinterpret_cast<AndroidMidiOutput*>(internal)) | |||
{ | |||
auto* env = getEnv(); | |||
auto messageSize = message.getRawDataSize(); | |||
LocalRef<jbyteArray> messageContent (env->NewByteArray (messageSize)); | |||
auto content = messageContent.get(); | |||
auto* rawBytes = env->GetByteArrayElements (content, nullptr); | |||
std::memcpy (rawBytes, message.getRawData(), static_cast<size_t> (messageSize)); | |||
env->ReleaseByteArrayElements (content, rawBytes, 0); | |||
androidMidi->send (content, (jint) 0, (jint) messageSize); | |||
} | |||
} | |||
} // namespace juce |
@@ -1,93 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2020 - Raw Material Software Limited | |||
JUCE is an open source library subject to commercial or open-source | |||
licensing. | |||
The code included in this file is provided under the terms of the ISC license | |||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission | |||
To use, copy, modify, and/or distribute this software for any purpose with or | |||
without fee is hereby granted provided that the above copyright notice and | |||
this permission notice appear in all copies. | |||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
DISCLAIMED. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
class iOSAudioIODeviceType; | |||
class iOSAudioIODevice : public AudioIODevice | |||
{ | |||
public: | |||
//============================================================================== | |||
String open (const BigInteger&, const BigInteger&, double, int) override; | |||
void close() override; | |||
void start (AudioIODeviceCallback*) override; | |||
void stop() override; | |||
Array<double> getAvailableSampleRates() override; | |||
Array<int> getAvailableBufferSizes() override; | |||
bool setAudioPreprocessingEnabled (bool) override; | |||
//============================================================================== | |||
bool isPlaying() override; | |||
bool isOpen() override; | |||
String getLastError() override; | |||
//============================================================================== | |||
StringArray getOutputChannelNames() override; | |||
StringArray getInputChannelNames() override; | |||
int getDefaultBufferSize() override; | |||
int getCurrentBufferSizeSamples() override; | |||
double getCurrentSampleRate() override; | |||
int getCurrentBitDepth() override; | |||
BigInteger getActiveOutputChannels() const override; | |||
BigInteger getActiveInputChannels() const override; | |||
int getOutputLatencyInSamples() override; | |||
int getInputLatencyInSamples() override; | |||
int getXRunCount() const noexcept override; | |||
//============================================================================== | |||
void setMidiMessageCollector (MidiMessageCollector*); | |||
AudioPlayHead* getAudioPlayHead() const; | |||
//============================================================================== | |||
bool isInterAppAudioConnected() const; | |||
#if JUCE_MODULE_AVAILABLE_juce_graphics | |||
Image getIcon (int size); | |||
#endif | |||
void switchApplication(); | |||
private: | |||
//============================================================================== | |||
iOSAudioIODevice (iOSAudioIODeviceType*, const String&, const String&); | |||
//============================================================================== | |||
friend class iOSAudioIODeviceType; | |||
friend struct AudioSessionHolder; | |||
struct Pimpl; | |||
friend struct Pimpl; | |||
std::unique_ptr<Pimpl> pimpl; | |||
JUCE_DECLARE_NON_COPYABLE (iOSAudioIODevice) | |||
}; | |||
} // namespace juce |
@@ -1,91 +0,0 @@ | |||
cmake_minimum_required(VERSION 3.4.1) | |||
# Set the name of the project and store it in PROJECT_NAME. Also set the following variables: | |||
# PROJECT_SOURCE_DIR (usually the root directory where Oboe has been cloned e.g.) | |||
# PROJECT_BINARY_DIR (usually the containing project's binary directory, | |||
# e.g. ${OBOE_HOME}/samples/RhythmGame/.externalNativeBuild/cmake/ndkExtractorDebug/x86/oboe-bin) | |||
project(oboe) | |||
set (oboe_sources | |||
src/aaudio/AAudioLoader.cpp | |||
src/aaudio/AudioStreamAAudio.cpp | |||
src/common/AudioSourceCaller.cpp | |||
src/common/AudioStream.cpp | |||
src/common/AudioStreamBuilder.cpp | |||
src/common/DataConversionFlowGraph.cpp | |||
src/common/FilterAudioStream.cpp | |||
src/common/FixedBlockAdapter.cpp | |||
src/common/FixedBlockReader.cpp | |||
src/common/FixedBlockWriter.cpp | |||
src/common/LatencyTuner.cpp | |||
src/common/SourceFloatCaller.cpp | |||
src/common/SourceI16Caller.cpp | |||
src/common/Utilities.cpp | |||
src/common/QuirksManager.cpp | |||
src/fifo/FifoBuffer.cpp | |||
src/fifo/FifoController.cpp | |||
src/fifo/FifoControllerBase.cpp | |||
src/fifo/FifoControllerIndirect.cpp | |||
src/flowgraph/FlowGraphNode.cpp | |||
src/flowgraph/ClipToRange.cpp | |||
src/flowgraph/ManyToMultiConverter.cpp | |||
src/flowgraph/MonoToMultiConverter.cpp | |||
src/flowgraph/RampLinear.cpp | |||
src/flowgraph/SampleRateConverter.cpp | |||
src/flowgraph/SinkFloat.cpp | |||
src/flowgraph/SinkI16.cpp | |||
src/flowgraph/SinkI24.cpp | |||
src/flowgraph/SourceFloat.cpp | |||
src/flowgraph/SourceI16.cpp | |||
src/flowgraph/SourceI24.cpp | |||
src/flowgraph/resampler/IntegerRatio.cpp | |||
src/flowgraph/resampler/LinearResampler.cpp | |||
src/flowgraph/resampler/MultiChannelResampler.cpp | |||
src/flowgraph/resampler/PolyphaseResampler.cpp | |||
src/flowgraph/resampler/PolyphaseResamplerMono.cpp | |||
src/flowgraph/resampler/PolyphaseResamplerStereo.cpp | |||
src/flowgraph/resampler/SincResampler.cpp | |||
src/flowgraph/resampler/SincResamplerStereo.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 | |||
src/common/StabilizedCallback.cpp | |||
src/common/Trace.cpp | |||
src/common/Version.cpp | |||
) | |||
add_library(oboe ${oboe_sources}) | |||
# Specify directories which the compiler should look for headers | |||
target_include_directories(oboe | |||
PRIVATE src | |||
PUBLIC include) | |||
# JUCE CHANGE STARTS HERE | |||
# This comment provided for Apache License compliance. We've removed the extra warnings flags and | |||
# the `-Werror` option, to avoid cases where compilers produce unexpected errors and fail the build. | |||
# We've also removed the explicit `-std=c++14` compile option, and replaced it with a more | |||
# cmake-friendly way of specifying the language standard. | |||
target_compile_options(oboe PRIVATE -Ofast) | |||
set_target_properties(oboe PROPERTIES CXX_STANDARD 14 CXX_STANDARD_REQUIRED TRUE CXX_EXTENSIONS FALSE) | |||
# JUCE CHANGE ENDS HERE | |||
# Enable logging for debug builds | |||
target_compile_definitions(oboe PUBLIC $<$<CONFIG:DEBUG>:OBOE_ENABLE_LOGGING=1>) | |||
target_link_libraries(oboe PRIVATE log OpenSLES) | |||
# When installing oboe put the libraries in the lib/<ABI> folder e.g. lib/arm64-v8a | |||
install(TARGETS oboe | |||
LIBRARY DESTINATION lib/${ANDROID_ABI} | |||
ARCHIVE DESTINATION lib/${ANDROID_ABI}) | |||
# Also install the headers | |||
install(DIRECTORY include/oboe DESTINATION include) |
@@ -1,202 +0,0 @@ | |||
Apache License | |||
Version 2.0, January 2004 | |||
http://www.apache.org/licenses/ | |||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |||
1. Definitions. | |||
"License" shall mean the terms and conditions for use, reproduction, | |||
and distribution as defined by Sections 1 through 9 of this document. | |||
"Licensor" shall mean the copyright owner or entity authorized by | |||
the copyright owner that is granting the License. | |||
"Legal Entity" shall mean the union of the acting entity and all | |||
other entities that control, are controlled by, or are under common | |||
control with that entity. For the purposes of this definition, | |||
"control" means (i) the power, direct or indirect, to cause the | |||
direction or management of such entity, whether by contract or | |||
otherwise, or (ii) ownership of fifty percent (50%) or more of the | |||
outstanding shares, or (iii) beneficial ownership of such entity. | |||
"You" (or "Your") shall mean an individual or Legal Entity | |||
exercising permissions granted by this License. | |||
"Source" form shall mean the preferred form for making modifications, | |||
including but not limited to software source code, documentation | |||
source, and configuration files. | |||
"Object" form shall mean any form resulting from mechanical | |||
transformation or translation of a Source form, including but | |||
not limited to compiled object code, generated documentation, | |||
and conversions to other media types. | |||
"Work" shall mean the work of authorship, whether in Source or | |||
Object form, made available under the License, as indicated by a | |||
copyright notice that is included in or attached to the work | |||
(an example is provided in the Appendix below). | |||
"Derivative Works" shall mean any work, whether in Source or Object | |||
form, that is based on (or derived from) the Work and for which the | |||
editorial revisions, annotations, elaborations, or other modifications | |||
represent, as a whole, an original work of authorship. For the purposes | |||
of this License, Derivative Works shall not include works that remain | |||
separable from, or merely link (or bind by name) to the interfaces of, | |||
the Work and Derivative Works thereof. | |||
"Contribution" shall mean any work of authorship, including | |||
the original version of the Work and any modifications or additions | |||
to that Work or Derivative Works thereof, that is intentionally | |||
submitted to Licensor for inclusion in the Work by the copyright owner | |||
or by an individual or Legal Entity authorized to submit on behalf of | |||
the copyright owner. For the purposes of this definition, "submitted" | |||
means any form of electronic, verbal, or written communication sent | |||
to the Licensor or its representatives, including but not limited to | |||
communication on electronic mailing lists, source code control systems, | |||
and issue tracking systems that are managed by, or on behalf of, the | |||
Licensor for the purpose of discussing and improving the Work, but | |||
excluding communication that is conspicuously marked or otherwise | |||
designated in writing by the copyright owner as "Not a Contribution." | |||
"Contributor" shall mean Licensor and any individual or Legal Entity | |||
on behalf of whom a Contribution has been received by Licensor and | |||
subsequently incorporated within the Work. | |||
2. Grant of Copyright License. Subject to the terms and conditions of | |||
this License, each Contributor hereby grants to You a perpetual, | |||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||
copyright license to reproduce, prepare Derivative Works of, | |||
publicly display, publicly perform, sublicense, and distribute the | |||
Work and such Derivative Works in Source or Object form. | |||
3. Grant of Patent License. Subject to the terms and conditions of | |||
this License, each Contributor hereby grants to You a perpetual, | |||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||
(except as stated in this section) patent license to make, have made, | |||
use, offer to sell, sell, import, and otherwise transfer the Work, | |||
where such license applies only to those patent claims licensable | |||
by such Contributor that are necessarily infringed by their | |||
Contribution(s) alone or by combination of their Contribution(s) | |||
with the Work to which such Contribution(s) was submitted. If You | |||
institute patent litigation against any entity (including a | |||
cross-claim or counterclaim in a lawsuit) alleging that the Work | |||
or a Contribution incorporated within the Work constitutes direct | |||
or contributory patent infringement, then any patent licenses | |||
granted to You under this License for that Work shall terminate | |||
as of the date such litigation is filed. | |||
4. Redistribution. You may reproduce and distribute copies of the | |||
Work or Derivative Works thereof in any medium, with or without | |||
modifications, and in Source or Object form, provided that You | |||
meet the following conditions: | |||
(a) You must give any other recipients of the Work or | |||
Derivative Works a copy of this License; and | |||
(b) You must cause any modified files to carry prominent notices | |||
stating that You changed the files; and | |||
(c) You must retain, in the Source form of any Derivative Works | |||
that You distribute, all copyright, patent, trademark, and | |||
attribution notices from the Source form of the Work, | |||
excluding those notices that do not pertain to any part of | |||
the Derivative Works; and | |||
(d) If the Work includes a "NOTICE" text file as part of its | |||
distribution, then any Derivative Works that You distribute must | |||
include a readable copy of the attribution notices contained | |||
within such NOTICE file, excluding those notices that do not | |||
pertain to any part of the Derivative Works, in at least one | |||
of the following places: within a NOTICE text file distributed | |||
as part of the Derivative Works; within the Source form or | |||
documentation, if provided along with the Derivative Works; or, | |||
within a display generated by the Derivative Works, if and | |||
wherever such third-party notices normally appear. The contents | |||
of the NOTICE file are for informational purposes only and | |||
do not modify the License. You may add Your own attribution | |||
notices within Derivative Works that You distribute, alongside | |||
or as an addendum to the NOTICE text from the Work, provided | |||
that such additional attribution notices cannot be construed | |||
as modifying the License. | |||
You may add Your own copyright statement to Your modifications and | |||
may provide additional or different license terms and conditions | |||
for use, reproduction, or distribution of Your modifications, or | |||
for any such Derivative Works as a whole, provided Your use, | |||
reproduction, and distribution of the Work otherwise complies with | |||
the conditions stated in this License. | |||
5. Submission of Contributions. Unless You explicitly state otherwise, | |||
any Contribution intentionally submitted for inclusion in the Work | |||
by You to the Licensor shall be under the terms and conditions of | |||
this License, without any additional terms or conditions. | |||
Notwithstanding the above, nothing herein shall supersede or modify | |||
the terms of any separate license agreement you may have executed | |||
with Licensor regarding such Contributions. | |||
6. Trademarks. This License does not grant permission to use the trade | |||
names, trademarks, service marks, or product names of the Licensor, | |||
except as required for reasonable and customary use in describing the | |||
origin of the Work and reproducing the content of the NOTICE file. | |||
7. Disclaimer of Warranty. Unless required by applicable law or | |||
agreed to in writing, Licensor provides the Work (and each | |||
Contributor provides its Contributions) on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |||
implied, including, without limitation, any warranties or conditions | |||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |||
PARTICULAR PURPOSE. You are solely responsible for determining the | |||
appropriateness of using or redistributing the Work and assume any | |||
risks associated with Your exercise of permissions under this License. | |||
8. Limitation of Liability. In no event and under no legal theory, | |||
whether in tort (including negligence), contract, or otherwise, | |||
unless required by applicable law (such as deliberate and grossly | |||
negligent acts) or agreed to in writing, shall any Contributor be | |||
liable to You for damages, including any direct, indirect, special, | |||
incidental, or consequential damages of any character arising as a | |||
result of this License or out of the use or inability to use the | |||
Work (including but not limited to damages for loss of goodwill, | |||
work stoppage, computer failure or malfunction, or any and all | |||
other commercial damages or losses), even if such Contributor | |||
has been advised of the possibility of such damages. | |||
9. Accepting Warranty or Additional Liability. While redistributing | |||
the Work or Derivative Works thereof, You may choose to offer, | |||
and charge a fee for, acceptance of support, warranty, indemnity, | |||
or other liability obligations and/or rights consistent with this | |||
License. However, in accepting such obligations, You may act only | |||
on Your own behalf and on Your sole responsibility, not on behalf | |||
of any other Contributor, and only if You agree to indemnify, | |||
defend, and hold each Contributor harmless for any liability | |||
incurred by, or claims asserted against, such Contributor by reason | |||
of your accepting any such warranty or additional liability. | |||
END OF TERMS AND CONDITIONS | |||
APPENDIX: How to apply the Apache License to your work. | |||
To apply the Apache License to your work, attach the following | |||
boilerplate notice, with the fields enclosed by brackets "[]" | |||
replaced with your own identifying information. (Don't include | |||
the brackets!) The text should be enclosed in the appropriate | |||
comment syntax for the file format. We also recommend that a | |||
file or class name and description of purpose be included on the | |||
same "printed page" as the copyright notice for easier | |||
identification within third-party archives. | |||
Copyright [yyyy] [name of copyright owner] | |||
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. |
@@ -1,523 +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_STREAM_H_ | |||
#define OBOE_STREAM_H_ | |||
#include <atomic> | |||
#include <cstdint> | |||
#include <ctime> | |||
#include <mutex> | |||
#include "oboe/Definitions.h" | |||
#include "oboe/ResultWithValue.h" | |||
#include "oboe/AudioStreamBuilder.h" | |||
#include "oboe/AudioStreamBase.h" | |||
/** WARNING - UNDER CONSTRUCTION - THIS API WILL CHANGE. */ | |||
namespace oboe { | |||
/** | |||
* The default number of nanoseconds to wait for when performing state change operations on the | |||
* stream, such as `start` and `stop`. | |||
* | |||
* @see oboe::AudioStream::start | |||
*/ | |||
constexpr int64_t kDefaultTimeoutNanos = (2000 * kNanosPerMillisecond); | |||
/** | |||
* Base class for Oboe C++ audio stream. | |||
*/ | |||
class AudioStream : public AudioStreamBase { | |||
public: | |||
AudioStream() {} | |||
/** | |||
* Construct an `AudioStream` using the given `AudioStreamBuilder` | |||
* | |||
* @param builder containing all the stream's attributes | |||
*/ | |||
explicit AudioStream(const AudioStreamBuilder &builder); | |||
virtual ~AudioStream() = default; | |||
/** | |||
* Open a stream based on the current settings. | |||
* | |||
* Note that we do not recommend re-opening a stream that has been closed. | |||
* TODO Should we prevent re-opening? | |||
* | |||
* @return | |||
*/ | |||
virtual Result open() { | |||
return Result::OK; // Called by subclasses. Might do more in the future. | |||
} | |||
/** | |||
* Close the stream and deallocate any resources from the open() call. | |||
*/ | |||
virtual Result close(); | |||
/** | |||
* Start the stream. This will block until the stream has been started, an error occurs | |||
* or `timeoutNanoseconds` has been reached. | |||
*/ | |||
virtual Result start(int64_t timeoutNanoseconds = kDefaultTimeoutNanos); | |||
/** | |||
* Pause the stream. This will block until the stream has been paused, an error occurs | |||
* or `timeoutNanoseconds` has been reached. | |||
*/ | |||
virtual Result pause(int64_t timeoutNanoseconds = kDefaultTimeoutNanos); | |||
/** | |||
* Flush the stream. This will block until the stream has been flushed, an error occurs | |||
* or `timeoutNanoseconds` has been reached. | |||
*/ | |||
virtual Result flush(int64_t timeoutNanoseconds = kDefaultTimeoutNanos); | |||
/** | |||
* Stop the stream. This will block until the stream has been stopped, an error occurs | |||
* or `timeoutNanoseconds` has been reached. | |||
*/ | |||
virtual Result stop(int64_t timeoutNanoseconds = kDefaultTimeoutNanos); | |||
/* Asynchronous requests. | |||
* Use waitForStateChange() if you need to wait for completion. | |||
*/ | |||
/** | |||
* Start the stream asynchronously. Returns immediately (does not block). Equivalent to calling | |||
* `start(0)`. | |||
*/ | |||
virtual Result requestStart() = 0; | |||
/** | |||
* Pause the stream asynchronously. Returns immediately (does not block). Equivalent to calling | |||
* `pause(0)`. | |||
*/ | |||
virtual Result requestPause() = 0; | |||
/** | |||
* Flush the stream asynchronously. Returns immediately (does not block). Equivalent to calling | |||
* `flush(0)`. | |||
*/ | |||
virtual Result requestFlush() = 0; | |||
/** | |||
* Stop the stream asynchronously. Returns immediately (does not block). Equivalent to calling | |||
* `stop(0)`. | |||
*/ | |||
virtual Result requestStop() = 0; | |||
/** | |||
* Query the current state, eg. StreamState::Pausing | |||
* | |||
* @return state or a negative error. | |||
*/ | |||
virtual StreamState getState() const = 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 | |||
* changing between calls. | |||
* | |||
* Note that generally applications do not need to call this. It is considered | |||
* an advanced technique and is mostly used for testing. | |||
* | |||
* <pre><code> | |||
* int64_t timeoutNanos = 500 * kNanosPerMillisecond; // arbitrary 1/2 second | |||
* StreamState currentState = stream->getState(); | |||
* StreamState nextState = StreamState::Unknown; | |||
* while (result == Result::OK && currentState != StreamState::Paused) { | |||
* result = stream->waitForStateChange( | |||
* currentState, &nextState, timeoutNanos); | |||
* currentState = nextState; | |||
* } | |||
* </code></pre> | |||
* | |||
* If the state does not change within the timeout period then it will | |||
* return ErrorTimeout. This is true even if timeoutNanoseconds is zero. | |||
* | |||
* @param inputState The state we want to change away from. | |||
* @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. | |||
*/ | |||
virtual Result waitForStateChange(StreamState inputState, | |||
StreamState *nextState, | |||
int64_t timeoutNanoseconds) = 0; | |||
/** | |||
* This can be used to adjust the latency of the buffer by changing | |||
* the threshold where blocking will occur. | |||
* By combining this with getXRunCount(), the latency can be tuned | |||
* at run-time for each device. | |||
* | |||
* This cannot be set higher than getBufferCapacity(). | |||
* | |||
* @param requestedFrames requested number of frames that can be filled without blocking | |||
* @return the resulting buffer size in frames (obtained using value()) or an error (obtained | |||
* using error()) | |||
*/ | |||
virtual ResultWithValue<int32_t> setBufferSizeInFrames(int32_t /* requestedFrames */) { | |||
return Result::ErrorUnimplemented; | |||
} | |||
/** | |||
* An XRun is an Underrun or an Overrun. | |||
* During playing, an underrun will occur if the stream is not written in time | |||
* and the system runs out of valid data. | |||
* During recording, an overrun will occur if the stream is not read in time | |||
* and there is no place to put the incoming data so it is discarded. | |||
* | |||
* An underrun or overrun can cause an audible "pop" or "glitch". | |||
* | |||
* @return a result which is either Result::OK with the xRun count as the value, or a | |||
* Result::Error* code | |||
*/ | |||
virtual ResultWithValue<int32_t> getXRunCount() const { | |||
return ResultWithValue<int32_t>(Result::ErrorUnimplemented); | |||
} | |||
/** | |||
* @return true if XRun counts are supported on the stream | |||
*/ | |||
virtual bool isXRunCountSupported() const = 0; | |||
/** | |||
* Query the number of frames that are read or written by the endpoint at one time. | |||
* | |||
* @return burst size | |||
*/ | |||
virtual int32_t getFramesPerBurst() = 0; | |||
/** | |||
* Get the number of bytes in each audio frame. This is calculated using the channel count | |||
* and the sample format. For example, a 2 channel floating point stream will have | |||
* 2 * 4 = 8 bytes per frame. | |||
* | |||
* @return number of bytes in each audio frame. | |||
*/ | |||
int32_t getBytesPerFrame() const { return mChannelCount * getBytesPerSample(); } | |||
/** | |||
* Get the number of bytes per sample. This is calculated using the sample format. For example, | |||
* a stream using 16-bit integer samples will have 2 bytes per sample. | |||
* | |||
* @return the number of bytes per sample. | |||
*/ | |||
int32_t getBytesPerSample() const; | |||
/** | |||
* The number of audio frames written into the stream. | |||
* This monotonic counter will never get reset. | |||
* | |||
* @return the number of frames written so far | |||
*/ | |||
virtual int64_t getFramesWritten(); | |||
/** | |||
* The number of audio frames read from the stream. | |||
* This monotonic counter will never get reset. | |||
* | |||
* @return the number of frames read so far | |||
*/ | |||
virtual int64_t getFramesRead(); | |||
/** | |||
* Calculate the latency of a stream based on getTimestamp(). | |||
* | |||
* Output latency is the time it takes for a given frame to travel from the | |||
* app to some type of digital-to-analog converter. If the DAC is external, for example | |||
* in a USB interface or a TV connected by HDMI, then there may be additional latency | |||
* that the Android device is unaware of. | |||
* | |||
* Input latency is the time it takes to a given frame to travel from an analog-to-digital | |||
* converter (ADC) to the app. | |||
* | |||
* Note that the latency of an OUTPUT stream will increase abruptly when you write data to it | |||
* and then decrease slowly over time as the data is consumed. | |||
* | |||
* The latency of an INPUT stream will decrease abruptly when you read data from it | |||
* and then increase slowly over time as more data arrives. | |||
* | |||
* The latency of an OUTPUT stream is generally higher than the INPUT latency | |||
* because an app generally tries to keep the OUTPUT buffer full and the INPUT buffer empty. | |||
* | |||
* @return a ResultWithValue which has a result of Result::OK and a value containing the latency | |||
* in milliseconds, or a result of Result::Error*. | |||
*/ | |||
virtual ResultWithValue<double> calculateLatencyMillis() { | |||
return ResultWithValue<double>(Result::ErrorUnimplemented); | |||
} | |||
/** | |||
* Get the estimated time that the frame at `framePosition` entered or left the audio processing | |||
* pipeline. | |||
* | |||
* This can be used to coordinate events and interactions with the external environment, and to | |||
* estimate the latency of an audio stream. An example of usage can be found in the hello-oboe | |||
* sample (search for "calculateCurrentOutputLatencyMillis"). | |||
* | |||
* The time is based on the implementation's best effort, using whatever knowledge is available | |||
* to the system, but cannot account for any delay unknown to the implementation. | |||
* | |||
* @deprecated since 1.0, use AudioStream::getTimestamp(clockid_t clockId) instead, which | |||
* returns ResultWithValue | |||
* @param clockId the type of clock to use e.g. CLOCK_MONOTONIC | |||
* @param framePosition the frame number to query | |||
* @param timeNanoseconds an output parameter which will contain the presentation timestamp | |||
*/ | |||
virtual Result getTimestamp(clockid_t /* clockId */, | |||
int64_t* /* framePosition */, | |||
int64_t* /* timeNanoseconds */) { | |||
return Result::ErrorUnimplemented; | |||
} | |||
/** | |||
* Get the estimated time that the frame at `framePosition` entered or left the audio processing | |||
* pipeline. | |||
* | |||
* This can be used to coordinate events and interactions with the external environment, and to | |||
* estimate the latency of an audio stream. An example of usage can be found in the hello-oboe | |||
* sample (search for "calculateCurrentOutputLatencyMillis"). | |||
* | |||
* The time is based on the implementation's best effort, using whatever knowledge is available | |||
* to the system, but cannot account for any delay unknown to the implementation. | |||
* | |||
* @param clockId the type of clock to use e.g. CLOCK_MONOTONIC | |||
* @return a FrameTimestamp containing the position and time at which a particular audio frame | |||
* entered or left the audio processing pipeline, or an error if the operation failed. | |||
*/ | |||
virtual ResultWithValue<FrameTimestamp> getTimestamp(clockid_t /* clockId */); | |||
// ============== I/O =========================== | |||
/** | |||
* Write data from the supplied buffer into the stream. This method will block until the write | |||
* is complete or it runs out of time. | |||
* | |||
* If `timeoutNanoseconds` is zero then this call will not wait. | |||
* | |||
* @param buffer The address of the first sample. | |||
* @param numFrames Number of frames to write. Only complete frames will be written. | |||
* @param timeoutNanoseconds Maximum number of nanoseconds to wait for completion. | |||
* @return a ResultWithValue which has a result of Result::OK and a value containing the number | |||
* of frames actually written, or result of Result::Error*. | |||
*/ | |||
virtual ResultWithValue<int32_t> write(const void* /* buffer */, | |||
int32_t /* numFrames */, | |||
int64_t /* timeoutNanoseconds */ ) { | |||
return ResultWithValue<int32_t>(Result::ErrorUnimplemented); | |||
} | |||
/** | |||
* Read data into the supplied buffer from the stream. This method will block until the read | |||
* is complete or it runs out of time. | |||
* | |||
* If `timeoutNanoseconds` is zero then this call will not wait. | |||
* | |||
* @param buffer The address of the first sample. | |||
* @param numFrames Number of frames to read. Only complete frames will be read. | |||
* @param timeoutNanoseconds Maximum number of nanoseconds to wait for completion. | |||
* @return a ResultWithValue which has a result of Result::OK and a value containing the number | |||
* of frames actually read, or result of Result::Error*. | |||
*/ | |||
virtual ResultWithValue<int32_t> read(void* /* buffer */, | |||
int32_t /* numFrames */, | |||
int64_t /* timeoutNanoseconds */) { | |||
return ResultWithValue<int32_t>(Result::ErrorUnimplemented); | |||
} | |||
/** | |||
* Get the underlying audio API which the stream uses. | |||
* | |||
* @return the API that this stream uses. | |||
*/ | |||
virtual AudioApi getAudioApi() const = 0; | |||
/** | |||
* Returns true if the underlying audio API is AAudio. | |||
* | |||
* @return true if this stream is implemented using the AAudio API. | |||
*/ | |||
bool usesAAudio() const { | |||
return getAudioApi() == AudioApi::AAudio; | |||
} | |||
/** | |||
* Only for debugging. Do not use in production. | |||
* If you need to call this method something is wrong. | |||
* If you think you need it for production then please let us know | |||
* so we can modify Oboe so that you don't need this. | |||
* | |||
* @return nullptr or a pointer to a stream from the system API | |||
*/ | |||
virtual void *getUnderlyingStream() const { | |||
return nullptr; | |||
} | |||
/** | |||
* Launch a thread that will stop the stream. | |||
*/ | |||
void launchStopThread(); | |||
/** | |||
* Update mFramesWritten. | |||
* For internal use only. | |||
*/ | |||
virtual void updateFramesWritten() = 0; | |||
/** | |||
* Update mFramesRead. | |||
* For internal use only. | |||
*/ | |||
virtual void updateFramesRead() = 0; | |||
/* | |||
* Swap old callback for new callback. | |||
* This not atomic. | |||
* This should only be used internally. | |||
* @param streamCallback | |||
* @return previous streamCallback | |||
*/ | |||
AudioStreamCallback *swapCallback(AudioStreamCallback *streamCallback) { | |||
AudioStreamCallback *previousCallback = mStreamCallback; | |||
mStreamCallback = streamCallback; | |||
return previousCallback; | |||
} | |||
/** | |||
* @return number of frames of data currently in the buffer | |||
*/ | |||
ResultWithValue<int32_t> getAvailableFrames(); | |||
/** | |||
* Wait until the stream has a minimum amount of data available in its buffer. | |||
* This can be used with an EXCLUSIVE MMAP input stream to avoid reading data too close to | |||
* the DSP write position, which may cause glitches. | |||
* | |||
* @param numFrames minimum frames available | |||
* @param timeoutNanoseconds | |||
* @return number of frames available, ErrorTimeout | |||
*/ | |||
ResultWithValue<int32_t> waitForAvailableFrames(int32_t numFrames, | |||
int64_t timeoutNanoseconds); | |||
protected: | |||
/** | |||
* This is used to detect more than one error callback from a stream. | |||
* These were bugs in some versions of Android that caused multiple error callbacks. | |||
* Internal bug b/63087953 | |||
* | |||
* Calling this sets an atomic<bool> true and returns the previous value. | |||
* | |||
* @return false on first call, true on subsequent calls | |||
*/ | |||
bool wasErrorCallbackCalled() { | |||
return mErrorCallbackCalled.exchange(true); | |||
} | |||
/** | |||
* Wait for a transition from one state to another. | |||
* @return OK if the endingState was observed, or ErrorUnexpectedState | |||
* if any state that was not the startingState or endingState was observed | |||
* or ErrorTimeout. | |||
*/ | |||
virtual Result waitForStateTransition(StreamState startingState, | |||
StreamState endingState, | |||
int64_t timeoutNanoseconds); | |||
/** | |||
* Override this to provide a default for when the application did not specify a callback. | |||
* | |||
* @param audioData | |||
* @param numFrames | |||
* @return result | |||
*/ | |||
virtual DataCallbackResult onDefaultCallback(void* /* audioData */, int /* numFrames */) { | |||
return DataCallbackResult::Stop; | |||
} | |||
/** | |||
* Override this to provide your own behaviour for the audio callback | |||
* | |||
* @param audioData container array which audio frames will be written into or read from | |||
* @param numFrames number of frames which were read/written | |||
* @return the result of the callback: stop or continue | |||
* | |||
*/ | |||
DataCallbackResult fireDataCallback(void *audioData, int numFrames); | |||
/** | |||
* @return true if callbacks may be called | |||
*/ | |||
bool isDataCallbackEnabled() { | |||
return mDataCallbackEnabled; | |||
} | |||
/** | |||
* This can be set false internally to prevent callbacks | |||
* after DataCallbackResult::Stop has been returned. | |||
*/ | |||
void setDataCallbackEnabled(bool enabled) { | |||
mDataCallbackEnabled = enabled; | |||
} | |||
/** | |||
* Number of frames which have been written into the stream | |||
* | |||
* This is signed integer to match the counters in AAudio. | |||
* At audio rates, the counter will overflow in about six million years. | |||
*/ | |||
std::atomic<int64_t> mFramesWritten{}; | |||
/** | |||
* Number of frames which have been read from the stream. | |||
* | |||
* This is signed integer to match the counters in AAudio. | |||
* At audio rates, the counter will overflow in about six million years. | |||
*/ | |||
std::atomic<int64_t> mFramesRead{}; | |||
std::mutex mLock; // for synchronizing start/stop/close | |||
private: | |||
int mPreviousScheduler = -1; | |||
std::atomic<bool> mDataCallbackEnabled{false}; | |||
std::atomic<bool> mErrorCallbackCalled{false}; | |||
}; | |||
/** | |||
* This struct is a stateless functor which closes a audiostream prior to its deletion. | |||
* This means it can be used to safely delete a smart pointer referring to an open stream. | |||
*/ | |||
struct StreamDeleterFunctor { | |||
void operator()(AudioStream *audioStream) { | |||
if (audioStream) { | |||
audioStream->close(); | |||
} | |||
delete audioStream; | |||
} | |||
}; | |||
} // namespace oboe | |||
#endif /* OBOE_STREAM_H_ */ |
@@ -1,201 +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_STREAM_BASE_H_ | |||
#define OBOE_STREAM_BASE_H_ | |||
#include <memory> | |||
#include "oboe/AudioStreamCallback.h" | |||
#include "oboe/Definitions.h" | |||
namespace oboe { | |||
/** | |||
* Base class containing parameters for audio streams and builders. | |||
**/ | |||
class AudioStreamBase { | |||
public: | |||
AudioStreamBase() {} | |||
virtual ~AudioStreamBase() = default; | |||
// This class only contains primitives so we can use default constructor and copy methods. | |||
/** | |||
* Default copy constructor | |||
*/ | |||
AudioStreamBase(const AudioStreamBase&) = default; | |||
/** | |||
* Default assignment operator | |||
*/ | |||
AudioStreamBase& operator=(const AudioStreamBase&) = default; | |||
/** | |||
* @return number of channels, for example 2 for stereo, or kUnspecified | |||
*/ | |||
int32_t getChannelCount() const { return mChannelCount; } | |||
/** | |||
* @return Direction::Input or Direction::Output | |||
*/ | |||
Direction getDirection() const { return mDirection; } | |||
/** | |||
* @return sample rate for the stream or kUnspecified | |||
*/ | |||
int32_t getSampleRate() const { return mSampleRate; } | |||
/** | |||
* @return the number of frames in each callback or kUnspecified. | |||
*/ | |||
int32_t getFramesPerCallback() const { return mFramesPerCallback; } | |||
/** | |||
* @return the audio sample format (e.g. Float or I16) | |||
*/ | |||
AudioFormat getFormat() const { return mFormat; } | |||
/** | |||
* Query the maximum number of frames that can be filled without blocking. | |||
* If the stream has been closed the last known value will be returned. | |||
* | |||
* @return buffer size | |||
*/ | |||
virtual int32_t getBufferSizeInFrames() { return mBufferSizeInFrames; } | |||
/** | |||
* @return capacityInFrames or kUnspecified | |||
*/ | |||
virtual int32_t getBufferCapacityInFrames() const { return mBufferCapacityInFrames; } | |||
/** | |||
* @return the sharing mode of the stream. | |||
*/ | |||
SharingMode getSharingMode() const { return mSharingMode; } | |||
/** | |||
* @return the performance mode of the stream. | |||
*/ | |||
PerformanceMode getPerformanceMode() const { return mPerformanceMode; } | |||
/** | |||
* @return the device ID of the stream. | |||
*/ | |||
int32_t getDeviceId() const { return mDeviceId; } | |||
/** | |||
* @return the callback object for this stream, if set. | |||
*/ | |||
AudioStreamCallback* getCallback() const { | |||
return mStreamCallback; | |||
} | |||
/** | |||
* @return the usage for this stream. | |||
*/ | |||
Usage getUsage() const { return mUsage; } | |||
/** | |||
* @return the stream's content type. | |||
*/ | |||
ContentType getContentType() const { return mContentType; } | |||
/** | |||
* @return the stream's input preset. | |||
*/ | |||
InputPreset getInputPreset() const { return mInputPreset; } | |||
/** | |||
* @return the stream's session ID allocation strategy (None or Allocate). | |||
*/ | |||
SessionId getSessionId() const { return mSessionId; } | |||
/** | |||
* @return true if Oboe can convert channel counts to achieve optimal results. | |||
*/ | |||
bool isChannelConversionAllowed() const { | |||
return mChannelConversionAllowed; | |||
} | |||
/** | |||
* @return true if Oboe can convert data formats to achieve optimal results. | |||
*/ | |||
bool isFormatConversionAllowed() const { | |||
return mFormatConversionAllowed; | |||
} | |||
/** | |||
* @return whether and how Oboe can convert sample rates to achieve optimal results. | |||
*/ | |||
SampleRateConversionQuality getSampleRateConversionQuality() const { | |||
return mSampleRateConversionQuality; | |||
} | |||
protected: | |||
/** The callback which will be fired when new data is ready to be read/written **/ | |||
AudioStreamCallback *mStreamCallback = nullptr; | |||
/** Number of audio frames which will be requested in each callback */ | |||
int32_t mFramesPerCallback = kUnspecified; | |||
/** Stream channel count */ | |||
int32_t mChannelCount = kUnspecified; | |||
/** Stream sample rate */ | |||
int32_t mSampleRate = kUnspecified; | |||
/** Stream audio device ID */ | |||
int32_t mDeviceId = kUnspecified; | |||
/** Stream buffer capacity specified as a number of audio frames */ | |||
int32_t mBufferCapacityInFrames = kUnspecified; | |||
/** Stream buffer size specified as a number of audio frames */ | |||
int32_t mBufferSizeInFrames = kUnspecified; | |||
/** | |||
* Number of frames which will be copied to/from the audio device in a single read/write | |||
* operation | |||
*/ | |||
int32_t mFramesPerBurst = kUnspecified; | |||
/** Stream sharing mode */ | |||
SharingMode mSharingMode = SharingMode::Shared; | |||
/** Format of audio frames */ | |||
AudioFormat mFormat = AudioFormat::Unspecified; | |||
/** Stream direction */ | |||
Direction mDirection = Direction::Output; | |||
/** Stream performance mode */ | |||
PerformanceMode mPerformanceMode = PerformanceMode::None; | |||
/** Stream usage. Only active on Android 28+ */ | |||
Usage mUsage = Usage::Media; | |||
/** Stream content type. Only active on Android 28+ */ | |||
ContentType mContentType = ContentType::Music; | |||
/** Stream input preset. Only active on Android 28+ | |||
* TODO InputPreset::Unspecified should be considered as a possible default alternative. | |||
*/ | |||
InputPreset mInputPreset = InputPreset::VoiceRecognition; | |||
/** Stream session ID allocation strategy. Only active on Android 28+ */ | |||
SessionId mSessionId = SessionId::None; | |||
// Control whether Oboe can convert channel counts to achieve optimal results. | |||
bool mChannelConversionAllowed = false; | |||
// Control whether Oboe can convert data formats to achieve optimal results. | |||
bool mFormatConversionAllowed = false; | |||
// Control whether and how Oboe can convert sample rates to achieve optimal results. | |||
SampleRateConversionQuality mSampleRateConversionQuality = SampleRateConversionQuality::None; | |||
}; | |||
} // namespace oboe | |||
#endif /* OBOE_STREAM_BASE_H_ */ |
@@ -1,424 +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_STREAM_BUILDER_H_ | |||
#define OBOE_STREAM_BUILDER_H_ | |||
#include "oboe/Definitions.h" | |||
#include "oboe/AudioStreamBase.h" | |||
namespace oboe { | |||
// This depends on AudioStream, so we use forward declaration, it will close and delete the stream | |||
struct StreamDeleterFunctor; | |||
using ManagedStream = std::unique_ptr<AudioStream, StreamDeleterFunctor>; | |||
/** | |||
* Factory class for an audio Stream. | |||
*/ | |||
class AudioStreamBuilder : public AudioStreamBase { | |||
public: | |||
AudioStreamBuilder() : AudioStreamBase() {} | |||
AudioStreamBuilder(const AudioStreamBase &audioStreamBase): AudioStreamBase(audioStreamBase) {} | |||
/** | |||
* Request a specific number of channels. | |||
* | |||
* Default is kUnspecified. If the value is unspecified then | |||
* the application should query for the actual value after the stream is opened. | |||
*/ | |||
AudioStreamBuilder *setChannelCount(int channelCount) { | |||
mChannelCount = channelCount; | |||
return this; | |||
} | |||
/** | |||
* Request the direction for a stream. The default is Direction::Output. | |||
* | |||
* @param direction Direction::Output or Direction::Input | |||
*/ | |||
AudioStreamBuilder *setDirection(Direction direction) { | |||
mDirection = direction; | |||
return this; | |||
} | |||
/** | |||
* Request a specific sample rate in Hz. | |||
* | |||
* Default is kUnspecified. 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", | |||
* because it refers to the number of complete frames transferred per second. | |||
* But it is traditionally called "sample rate". Se we use that term. | |||
* | |||
*/ | |||
AudioStreamBuilder *setSampleRate(int32_t sampleRate) { | |||
mSampleRate = sampleRate; | |||
return this; | |||
} | |||
/** | |||
* Request a specific number of frames for the data callback. | |||
* | |||
* Default is kUnspecified. 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 | |||
* leaving this unspecified. This allow the underlying API to optimize | |||
* the callbacks. But if your application is, for example, doing FFTs or other block | |||
* oriented operations, then call this function to get the sizes you need. | |||
* | |||
* @param framesPerCallback | |||
* @return pointer to the builder so calls can be chained | |||
*/ | |||
AudioStreamBuilder *setFramesPerCallback(int framesPerCallback) { | |||
mFramesPerCallback = framesPerCallback; | |||
return this; | |||
} | |||
/** | |||
* Request a sample data format, for example Format::Float. | |||
* | |||
* Default is Format::Unspecified. If the value is unspecified then | |||
* the application should query for the actual value after the stream is opened. | |||
*/ | |||
AudioStreamBuilder *setFormat(AudioFormat format) { | |||
mFormat = format; | |||
return this; | |||
} | |||
/** | |||
* Set the requested buffer capacity in frames. | |||
* BufferCapacityInFrames is the maximum possible BufferSizeInFrames. | |||
* | |||
* The final stream capacity may differ. For AAudio it should be at least this big. | |||
* For OpenSL ES, it could be smaller. | |||
* | |||
* Default is kUnspecified. | |||
* | |||
* @param bufferCapacityInFrames the desired buffer capacity in frames or kUnspecified | |||
* @return pointer to the builder so calls can be chained | |||
*/ | |||
AudioStreamBuilder *setBufferCapacityInFrames(int32_t bufferCapacityInFrames) { | |||
mBufferCapacityInFrames = bufferCapacityInFrames; | |||
return this; | |||
} | |||
/** | |||
* Get the audio API which will be requested when opening the stream. No guarantees that this is | |||
* the API which will actually be used. Query the stream itself to find out the API which is | |||
* being used. | |||
* | |||
* If you do not specify the API, then AAudio will be used if isAAudioRecommended() | |||
* returns true. Otherwise OpenSL ES will be used. | |||
* | |||
* @return the requested audio API | |||
*/ | |||
AudioApi getAudioApi() const { return mAudioApi; } | |||
/** | |||
* If you leave this unspecified then Oboe will choose the best API | |||
* for the device and SDK version at runtime. | |||
* | |||
* This should almost always be left unspecified, except for debugging purposes. | |||
* Specifying AAudio will force Oboe to use AAudio on 8.0, which is extremely risky. | |||
* Specifying OpenSLES should mainly be used to test legacy performance/functionality. | |||
* | |||
* If the caller requests AAudio and it is supported then AAudio will be used. | |||
* | |||
* @param audioApi Must be AudioApi::Unspecified, AudioApi::OpenSLES or AudioApi::AAudio. | |||
* @return pointer to the builder so calls can be chained | |||
*/ | |||
AudioStreamBuilder *setAudioApi(AudioApi audioApi) { | |||
mAudioApi = audioApi; | |||
return this; | |||
} | |||
/** | |||
* Is the AAudio API supported on this device? | |||
* | |||
* AAudio was introduced in the Oreo 8.0 release. | |||
* | |||
* @return true if supported | |||
*/ | |||
static bool isAAudioSupported(); | |||
/** | |||
* Is the AAudio API recommended this device? | |||
* | |||
* AAudio may be supported but not recommended because of version specific issues. | |||
* AAudio is not recommended for Android 8.0 or earlier versions. | |||
* | |||
* @return true if recommended | |||
*/ | |||
static bool isAAudioRecommended(); | |||
/** | |||
* Request a mode for sharing the device. | |||
* 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 | |||
* @return pointer to the builder so calls can be chained | |||
*/ | |||
AudioStreamBuilder *setSharingMode(SharingMode sharingMode) { | |||
mSharingMode = sharingMode; | |||
return this; | |||
} | |||
/** | |||
* Request a performance level for the stream. | |||
* This will determine the latency, the power consumption, and the level of | |||
* protection from glitches. | |||
* | |||
* @param performanceMode for example, PerformanceMode::LowLatency | |||
* @return pointer to the builder so calls can be chained | |||
*/ | |||
AudioStreamBuilder *setPerformanceMode(PerformanceMode performanceMode) { | |||
mPerformanceMode = performanceMode; | |||
return this; | |||
} | |||
/** | |||
* Set the intended use case for the stream. | |||
* | |||
* The system will use this information to optimize the behavior of the stream. | |||
* This could, for example, affect how volume and focus is handled for the stream. | |||
* | |||
* The default, if you do not call this function, is Usage::Media. | |||
* | |||
* Added in API level 28. | |||
* | |||
* @param usage the desired usage, eg. Usage::Game | |||
*/ | |||
AudioStreamBuilder *setUsage(Usage usage) { | |||
mUsage = usage; | |||
return this; | |||
} | |||
/** | |||
* Set the type of audio data that the stream will carry. | |||
* | |||
* The system will use this information to optimize the behavior of the stream. | |||
* This could, for example, affect whether a stream is paused when a notification occurs. | |||
* | |||
* The default, if you do not call this function, is ContentType::Music. | |||
* | |||
* Added in API level 28. | |||
* | |||
* @param contentType the type of audio data, eg. ContentType::Speech | |||
*/ | |||
AudioStreamBuilder *setContentType(ContentType contentType) { | |||
mContentType = contentType; | |||
return this; | |||
} | |||
/** | |||
* Set the input (capture) preset for the stream. | |||
* | |||
* The system will use this information to optimize the behavior of the stream. | |||
* This could, for example, affect which microphones are used and how the | |||
* recorded data is processed. | |||
* | |||
* The default, if you do not call this function, is InputPreset::VoiceRecognition. | |||
* That is because VoiceRecognition is the preset with the lowest latency | |||
* on many platforms. | |||
* | |||
* Added in API level 28. | |||
* | |||
* @param inputPreset the desired configuration for recording | |||
*/ | |||
AudioStreamBuilder *setInputPreset(InputPreset inputPreset) { | |||
mInputPreset = inputPreset; | |||
return this; | |||
} | |||
/** Set the requested session ID. | |||
* | |||
* The session ID can be used to associate a stream with effects processors. | |||
* The effects are controlled using the Android AudioEffect Java API. | |||
* | |||
* The default, if you do not call this function, is SessionId::None. | |||
* | |||
* If set to SessionId::Allocate then a session ID will be allocated | |||
* when the stream is opened. | |||
* | |||
* The allocated session ID can be obtained by calling AudioStream::getSessionId() | |||
* and then used with this function when opening another stream. | |||
* This allows effects to be shared between streams. | |||
* | |||
* Session IDs from Oboe can be used the Android Java APIs and vice versa. | |||
* So a session ID from an Oboe stream can be passed to Java | |||
* and effects applied using the Java AudioEffect API. | |||
* | |||
* Allocated session IDs will always be positive and nonzero. | |||
* | |||
* Added in API level 28. | |||
* | |||
* @param sessionId an allocated sessionID or SessionId::Allocate | |||
*/ | |||
AudioStreamBuilder *setSessionId(SessionId sessionId) { | |||
mSessionId = sessionId; | |||
return this; | |||
} | |||
/** | |||
* Request a stream to a specific audio input/output device given an audio device ID. | |||
* | |||
* In most cases, the primary device will be the appropriate device to use, and the | |||
* deviceId can be left kUnspecified. | |||
* | |||
* On Android, for example, the ID could be obtained from the Java AudioManager. | |||
* AudioManager.getDevices() returns an array of AudioDeviceInfo[], which contains | |||
* a getId() method (as well as other type information), that should be passed | |||
* to this method. | |||
* | |||
* | |||
* Note that when using OpenSL ES, this will be ignored and the created | |||
* stream will have deviceId kUnspecified. | |||
* | |||
* @param deviceId device identifier or kUnspecified | |||
* @return pointer to the builder so calls can be chained | |||
*/ | |||
AudioStreamBuilder *setDeviceId(int32_t deviceId) { | |||
mDeviceId = deviceId; | |||
return this; | |||
} | |||
/** | |||
* Specifies an object to handle data or error related callbacks from the underlying API. | |||
* | |||
* <strong>Important: See AudioStreamCallback for restrictions on what may be called | |||
* from the callback methods.</strong> | |||
* | |||
* 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) { | |||
mStreamCallback = streamCallback; | |||
return this; | |||
} | |||
/** | |||
* If true then Oboe might convert channel counts to achieve optimal results. | |||
* On some versions of Android for example, stereo streams could not use a FAST track. | |||
* So a mono stream might be used instead and duplicated to two channels. | |||
* On some devices, mono streams might be broken, so a stereo stream might be opened | |||
* and converted to mono. | |||
* | |||
* Default is true. | |||
*/ | |||
AudioStreamBuilder *setChannelConversionAllowed(bool allowed) { | |||
mChannelConversionAllowed = allowed; | |||
return this; | |||
} | |||
/** | |||
* If true then Oboe might convert data formats to achieve optimal results. | |||
* On some versions of Android, for example, a float stream could not get a | |||
* low latency data path. So an I16 stream might be opened and converted to float. | |||
* | |||
* Default is true. | |||
*/ | |||
AudioStreamBuilder *setFormatConversionAllowed(bool allowed) { | |||
mFormatConversionAllowed = allowed; | |||
return this; | |||
} | |||
/** | |||
* Specify the quality of the sample rate converter in Oboe. | |||
* | |||
* If set to None then Oboe will not do sample rate conversion. But the underlying APIs might | |||
* still do sample rate conversion if you specify a sample rate. | |||
* That can prevent you from getting a low latency stream. | |||
* | |||
* If you do the conversion in Oboe then you might still get a low latency stream. | |||
* | |||
* Default is SampleRateConversionQuality::None | |||
*/ | |||
AudioStreamBuilder *setSampleRateConversionQuality(SampleRateConversionQuality quality) { | |||
mSampleRateConversionQuality = quality; | |||
return this; | |||
} | |||
/** | |||
* @return true if AAudio will be used based on the current settings. | |||
*/ | |||
bool willUseAAudio() const { | |||
return (mAudioApi == AudioApi::AAudio && isAAudioSupported()) | |||
|| (mAudioApi == AudioApi::Unspecified && isAAudioRecommended()); | |||
} | |||
/** | |||
* Create and open a stream object based on the current settings. | |||
* | |||
* The caller owns the pointer to the AudioStream object. | |||
* | |||
* @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); | |||
/** | |||
* Create and open a ManagedStream object based on the current builder state. | |||
* | |||
* The caller must create a unique ptr, and pass by reference so it can be | |||
* modified to point to an opened stream. The caller owns the unique ptr, | |||
* and it will be automatically closed and deleted when going out of scope. | |||
* @param stream Reference to the ManagedStream (uniqueptr) used to keep track of stream | |||
* @return OBOE_OK if successful or a negative error code. | |||
*/ | |||
Result openManagedStream(ManagedStream &stream); | |||
private: | |||
/** | |||
* @param other | |||
* @return true if channels, format and sample rate match | |||
*/ | |||
bool isCompatible(AudioStreamBase &other); | |||
/** | |||
* Create an AudioStream object. The AudioStream must be opened before use. | |||
* | |||
* The caller owns the pointer. | |||
* | |||
* @return pointer to an AudioStream object or nullptr. | |||
*/ | |||
oboe::AudioStream *build(); | |||
AudioApi mAudioApi = AudioApi::Unspecified; | |||
}; | |||
} // namespace oboe | |||
#endif /* OBOE_STREAM_BUILDER_H_ */ |
@@ -1,123 +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; | |||
/** | |||
* AudioStreamCallback defines a callback interface for: | |||
* | |||
* 1) moving data to/from an audio stream using `onAudioReady` | |||
* 2) being alerted when a stream has an error using `onError*` methods | |||
* | |||
*/ | |||
class AudioStreamCallback { | |||
public: | |||
virtual ~AudioStreamCallback() = default; | |||
/** | |||
* A buffer is ready for processing. | |||
* | |||
* For an output stream, this function should render and write numFrames of data | |||
* in the stream's current data format to the audioData buffer. | |||
* | |||
* For an input stream, this function should read and process numFrames of data | |||
* from the audioData buffer. | |||
* | |||
* The audio data is passed through the buffer. So do NOT call read() or | |||
* write() on the stream that is making the callback. | |||
* | |||
* Note that numFrames can vary unless AudioStreamBuilder::setFramesPerCallback() | |||
* is called. | |||
* | |||
* Also note that this callback function should be considered a "real-time" function. | |||
* It must not do anything that could cause an unbounded delay because that can cause the | |||
* audio to glitch or pop. | |||
* | |||
* These are things the function should NOT do: | |||
* <ul> | |||
* <li>allocate memory using, for example, malloc() or new</li> | |||
* <li>any file operations such as opening, closing, reading or writing</li> | |||
* <li>any network operations such as streaming</li> | |||
* <li>use any mutexes or other synchronization primitives</li> | |||
* <li>sleep</li> | |||
* <li>oboeStream->stop(), pause(), flush() or close()</li> | |||
* <li>oboeStream->read()</li> | |||
* <li>oboeStream->write()</li> | |||
* </ul> | |||
* | |||
* The following are OK to call from the data callback: | |||
* <ul> | |||
* <li>oboeStream->get*()</li> | |||
* <li>oboe::convertToText()</li> | |||
* <li>oboeStream->setBufferSizeInFrames()</li> | |||
* </ul> | |||
* | |||
* If you need to move data, eg. MIDI commands, in or out of the callback function then | |||
* we recommend the use of non-blocking techniques such as an atomic FIFO. | |||
* | |||
* @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 disconnected. | |||
* | |||
* Note that this will be called on a different thread than the onAudioReady() thread. | |||
* This thread will be created by Oboe. | |||
* | |||
* The underlying stream will already be stopped by Oboe but not yet closed. | |||
* So the stream can be queried. | |||
* | |||
* Do not close or delete the stream in this method because it will be | |||
* closed after this method returns. | |||
* | |||
* @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 AAudio or OpenSL ES stream will already be stopped AND closed by Oboe. | |||
* So the underlying stream cannot be referenced. | |||
* But you can still query most parameters. | |||
* | |||
* This callback could be used to reopen a new stream on another device. | |||
* You can safely delete the old AudioStream in this method. | |||
* | |||
* @param oboeStream pointer to the associated stream | |||
* @param error | |||
*/ | |||
virtual void onErrorAfterClose(AudioStream* /* oboeStream */, Result /* error */) {} | |||
}; | |||
} // namespace oboe | |||
#endif //OBOE_STREAM_CALLBACK_H |
@@ -1,500 +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 <cstdint> | |||
#include <type_traits> | |||
// Oboe needs to be able to build on old NDKs so we use hard coded constants. | |||
// The correctness of these constants is verified in "aaudio/AAudioLoader.cpp". | |||
namespace oboe { | |||
/** | |||
* Represents any attribute, property or value which hasn't been specified. | |||
*/ | |||
constexpr int32_t kUnspecified = 0; | |||
// TODO: Investigate using std::chrono | |||
/** | |||
* The number of nanoseconds in a microsecond. 1,000. | |||
*/ | |||
constexpr int64_t kNanosPerMicrosecond = 1000; | |||
/** | |||
* The number of nanoseconds in a millisecond. 1,000,000. | |||
*/ | |||
constexpr int64_t kNanosPerMillisecond = kNanosPerMicrosecond * 1000; | |||
/** | |||
* The number of milliseconds in a second. 1,000. | |||
*/ | |||
constexpr int64_t kMillisPerSecond = 1000; | |||
/** | |||
* The number of nanoseconds in a second. 1,000,000,000. | |||
*/ | |||
constexpr int64_t kNanosPerSecond = kNanosPerMillisecond * kMillisPerSecond; | |||
/** | |||
* The state of the audio stream. | |||
*/ | |||
enum class StreamState : int32_t { // aaudio_stream_state_t | |||
Uninitialized = 0, // AAUDIO_STREAM_STATE_UNINITIALIZED, | |||
Unknown = 1, // AAUDIO_STREAM_STATE_UNKNOWN, | |||
Open = 2, // AAUDIO_STREAM_STATE_OPEN, | |||
Starting = 3, // AAUDIO_STREAM_STATE_STARTING, | |||
Started = 4, // AAUDIO_STREAM_STATE_STARTED, | |||
Pausing = 5, // AAUDIO_STREAM_STATE_PAUSING, | |||
Paused = 6, // AAUDIO_STREAM_STATE_PAUSED, | |||
Flushing = 7, // AAUDIO_STREAM_STATE_FLUSHING, | |||
Flushed = 8, // AAUDIO_STREAM_STATE_FLUSHED, | |||
Stopping = 9, // AAUDIO_STREAM_STATE_STOPPING, | |||
Stopped = 10, // AAUDIO_STREAM_STATE_STOPPED, | |||
Closing = 11, // AAUDIO_STREAM_STATE_CLOSING, | |||
Closed = 12, // AAUDIO_STREAM_STATE_CLOSED, | |||
Disconnected = 13, // AAUDIO_STREAM_STATE_DISCONNECTED, | |||
}; | |||
/** | |||
* The direction of the stream. | |||
*/ | |||
enum class Direction : int32_t { // aaudio_direction_t | |||
/** | |||
* Used for playback. | |||
*/ | |||
Output = 0, // AAUDIO_DIRECTION_OUTPUT, | |||
/** | |||
* Used for recording. | |||
*/ | |||
Input = 1, // AAUDIO_DIRECTION_INPUT, | |||
}; | |||
/** | |||
* The format of audio samples. | |||
*/ | |||
enum class AudioFormat : int32_t { // aaudio_format_t | |||
/** | |||
* Invalid format. | |||
*/ | |||
Invalid = -1, // AAUDIO_FORMAT_INVALID, | |||
/** | |||
* Unspecified format. Format will be decided by Oboe. | |||
*/ | |||
Unspecified = 0, // AAUDIO_FORMAT_UNSPECIFIED, | |||
/** | |||
* Signed 16-bit integers. | |||
*/ | |||
I16 = 1, // AAUDIO_FORMAT_PCM_I16, | |||
/** | |||
* Single precision floating points. | |||
*/ | |||
Float = 2, // AAUDIO_FORMAT_PCM_FLOAT, | |||
}; | |||
/** | |||
* The result of an audio callback. | |||
*/ | |||
enum class DataCallbackResult : int32_t { // aaudio_data_callback_result_t | |||
// Indicates to the caller that the callbacks should continue. | |||
Continue = 0, // AAUDIO_CALLBACK_RESULT_CONTINUE, | |||
// Indicates to the caller that the callbacks should stop immediately. | |||
Stop = 1, // AAUDIO_CALLBACK_RESULT_STOP, | |||
}; | |||
/** | |||
* The result of an operation. All except the `OK` result indicates that an error occurred. | |||
* The `Result` can be converted into a human readable string using `convertToText`. | |||
*/ | |||
enum class Result : int32_t { // aaudio_result_t | |||
OK = 0, // AAUDIO_OK | |||
ErrorBase = -900, // AAUDIO_ERROR_BASE, | |||
ErrorDisconnected = -899, // AAUDIO_ERROR_DISCONNECTED, | |||
ErrorIllegalArgument = -898, // AAUDIO_ERROR_ILLEGAL_ARGUMENT, | |||
ErrorInternal = -896, // AAUDIO_ERROR_INTERNAL, | |||
ErrorInvalidState = -895, // AAUDIO_ERROR_INVALID_STATE, | |||
ErrorInvalidHandle = -892, // AAUDIO_ERROR_INVALID_HANDLE, | |||
ErrorUnimplemented = -890, // AAUDIO_ERROR_UNIMPLEMENTED, | |||
ErrorUnavailable = -889, // AAUDIO_ERROR_UNAVAILABLE, | |||
ErrorNoFreeHandles = -888, // AAUDIO_ERROR_NO_FREE_HANDLES, | |||
ErrorNoMemory = -887, // AAUDIO_ERROR_NO_MEMORY, | |||
ErrorNull = -886, // AAUDIO_ERROR_NULL, | |||
ErrorTimeout = -885, // AAUDIO_ERROR_TIMEOUT, | |||
ErrorWouldBlock = -884, // AAUDIO_ERROR_WOULD_BLOCK, | |||
ErrorInvalidFormat = -883, // AAUDIO_ERROR_INVALID_FORMAT, | |||
ErrorOutOfRange = -882, // AAUDIO_ERROR_OUT_OF_RANGE, | |||
ErrorNoService = -881, // AAUDIO_ERROR_NO_SERVICE, | |||
ErrorInvalidRate = -880, // AAUDIO_ERROR_INVALID_RATE, | |||
// Reserved for future AAudio result types | |||
Reserved1, | |||
Reserved2, | |||
Reserved3, | |||
Reserved4, | |||
Reserved5, | |||
Reserved6, | |||
Reserved7, | |||
Reserved8, | |||
Reserved9, | |||
Reserved10, | |||
ErrorClosed, | |||
}; | |||
/** | |||
* The sharing mode of the audio stream. | |||
*/ | |||
enum class SharingMode : int32_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. | |||
* | |||
* If you do not need the lowest possible latency then we recommend using Shared, | |||
* which is the default. | |||
*/ | |||
Exclusive = 0, // AAUDIO_SHARING_MODE_EXCLUSIVE, | |||
/** | |||
* Multiple applications can share the same device. | |||
* The data from output streams will be mixed by the audio service. | |||
* The data for input streams will be distributed by the audio service. | |||
* | |||
* This will have higher latency than the EXCLUSIVE mode. | |||
*/ | |||
Shared = 1, // AAUDIO_SHARING_MODE_SHARED, | |||
}; | |||
/** | |||
* The performance mode of the audio stream. | |||
*/ | |||
enum class PerformanceMode : int32_t { // aaudio_performance_mode_t | |||
/** | |||
* No particular performance needs. Default. | |||
*/ | |||
None = 10, // AAUDIO_PERFORMANCE_MODE_NONE, | |||
/** | |||
* Extending battery life is most important. | |||
*/ | |||
PowerSaving = 11, // AAUDIO_PERFORMANCE_MODE_POWER_SAVING, | |||
/** | |||
* Reducing latency is most important. | |||
*/ | |||
LowLatency = 12, // AAUDIO_PERFORMANCE_MODE_LOW_LATENCY | |||
}; | |||
/** | |||
* The underlying audio API used by the audio stream. | |||
*/ | |||
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 | |||
}; | |||
/** | |||
* Specifies the quality of the sample rate conversion performed by Oboe. | |||
* Higher quality will require more CPU load. | |||
* Higher quality conversion will probably be implemented using a sinc based resampler. | |||
*/ | |||
enum class SampleRateConversionQuality : int32_t { | |||
/** | |||
* No conversion by Oboe. Underlying APIs may still do conversion. | |||
*/ | |||
None, | |||
/** | |||
* Fastest conversion but may not sound great. | |||
* This may be implemented using bilinear interpolation. | |||
*/ | |||
Fastest, | |||
Low, | |||
Medium, | |||
High, | |||
/** | |||
* Highest quality conversion, which may be expensive in terms of CPU. | |||
*/ | |||
Best, | |||
}; | |||
/** | |||
* The Usage attribute expresses *why* you are playing a sound, what is this sound used for. | |||
* This information is used by certain platforms or routing policies | |||
* to make more refined volume or routing decisions. | |||
* | |||
* Note that these match the equivalent values in AudioAttributes in the Android Java API. | |||
* | |||
* This attribute only has an effect on Android API 28+. | |||
*/ | |||
enum class Usage : int32_t { // aaudio_usage_t | |||
/** | |||
* Use this for streaming media, music performance, video, podcasts, etcetera. | |||
*/ | |||
Media = 1, // AAUDIO_USAGE_MEDIA | |||
/** | |||
* Use this for voice over IP, telephony, etcetera. | |||
*/ | |||
VoiceCommunication = 2, // AAUDIO_USAGE_VOICE_COMMUNICATION | |||
/** | |||
* Use this for sounds associated with telephony such as busy tones, DTMF, etcetera. | |||
*/ | |||
VoiceCommunicationSignalling = 3, // AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING | |||
/** | |||
* Use this to demand the users attention. | |||
*/ | |||
Alarm = 4, // AAUDIO_USAGE_ALARM | |||
/** | |||
* Use this for notifying the user when a message has arrived or some | |||
* other background event has occured. | |||
*/ | |||
Notification = 5, // AAUDIO_USAGE_NOTIFICATION | |||
/** | |||
* Use this when the phone rings. | |||
*/ | |||
NotificationRingtone = 6, // AAUDIO_USAGE_NOTIFICATION_RINGTONE | |||
/** | |||
* Use this to attract the users attention when, for example, the battery is low. | |||
*/ | |||
NotificationEvent = 10, // AAUDIO_USAGE_NOTIFICATION_EVENT | |||
/** | |||
* Use this for screen readers, etcetera. | |||
*/ | |||
AssistanceAccessibility = 11, // AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY | |||
/** | |||
* Use this for driving or navigation directions. | |||
*/ | |||
AssistanceNavigationGuidance = 12, // AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE | |||
/** | |||
* Use this for user interface sounds, beeps, etcetera. | |||
*/ | |||
AssistanceSonification = 13, // AAUDIO_USAGE_ASSISTANCE_SONIFICATION | |||
/** | |||
* Use this for game audio and sound effects. | |||
*/ | |||
Game = 14, // AAUDIO_USAGE_GAME | |||
/** | |||
* Use this for audio responses to user queries, audio instructions or help utterances. | |||
*/ | |||
Assistant = 16, // AAUDIO_USAGE_ASSISTANT | |||
}; | |||
/** | |||
* The ContentType attribute describes *what* you are playing. | |||
* It expresses the general category of the content. This information is optional. | |||
* But in case it is known (for instance {@link Movie} for a | |||
* movie streaming service or {@link Speech} for | |||
* an audio book application) this information might be used by the audio framework to | |||
* enforce audio focus. | |||
* | |||
* Note that these match the equivalent values in AudioAttributes in the Android Java API. | |||
* | |||
* This attribute only has an effect on Android API 28+. | |||
*/ | |||
enum ContentType : int32_t { // aaudio_content_type_t | |||
/** | |||
* Use this for spoken voice, audio books, etcetera. | |||
*/ | |||
Speech = 1, // AAUDIO_CONTENT_TYPE_SPEECH | |||
/** | |||
* Use this for pre-recorded or live music. | |||
*/ | |||
Music = 2, // AAUDIO_CONTENT_TYPE_MUSIC | |||
/** | |||
* Use this for a movie or video soundtrack. | |||
*/ | |||
Movie = 3, // AAUDIO_CONTENT_TYPE_MOVIE | |||
/** | |||
* Use this for sound is designed to accompany a user action, | |||
* such as a click or beep sound made when the user presses a button. | |||
*/ | |||
Sonification = 4, // AAUDIO_CONTENT_TYPE_SONIFICATION | |||
}; | |||
/** | |||
* Defines the audio source. | |||
* An audio source defines both a default physical source of audio signal, and a recording | |||
* configuration. | |||
* | |||
* Note that these match the equivalent values in MediaRecorder.AudioSource in the Android Java API. | |||
* | |||
* This attribute only has an effect on Android API 28+. | |||
*/ | |||
enum InputPreset : int32_t { // aaudio_input_preset_t | |||
/** | |||
* Use this preset when other presets do not apply. | |||
*/ | |||
Generic = 1, // AAUDIO_INPUT_PRESET_GENERIC | |||
/** | |||
* Use this preset when recording video. | |||
*/ | |||
Camcorder = 5, // AAUDIO_INPUT_PRESET_CAMCORDER | |||
/** | |||
* Use this preset when doing speech recognition. | |||
*/ | |||
VoiceRecognition = 6, // AAUDIO_INPUT_PRESET_VOICE_RECOGNITION | |||
/** | |||
* Use this preset when doing telephony or voice messaging. | |||
*/ | |||
VoiceCommunication = 7, // AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION | |||
/** | |||
* Use this preset to obtain an input with no effects. | |||
* Note that this input will not have automatic gain control | |||
* so the recorded volume may be very low. | |||
*/ | |||
Unprocessed = 9, // AAUDIO_INPUT_PRESET_UNPROCESSED | |||
/** | |||
* Use this preset for capturing audio meant to be processed in real time | |||
* and played back for live performance (e.g karaoke). | |||
* The capture path will minimize latency and coupling with playback path. | |||
*/ | |||
VoicePerformance = 10, // AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE | |||
}; | |||
/** | |||
* This attribute can be used to allocate a session ID to the audio stream. | |||
* | |||
* This attribute only has an effect on Android API 28+. | |||
*/ | |||
enum SessionId { | |||
/** | |||
* Do not allocate a session ID. | |||
* Effects cannot be used with this stream. | |||
* Default. | |||
*/ | |||
None = -1, // AAUDIO_SESSION_ID_NONE | |||
/** | |||
* Allocate a session ID that can be used to attach and control | |||
* effects using the Java AudioEffects API. | |||
* Note that the use of this flag may result in higher latency. | |||
* | |||
* Note that this matches the value of AudioManager.AUDIO_SESSION_ID_GENERATE. | |||
*/ | |||
Allocate = 0, // AAUDIO_SESSION_ID_ALLOCATE | |||
}; | |||
/** | |||
* The channel count of the audio stream. The underlying type is `int32_t`. | |||
* Use of this enum is convenient to avoid "magic" | |||
* numbers when specifying the channel count. | |||
* | |||
* For example, you can write | |||
* `builder.setChannelCount(ChannelCount::Stereo)` | |||
* rather than `builder.setChannelCount(2)` | |||
* | |||
*/ | |||
enum ChannelCount : int32_t { | |||
/** | |||
* Audio channel count definition, use Mono or Stereo | |||
*/ | |||
Unspecified = kUnspecified, | |||
/** | |||
* Use this for mono audio | |||
*/ | |||
Mono = 1, | |||
/** | |||
* Use this for stereo audio. | |||
*/ | |||
Stereo = 2, | |||
}; | |||
/** | |||
* On API 16 to 26 OpenSL ES will be used. When using OpenSL ES the optimal values for sampleRate and | |||
* framesPerBurst are not known by the native code. | |||
* On API 17+ these values should be obtained from the AudioManager using this code: | |||
* | |||
* <pre><code> | |||
* // Note that this technique only works for built-in speakers and headphones. | |||
* AudioManager myAudioMgr = (AudioManager) 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); | |||
* </code></pre> | |||
* | |||
* It can then be passed down to Oboe through JNI. | |||
* | |||
* AAudio will get the optimal framesPerBurst from the HAL and will ignore this value. | |||
*/ | |||
class DefaultStreamValues { | |||
public: | |||
/** The default sample rate to use when opening new audio streams */ | |||
static int32_t SampleRate; | |||
/** The default frames per burst to use when opening new audio streams */ | |||
static int32_t FramesPerBurst; | |||
/** The default channel count to use when opening new audio streams */ | |||
static int32_t ChannelCount; | |||
}; | |||
/** | |||
* The time at which the frame at `position` was presented | |||
*/ | |||
struct FrameTimestamp { | |||
int64_t position; // in frames | |||
int64_t timestamp; // in nanoseconds | |||
}; | |||
} // namespace oboe | |||
#endif // OBOE_DEFINITIONS_H |
@@ -1,118 +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_LATENCY_TUNER_ | |||
#define OBOE_LATENCY_TUNER_ | |||
#include <atomic> | |||
#include <cstdint> | |||
#include "oboe/Definitions.h" | |||
#include "oboe/AudioStream.h" | |||
namespace oboe { | |||
/** | |||
* LatencyTuner can be used to dynamically tune the latency of an output stream. | |||
* It adjusts the stream's bufferSize by monitoring the number of underruns. | |||
* | |||
* This only affects the latency associated with the first level of buffering that is closest | |||
* to the application. It does not affect low latency in the HAL, or touch latency in the UI. | |||
* | |||
* Call tune() right before returning from your data callback function if using callbacks. | |||
* Call tune() right before calling write() if using blocking writes. | |||
* | |||
* If you want to see the ongoing results of this tuning process then call | |||
* stream->getBufferSize() periodically. | |||
* | |||
*/ | |||
class LatencyTuner { | |||
public: | |||
/** | |||
* Construct a new LatencyTuner object which will act on the given audio stream | |||
* | |||
* @param stream the stream who's latency will be tuned | |||
*/ | |||
explicit LatencyTuner(AudioStream &stream); | |||
/** | |||
* Construct a new LatencyTuner object which will act on the given audio stream. | |||
* | |||
* @param stream the stream who's latency will be tuned | |||
* @param the maximum buffer size which the tune() operation will set the buffer size to | |||
*/ | |||
explicit LatencyTuner(AudioStream &stream, int32_t maximumBufferSize); | |||
/** | |||
* Adjust the bufferSizeInFrames to optimize latency. | |||
* It will start with a low latency and then raise it if an underrun occurs. | |||
* | |||
* Latency tuning is only supported for AAudio. | |||
* | |||
* @return OK or negative error, ErrorUnimplemented for OpenSL ES | |||
*/ | |||
Result tune(); | |||
/** | |||
* This may be called from another thread. Then tune() will call reset(), | |||
* which will lower the latency to the minimum and then allow it to rise back up | |||
* if there are glitches. | |||
* | |||
* This is typically called in response to a user decision to minimize latency. In other words, | |||
* call this from a button handler. | |||
*/ | |||
void requestReset(); | |||
/** | |||
* @return true if the audio stream's buffer size is at the maximum value. If no maximum value | |||
* was specified when constructing the LatencyTuner then the value of | |||
* stream->getBufferCapacityInFrames is used | |||
*/ | |||
bool isAtMaximumBufferSize(); | |||
private: | |||
/** | |||
* Drop the latency down to the minimum and then let it rise back up. | |||
* This is useful if a glitch caused the latency to increase and it hasn't gone back down. | |||
* | |||
* This should only be called in the same thread as tune(). | |||
*/ | |||
void reset(); | |||
enum class State { | |||
Idle, | |||
Active, | |||
AtMax, | |||
Unsupported | |||
} ; | |||
// arbitrary number of calls to wait before bumping up the latency | |||
static constexpr int32_t kIdleCount = 8; | |||
AudioStream &mStream; | |||
State mState = State::Idle; | |||
int32_t mMaxBufferSize = 0; | |||
int32_t mPreviousXRuns = 0; | |||
int32_t mIdleCountDown = 0; | |||
std::atomic<int32_t> mLatencyTriggerRequests{0}; // TODO user atomic requester from AAudio | |||
std::atomic<int32_t> mLatencyTriggerResponses{0}; | |||
}; | |||
} // namespace oboe | |||
#endif // OBOE_LATENCY_TUNER_ |
@@ -1,37 +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_OBOE_H | |||
#define OBOE_OBOE_H | |||
/** | |||
* \mainpage API reference | |||
* | |||
* All documentation is found in the <a href="namespaceoboe.html">oboe namespace section</a> | |||
* | |||
*/ | |||
#include "oboe/Definitions.h" | |||
#include "oboe/ResultWithValue.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/StabilizedCallback.h" | |||
#endif //OBOE_OBOE_H |
@@ -1,155 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
#ifndef OBOE_RESULT_WITH_VALUE_H | |||
#define OBOE_RESULT_WITH_VALUE_H | |||
#include "oboe/Definitions.h" | |||
#include <iostream> | |||
#include <sstream> | |||
namespace oboe { | |||
/** | |||
* A ResultWithValue can store both the result of an operation (either OK or an error) and a value. | |||
* | |||
* It has been designed for cases where the caller needs to know whether an operation succeeded and, | |||
* if it did, a value which was obtained during the operation. | |||
* | |||
* For example, when reading from a stream the caller needs to know the result of the read operation | |||
* and, if it was successful, how many frames were read. Note that ResultWithValue can be evaluated | |||
* as a boolean so it's simple to check whether the result is OK. | |||
* | |||
* <code> | |||
* ResultWithValue<int32_t> resultOfRead = myStream.read(&buffer, numFrames, timeoutNanoseconds); | |||
* | |||
* if (resultOfRead) { | |||
* LOGD("Frames read: %d", resultOfRead.value()); | |||
* } else { | |||
* LOGD("Error reading from stream: %s", resultOfRead.error()); | |||
* } | |||
* </code> | |||
*/ | |||
template <typename T> | |||
class ResultWithValue { | |||
public: | |||
/** | |||
* Construct a ResultWithValue containing an error result. | |||
* | |||
* @param error The error | |||
*/ | |||
ResultWithValue(oboe::Result error) | |||
: mValue{} | |||
, mError(error) {} | |||
/** | |||
* Construct a ResultWithValue containing an OK result and a value. | |||
* | |||
* @param value the value to store | |||
*/ | |||
explicit ResultWithValue(T value) | |||
: mValue(value) | |||
, mError(oboe::Result::OK) {} | |||
/** | |||
* Get the result. | |||
* | |||
* @return the result | |||
*/ | |||
oboe::Result error() const { | |||
return mError; | |||
} | |||
/** | |||
* Get the value | |||
* @return | |||
*/ | |||
T value() const { | |||
return mValue; | |||
} | |||
/** | |||
* @return true if OK | |||
*/ | |||
explicit operator bool() const { return mError == oboe::Result::OK; } | |||
/** | |||
* Quick way to check for an error. | |||
* | |||
* The caller could write something like this: | |||
* <code> | |||
* if (!result) { printf("Got error %s\n", convertToText(result.error())); } | |||
* </code> | |||
* | |||
* @return true if an error occurred | |||
*/ | |||
bool operator !() const { return mError != oboe::Result::OK; } | |||
/** | |||
* Implicitly convert to a Result. This enables easy comparison with Result values. Example: | |||
* | |||
* <code> | |||
* ResultWithValue result = openStream(); | |||
* if (result == Result::ErrorNoMemory){ // tell user they're out of memory } | |||
* </code> | |||
*/ | |||
operator Result() const { | |||
return mError; | |||
} | |||
/** | |||
* Create a ResultWithValue from a number. If the number is positive the ResultWithValue will | |||
* have a result of Result::OK and the value will contain the number. If the number is negative | |||
* the result will be obtained from the negative number (numeric error codes can be found in | |||
* AAudio.h) and the value will be null. | |||
* | |||
*/ | |||
static ResultWithValue<T> createBasedOnSign(T numericResult){ | |||
// Ensure that the type is either an integer or float | |||
static_assert(std::is_arithmetic<T>::value, | |||
"createBasedOnSign can only be called for numeric types (int or float)"); | |||
if (numericResult >= 0){ | |||
return ResultWithValue<T>(numericResult); | |||
} else { | |||
return ResultWithValue<T>(static_cast<Result>(numericResult)); | |||
} | |||
} | |||
private: | |||
const T mValue; | |||
const oboe::Result mError; | |||
}; | |||
/** | |||
* If the result is `OK` then return the value, otherwise return a human-readable error message. | |||
*/ | |||
template <typename T> | |||
std::ostream& operator<<(std::ostream &strm, const ResultWithValue<T> &result) { | |||
if (!result) { | |||
strm << convertToText(result.error()); | |||
} else { | |||
strm << result.value(); | |||
} | |||
return strm; | |||
} | |||
} // namespace oboe | |||
#endif //OBOE_RESULT_WITH_VALUE_H |
@@ -1,75 +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. | |||
*/ | |||
#ifndef OBOE_STABILIZEDCALLBACK_H | |||
#define OBOE_STABILIZEDCALLBACK_H | |||
#include <cstdint> | |||
#include "oboe/AudioStream.h" | |||
namespace oboe { | |||
class StabilizedCallback : public AudioStreamCallback { | |||
public: | |||
explicit StabilizedCallback(AudioStreamCallback *callback); | |||
DataCallbackResult | |||
onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numFrames) override; | |||
void onErrorBeforeClose(AudioStream *oboeStream, Result error) override { | |||
return mCallback->onErrorBeforeClose(oboeStream, error); | |||
} | |||
void onErrorAfterClose(AudioStream *oboeStream, Result error) override { | |||
// Reset all fields now that the stream has been closed | |||
mFrameCount = 0; | |||
mEpochTimeNanos = 0; | |||
mOpsPerNano = 1; | |||
return mCallback->onErrorAfterClose(oboeStream, error); | |||
} | |||
private: | |||
AudioStreamCallback *mCallback = nullptr; | |||
int64_t mFrameCount = 0; | |||
int64_t mEpochTimeNanos = 0; | |||
double mOpsPerNano = 1; | |||
void generateLoad(int64_t durationNanos); | |||
}; | |||
/** | |||
* cpu_relax is an architecture specific method of telling the CPU that you don't want it to | |||
* do much work. asm volatile keeps the compiler from optimising these instructions out. | |||
*/ | |||
#if defined(__i386__) || defined(__x86_64__) | |||
#define cpu_relax() asm volatile("rep; nop" ::: "memory"); | |||
#elif defined(__arm__) || defined(__mips__) | |||
#define cpu_relax() asm volatile("":::"memory") | |||
#elif defined(__aarch64__) | |||
#define cpu_relax() asm volatile("yield" ::: "memory") | |||
#else | |||
#error "cpu_relax is not defined for this architecture" | |||
#endif | |||
} | |||
#endif //OBOE_STABILIZEDCALLBACK_H |
@@ -1,73 +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 <unistd.h> | |||
#include <sys/types.h> | |||
#include "oboe/Definitions.h" | |||
namespace oboe { | |||
/** | |||
* Convert an array of floats to an array of 16-bit integers. | |||
* | |||
* @param source the input array. | |||
* @param destination the output array. | |||
* @param numSamples the number of values to convert. | |||
*/ | |||
void convertFloatToPcm16(const float *source, int16_t *destination, int32_t numSamples); | |||
/** | |||
* Convert an array of 16-bit integers to an array of floats. | |||
* | |||
* @param source the input array. | |||
* @param destination the output array. | |||
* @param numSamples the number of values to convert. | |||
*/ | |||
void convertPcm16ToFloat(const int16_t *source, float *destination, int32_t numSamples); | |||
/** | |||
* @return the size of a sample of the given format in bytes or 0 if format is invalid | |||
*/ | |||
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 input object to convert from. @see common/Utilities.cpp for concrete implementations | |||
* @return text representation of an Oboe enum value. There is no need to call free on this. | |||
*/ | |||
template <typename FromType> | |||
const char * convertToText(FromType input); | |||
/** | |||
* Return the version of the SDK that is currently running. | |||
* | |||
* For example, on Android, this would return 27 for Oreo 8.1. | |||
* If the version number cannot be determined then this will return -1. | |||
* | |||
* @return version number or -1 | |||
*/ | |||
int getSdkVersion(); | |||
} // namespace oboe | |||
#endif //OBOE_UTILITIES_H |
@@ -1,92 +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 | |||
#include <cstdint> | |||
/** | |||
* 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 1 | |||
// Type: 8-bit unsigned int. Min value: 0 Max value: 255. See below for description. | |||
#define OBOE_VERSION_MINOR 2 | |||
// Type: 16-bit unsigned int. Min value: 0 Max value: 65535. See below for description. | |||
#define OBOE_VERSION_PATCH 4 | |||
#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 { | |||
const char * getVersionText(); | |||
/** | |||
* Oboe versioning object | |||
*/ | |||
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 |
@@ -1,10 +0,0 @@ | |||
The files in this directory are reproduced from the official Oboe repository, which can be found at | |||
github.com/google/oboe. | |||
These files are from tag 1.3 (5c42747). | |||
We've included only those parts of the original repository which are required to build the Oboe | |||
library. Documentation, samples, tests, and other non-library items have been omitted. | |||
Files in this directory and below are licensed under the terms of the license in the LICENSE file | |||
which you can find in the same directory as this readme. |
@@ -1,348 +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 <dlfcn.h> | |||
#include <oboe/Utilities.h> | |||
#include "common/OboeDebug.h" | |||
#include "AAudioLoader.h" | |||
#define LIB_AAUDIO_NAME "libaaudio.so" | |||
namespace oboe { | |||
AAudioLoader::~AAudioLoader() { | |||
if (mLibHandle != nullptr) { | |||
dlclose(mLibHandle); | |||
mLibHandle = nullptr; | |||
} | |||
} | |||
AAudioLoader* AAudioLoader::getInstance() { | |||
static AAudioLoader instance; | |||
return &instance; | |||
} | |||
int AAudioLoader::open() { | |||
if (mLibHandle != nullptr) { | |||
return 0; | |||
} | |||
// Use RTLD_NOW to avoid the unpredictable behavior that RTLD_LAZY can cause. | |||
// Also resolving all the links now will prevent a run-time penalty later. | |||
mLibHandle = dlopen(LIB_AAUDIO_NAME, RTLD_NOW); | |||
if (mLibHandle == nullptr) { | |||
LOGI("AAudioLoader::open() could not find " LIB_AAUDIO_NAME); | |||
return -1; // TODO review return code | |||
} else { | |||
LOGD("AAudioLoader(): dlopen(%s) returned %p", LIB_AAUDIO_NAME, mLibHandle); | |||
} | |||
// Load all the function pointers. | |||
createStreamBuilder = load_I_PPB("AAudio_createStreamBuilder"); | |||
builder_openStream = load_I_PBPPS("AAudioStreamBuilder_openStream"); | |||
builder_setChannelCount = load_V_PBI("AAudioStreamBuilder_setChannelCount"); | |||
if (builder_setChannelCount == nullptr) { | |||
// Use old deprecated alias if needed. | |||
builder_setChannelCount = load_V_PBI("AAudioStreamBuilder_setSamplesPerFrame"); | |||
} | |||
builder_setBufferCapacityInFrames = load_V_PBI("AAudioStreamBuilder_setBufferCapacityInFrames"); | |||
builder_setDeviceId = load_V_PBI("AAudioStreamBuilder_setDeviceId"); | |||
builder_setDirection = load_V_PBI("AAudioStreamBuilder_setDirection"); | |||
builder_setFormat = load_V_PBI("AAudioStreamBuilder_setFormat"); | |||
builder_setFramesPerDataCallback = load_V_PBI("AAudioStreamBuilder_setFramesPerDataCallback"); | |||
builder_setSharingMode = load_V_PBI("AAudioStreamBuilder_setSharingMode"); | |||
builder_setPerformanceMode = load_V_PBI("AAudioStreamBuilder_setPerformanceMode"); | |||
builder_setSampleRate = load_V_PBI("AAudioStreamBuilder_setSampleRate"); | |||
if (getSdkVersion() >= __ANDROID_API_P__){ | |||
builder_setUsage = load_V_PBI("AAudioStreamBuilder_setUsage"); | |||
builder_setContentType = load_V_PBI("AAudioStreamBuilder_setContentType"); | |||
builder_setInputPreset = load_V_PBI("AAudioStreamBuilder_setInputPreset"); | |||
builder_setSessionId = load_V_PBI("AAudioStreamBuilder_setSessionId"); | |||
} | |||
builder_delete = load_I_PB("AAudioStreamBuilder_delete"); | |||
builder_setDataCallback = load_V_PBPDPV("AAudioStreamBuilder_setDataCallback"); | |||
builder_setErrorCallback = load_V_PBPEPV("AAudioStreamBuilder_setErrorCallback"); | |||
stream_read = load_I_PSPVIL("AAudioStream_read"); | |||
stream_write = load_I_PSCPVIL("AAudioStream_write"); | |||
stream_waitForStateChange = load_I_PSTPTL("AAudioStream_waitForStateChange"); | |||
stream_getTimestamp = load_I_PSKPLPL("AAudioStream_getTimestamp"); | |||
stream_isMMapUsed = load_B_PS("AAudioStream_isMMapUsed"); | |||
stream_getChannelCount = load_I_PS("AAudioStream_getChannelCount"); | |||
if (stream_getChannelCount == nullptr) { | |||
// Use old alias if needed. | |||
stream_getChannelCount = load_I_PS("AAudioStream_getSamplesPerFrame"); | |||
} | |||
stream_close = load_I_PS("AAudioStream_close"); | |||
stream_getBufferSize = load_I_PS("AAudioStream_getBufferSizeInFrames"); | |||
stream_getDeviceId = load_I_PS("AAudioStream_getDeviceId"); | |||
stream_getBufferCapacity = load_I_PS("AAudioStream_getBufferCapacityInFrames"); | |||
stream_getFormat = load_F_PS("AAudioStream_getFormat"); | |||
stream_getFramesPerBurst = load_I_PS("AAudioStream_getFramesPerBurst"); | |||
stream_getFramesRead = load_L_PS("AAudioStream_getFramesRead"); | |||
stream_getFramesWritten = load_L_PS("AAudioStream_getFramesWritten"); | |||
stream_getPerformanceMode = load_I_PS("AAudioStream_getPerformanceMode"); | |||
stream_getSampleRate = load_I_PS("AAudioStream_getSampleRate"); | |||
stream_getSharingMode = load_I_PS("AAudioStream_getSharingMode"); | |||
stream_getState = load_I_PS("AAudioStream_getState"); | |||
stream_getXRunCount = load_I_PS("AAudioStream_getXRunCount"); | |||
stream_requestStart = load_I_PS("AAudioStream_requestStart"); | |||
stream_requestPause = load_I_PS("AAudioStream_requestPause"); | |||
stream_requestFlush = load_I_PS("AAudioStream_requestFlush"); | |||
stream_requestStop = load_I_PS("AAudioStream_requestStop"); | |||
stream_setBufferSize = load_I_PSI("AAudioStream_setBufferSizeInFrames"); | |||
convertResultToText = load_CPH_I("AAudio_convertResultToText"); | |||
if (getSdkVersion() >= __ANDROID_API_P__){ | |||
stream_getUsage = load_I_PS("AAudioStream_getUsage"); | |||
stream_getContentType = load_I_PS("AAudioStream_getContentType"); | |||
stream_getInputPreset = load_I_PS("AAudioStream_getInputPreset"); | |||
stream_getSessionId = load_I_PS("AAudioStream_getSessionId"); | |||
} | |||
return 0; | |||
} | |||
static void AAudioLoader_check(void *proc, const char *functionName) { | |||
if (proc == nullptr) { | |||
LOGW("AAudioLoader could not find %s", functionName); | |||
} | |||
} | |||
AAudioLoader::signature_I_PPB AAudioLoader::load_I_PPB(const char *functionName) { | |||
void *proc = dlsym(mLibHandle, functionName); | |||
AAudioLoader_check(proc, functionName); | |||
return reinterpret_cast<signature_I_PPB>(proc); | |||
} | |||
AAudioLoader::signature_CPH_I AAudioLoader::load_CPH_I(const char *functionName) { | |||
void *proc = dlsym(mLibHandle, functionName); | |||
AAudioLoader_check(proc, functionName); | |||
return reinterpret_cast<signature_CPH_I>(proc); | |||
} | |||
AAudioLoader::signature_V_PBI AAudioLoader::load_V_PBI(const char *functionName) { | |||
void *proc = dlsym(mLibHandle, functionName); | |||
AAudioLoader_check(proc, functionName); | |||
return reinterpret_cast<signature_V_PBI>(proc); | |||
} | |||
AAudioLoader::signature_V_PBPDPV AAudioLoader::load_V_PBPDPV(const char *functionName) { | |||
void *proc = dlsym(mLibHandle, functionName); | |||
AAudioLoader_check(proc, functionName); | |||
return reinterpret_cast<signature_V_PBPDPV>(proc); | |||
} | |||
AAudioLoader::signature_V_PBPEPV AAudioLoader::load_V_PBPEPV(const char *functionName) { | |||
void *proc = dlsym(mLibHandle, functionName); | |||
AAudioLoader_check(proc, functionName); | |||
return reinterpret_cast<signature_V_PBPEPV>(proc); | |||
} | |||
AAudioLoader::signature_I_PSI AAudioLoader::load_I_PSI(const char *functionName) { | |||
void *proc = dlsym(mLibHandle, functionName); | |||
AAudioLoader_check(proc, functionName); | |||
return reinterpret_cast<signature_I_PSI>(proc); | |||
} | |||
AAudioLoader::signature_I_PS AAudioLoader::load_I_PS(const char *functionName) { | |||
void *proc = dlsym(mLibHandle, functionName); | |||
AAudioLoader_check(proc, functionName); | |||
return reinterpret_cast<signature_I_PS>(proc); | |||
} | |||
AAudioLoader::signature_L_PS AAudioLoader::load_L_PS(const char *functionName) { | |||
void *proc = dlsym(mLibHandle, functionName); | |||
AAudioLoader_check(proc, functionName); | |||
return reinterpret_cast<signature_L_PS>(proc); | |||
} | |||
AAudioLoader::signature_F_PS AAudioLoader::load_F_PS(const char *functionName) { | |||
void *proc = dlsym(mLibHandle, functionName); | |||
AAudioLoader_check(proc, functionName); | |||
return reinterpret_cast<signature_F_PS>(proc); | |||
} | |||
AAudioLoader::signature_B_PS AAudioLoader::load_B_PS(const char *functionName) { | |||
void *proc = dlsym(mLibHandle, functionName); | |||
AAudioLoader_check(proc, functionName); | |||
return reinterpret_cast<signature_B_PS>(proc); | |||
} | |||
AAudioLoader::signature_I_PB AAudioLoader::load_I_PB(const char *functionName) { | |||
void *proc = dlsym(mLibHandle, functionName); | |||
AAudioLoader_check(proc, functionName); | |||
return reinterpret_cast<signature_I_PB>(proc); | |||
} | |||
AAudioLoader::signature_I_PBPPS AAudioLoader::load_I_PBPPS(const char *functionName) { | |||
void *proc = dlsym(mLibHandle, functionName); | |||
AAudioLoader_check(proc, functionName); | |||
return reinterpret_cast<signature_I_PBPPS>(proc); | |||
} | |||
AAudioLoader::signature_I_PSCPVIL AAudioLoader::load_I_PSCPVIL(const char *functionName) { | |||
void *proc = dlsym(mLibHandle, functionName); | |||
AAudioLoader_check(proc, functionName); | |||
return reinterpret_cast<signature_I_PSCPVIL>(proc); | |||
} | |||
AAudioLoader::signature_I_PSPVIL AAudioLoader::load_I_PSPVIL(const char *functionName) { | |||
void *proc = dlsym(mLibHandle, functionName); | |||
AAudioLoader_check(proc, functionName); | |||
return reinterpret_cast<signature_I_PSPVIL>(proc); | |||
} | |||
AAudioLoader::signature_I_PSTPTL AAudioLoader::load_I_PSTPTL(const char *functionName) { | |||
void *proc = dlsym(mLibHandle, functionName); | |||
AAudioLoader_check(proc, functionName); | |||
return reinterpret_cast<signature_I_PSTPTL>(proc); | |||
} | |||
AAudioLoader::signature_I_PSKPLPL AAudioLoader::load_I_PSKPLPL(const char *functionName) { | |||
void *proc = dlsym(mLibHandle, functionName); | |||
AAudioLoader_check(proc, functionName); | |||
return reinterpret_cast<signature_I_PSKPLPL>(proc); | |||
} | |||
// Ensure that all AAudio primitive data types are int32_t | |||
#define ASSERT_INT32(type) static_assert(std::is_same<int32_t, type>::value, \ | |||
#type" must be int32_t") | |||
#define ERRMSG "Oboe constants must match AAudio constants." | |||
// These asserts help verify that the Oboe definitions match the equivalent AAudio definitions. | |||
// This code is in this .cpp file so it only gets tested once. | |||
#ifdef AAUDIO_AAUDIO_H | |||
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); | |||
static_assert((int32_t)StreamState::Uninitialized == AAUDIO_STREAM_STATE_UNINITIALIZED, ERRMSG); | |||
static_assert((int32_t)StreamState::Unknown == AAUDIO_STREAM_STATE_UNKNOWN, ERRMSG); | |||
static_assert((int32_t)StreamState::Open == AAUDIO_STREAM_STATE_OPEN, ERRMSG); | |||
static_assert((int32_t)StreamState::Starting == AAUDIO_STREAM_STATE_STARTING, ERRMSG); | |||
static_assert((int32_t)StreamState::Started == AAUDIO_STREAM_STATE_STARTED, ERRMSG); | |||
static_assert((int32_t)StreamState::Pausing == AAUDIO_STREAM_STATE_PAUSING, ERRMSG); | |||
static_assert((int32_t)StreamState::Paused == AAUDIO_STREAM_STATE_PAUSED, ERRMSG); | |||
static_assert((int32_t)StreamState::Flushing == AAUDIO_STREAM_STATE_FLUSHING, ERRMSG); | |||
static_assert((int32_t)StreamState::Flushed == AAUDIO_STREAM_STATE_FLUSHED, ERRMSG); | |||
static_assert((int32_t)StreamState::Stopping == AAUDIO_STREAM_STATE_STOPPING, ERRMSG); | |||
static_assert((int32_t)StreamState::Stopped == AAUDIO_STREAM_STATE_STOPPED, ERRMSG); | |||
static_assert((int32_t)StreamState::Closing == AAUDIO_STREAM_STATE_CLOSING, ERRMSG); | |||
static_assert((int32_t)StreamState::Closed == AAUDIO_STREAM_STATE_CLOSED, ERRMSG); | |||
static_assert((int32_t)StreamState::Disconnected == AAUDIO_STREAM_STATE_DISCONNECTED, ERRMSG); | |||
static_assert((int32_t)Direction::Output == AAUDIO_DIRECTION_OUTPUT, ERRMSG); | |||
static_assert((int32_t)Direction::Input == AAUDIO_DIRECTION_INPUT, ERRMSG); | |||
static_assert((int32_t)AudioFormat::Invalid == AAUDIO_FORMAT_INVALID, ERRMSG); | |||
static_assert((int32_t)AudioFormat::Unspecified == AAUDIO_FORMAT_UNSPECIFIED, ERRMSG); | |||
static_assert((int32_t)AudioFormat::I16 == AAUDIO_FORMAT_PCM_I16, ERRMSG); | |||
static_assert((int32_t)AudioFormat::Float == AAUDIO_FORMAT_PCM_FLOAT, ERRMSG); | |||
static_assert((int32_t)DataCallbackResult::Continue == AAUDIO_CALLBACK_RESULT_CONTINUE, ERRMSG); | |||
static_assert((int32_t)DataCallbackResult::Stop == AAUDIO_CALLBACK_RESULT_STOP, ERRMSG); | |||
static_assert((int32_t)Result::OK == AAUDIO_OK, ERRMSG); | |||
static_assert((int32_t)Result::ErrorBase == AAUDIO_ERROR_BASE, ERRMSG); | |||
static_assert((int32_t)Result::ErrorDisconnected == AAUDIO_ERROR_DISCONNECTED, ERRMSG); | |||
static_assert((int32_t)Result::ErrorIllegalArgument == AAUDIO_ERROR_ILLEGAL_ARGUMENT, ERRMSG); | |||
static_assert((int32_t)Result::ErrorInternal == AAUDIO_ERROR_INTERNAL, ERRMSG); | |||
static_assert((int32_t)Result::ErrorInvalidState == AAUDIO_ERROR_INVALID_STATE, ERRMSG); | |||
static_assert((int32_t)Result::ErrorInvalidHandle == AAUDIO_ERROR_INVALID_HANDLE, ERRMSG); | |||
static_assert((int32_t)Result::ErrorUnimplemented == AAUDIO_ERROR_UNIMPLEMENTED, ERRMSG); | |||
static_assert((int32_t)Result::ErrorUnavailable == AAUDIO_ERROR_UNAVAILABLE, ERRMSG); | |||
static_assert((int32_t)Result::ErrorNoFreeHandles == AAUDIO_ERROR_NO_FREE_HANDLES, ERRMSG); | |||
static_assert((int32_t)Result::ErrorNoMemory == AAUDIO_ERROR_NO_MEMORY, ERRMSG); | |||
static_assert((int32_t)Result::ErrorNull == AAUDIO_ERROR_NULL, ERRMSG); | |||
static_assert((int32_t)Result::ErrorTimeout == AAUDIO_ERROR_TIMEOUT, ERRMSG); | |||
static_assert((int32_t)Result::ErrorWouldBlock == AAUDIO_ERROR_WOULD_BLOCK, ERRMSG); | |||
static_assert((int32_t)Result::ErrorInvalidFormat == AAUDIO_ERROR_INVALID_FORMAT, ERRMSG); | |||
static_assert((int32_t)Result::ErrorOutOfRange == AAUDIO_ERROR_OUT_OF_RANGE, ERRMSG); | |||
static_assert((int32_t)Result::ErrorNoService == AAUDIO_ERROR_NO_SERVICE, ERRMSG); | |||
static_assert((int32_t)Result::ErrorInvalidRate == AAUDIO_ERROR_INVALID_RATE, ERRMSG); | |||
static_assert((int32_t)SharingMode::Exclusive == AAUDIO_SHARING_MODE_EXCLUSIVE, ERRMSG); | |||
static_assert((int32_t)SharingMode::Shared == AAUDIO_SHARING_MODE_SHARED, ERRMSG); | |||
static_assert((int32_t)PerformanceMode::None == AAUDIO_PERFORMANCE_MODE_NONE, ERRMSG); | |||
static_assert((int32_t)PerformanceMode::PowerSaving | |||
== AAUDIO_PERFORMANCE_MODE_POWER_SAVING, ERRMSG); | |||
static_assert((int32_t)PerformanceMode::LowLatency | |||
== AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, ERRMSG); | |||
#endif | |||
// The aaudio_ usage, content and input_preset types were added in NDK 17, | |||
// which is the first version to support Android Pie (API 28). | |||
#if __NDK_MAJOR__ >= 17 | |||
ASSERT_INT32(aaudio_usage_t); | |||
ASSERT_INT32(aaudio_content_type_t); | |||
ASSERT_INT32(aaudio_input_preset_t); | |||
static_assert((int32_t)Usage::Media == AAUDIO_USAGE_MEDIA, ERRMSG); | |||
static_assert((int32_t)Usage::VoiceCommunication == AAUDIO_USAGE_VOICE_COMMUNICATION, ERRMSG); | |||
static_assert((int32_t)Usage::VoiceCommunicationSignalling | |||
== AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING, ERRMSG); | |||
static_assert((int32_t)Usage::Alarm == AAUDIO_USAGE_ALARM, ERRMSG); | |||
static_assert((int32_t)Usage::Notification == AAUDIO_USAGE_NOTIFICATION, ERRMSG); | |||
static_assert((int32_t)Usage::NotificationRingtone == AAUDIO_USAGE_NOTIFICATION_RINGTONE, ERRMSG); | |||
static_assert((int32_t)Usage::NotificationEvent == AAUDIO_USAGE_NOTIFICATION_EVENT, ERRMSG); | |||
static_assert((int32_t)Usage::AssistanceAccessibility == AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY, ERRMSG); | |||
static_assert((int32_t)Usage::AssistanceNavigationGuidance | |||
== AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE, ERRMSG); | |||
static_assert((int32_t)Usage::AssistanceSonification == AAUDIO_USAGE_ASSISTANCE_SONIFICATION, ERRMSG); | |||
static_assert((int32_t)Usage::Game == AAUDIO_USAGE_GAME, ERRMSG); | |||
static_assert((int32_t)Usage::Assistant == AAUDIO_USAGE_ASSISTANT, ERRMSG); | |||
static_assert((int32_t)ContentType::Speech == AAUDIO_CONTENT_TYPE_SPEECH, ERRMSG); | |||
static_assert((int32_t)ContentType::Music == AAUDIO_CONTENT_TYPE_MUSIC, ERRMSG); | |||
static_assert((int32_t)ContentType::Movie == AAUDIO_CONTENT_TYPE_MOVIE, ERRMSG); | |||
static_assert((int32_t)ContentType::Sonification == AAUDIO_CONTENT_TYPE_SONIFICATION, ERRMSG); | |||
static_assert((int32_t)InputPreset::Generic == AAUDIO_INPUT_PRESET_GENERIC, ERRMSG); | |||
static_assert((int32_t)InputPreset::Camcorder == AAUDIO_INPUT_PRESET_CAMCORDER, ERRMSG); | |||
static_assert((int32_t)InputPreset::VoiceRecognition == AAUDIO_INPUT_PRESET_VOICE_RECOGNITION, ERRMSG); | |||
static_assert((int32_t)InputPreset::VoiceCommunication | |||
== AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION, ERRMSG); | |||
static_assert((int32_t)InputPreset::Unprocessed == AAUDIO_INPUT_PRESET_UNPROCESSED, ERRMSG); | |||
static_assert((int32_t)SessionId::None == AAUDIO_SESSION_ID_NONE, ERRMSG); | |||
static_assert((int32_t)SessionId::Allocate == AAUDIO_SESSION_ID_ALLOCATE, ERRMSG); | |||
#endif | |||
} // namespace oboe |
@@ -1,229 +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_AAUDIO_LOADER_H_ | |||
#define OBOE_AAUDIO_LOADER_H_ | |||
#include <unistd.h> | |||
#include "oboe/Definitions.h" | |||
// If the NDK is before O then define this in your build | |||
// so that AAudio.h will not be included. | |||
#ifdef OBOE_NO_INCLUDE_AAUDIO | |||
// Define missing types from AAudio.h | |||
typedef int32_t aaudio_stream_state_t; | |||
typedef int32_t aaudio_direction_t; | |||
typedef int32_t aaudio_format_t; | |||
typedef int32_t aaudio_data_callback_result_t; | |||
typedef int32_t aaudio_result_t; | |||
typedef int32_t aaudio_sharing_mode_t; | |||
typedef int32_t aaudio_performance_mode_t; | |||
typedef struct AAudioStreamStruct AAudioStream; | |||
typedef struct AAudioStreamBuilderStruct AAudioStreamBuilder; | |||
typedef aaudio_data_callback_result_t (*AAudioStream_dataCallback)( | |||
AAudioStream *stream, | |||
void *userData, | |||
void *audioData, | |||
int32_t numFrames); | |||
typedef void (*AAudioStream_errorCallback)( | |||
AAudioStream *stream, | |||
void *userData, | |||
aaudio_result_t error); | |||
// These were defined in P | |||
typedef int32_t aaudio_usage_t; | |||
typedef int32_t aaudio_content_type_t; | |||
typedef int32_t aaudio_input_preset_t; | |||
typedef int32_t aaudio_session_id_t; | |||
#else | |||
#include <aaudio/AAudio.h> | |||
#include <android/ndk-version.h> | |||
#endif | |||
#ifndef __NDK_MAJOR__ | |||
#define __NDK_MAJOR__ 0 | |||
#endif | |||
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(). | |||
* On older versions this linkage will safely fail. | |||
*/ | |||
class AAudioLoader { | |||
public: | |||
// Use signatures for common functions. | |||
// Key to letter abbreviations. | |||
// S = Stream | |||
// B = Builder | |||
// I = int32_t | |||
// L = int64_t | |||
// T = sTate | |||
// K = clocKid_t | |||
// P = Pointer to following data type | |||
// C = Const prefix | |||
// H = cHar | |||
typedef int32_t (*signature_I_PPB)(AAudioStreamBuilder **builder); | |||
typedef const char * (*signature_CPH_I)(int32_t); | |||
typedef int32_t (*signature_I_PBPPS)(AAudioStreamBuilder *, | |||
AAudioStream **stream); // AAudioStreamBuilder_open() | |||
typedef int32_t (*signature_I_PB)(AAudioStreamBuilder *); // AAudioStreamBuilder_delete() | |||
// AAudioStreamBuilder_setSampleRate() | |||
typedef void (*signature_V_PBI)(AAudioStreamBuilder *, int32_t); | |||
typedef int32_t (*signature_I_PS)(AAudioStream *); // AAudioStream_getSampleRate() | |||
typedef int64_t (*signature_L_PS)(AAudioStream *); // AAudioStream_getFramesRead() | |||
// AAudioStream_setBufferSizeInFrames() | |||
typedef int32_t (*signature_I_PSI)(AAudioStream *, int32_t); | |||
typedef void (*signature_V_PBPDPV)(AAudioStreamBuilder *, | |||
AAudioStream_dataCallback, | |||
void *); | |||
typedef void (*signature_V_PBPEPV)(AAudioStreamBuilder *, | |||
AAudioStream_errorCallback, | |||
void *); | |||
typedef aaudio_format_t (*signature_F_PS)(AAudioStream *stream); | |||
typedef int32_t (*signature_I_PSPVIL)(AAudioStream *, void *, int32_t, int64_t); | |||
typedef int32_t (*signature_I_PSCPVIL)(AAudioStream *, const void *, int32_t, int64_t); | |||
typedef int32_t (*signature_I_PSTPTL)(AAudioStream *, | |||
aaudio_stream_state_t, | |||
aaudio_stream_state_t *, | |||
int64_t); | |||
typedef int32_t (*signature_I_PSKPLPL)(AAudioStream *, clockid_t, int64_t *, int64_t *); | |||
typedef bool (*signature_B_PS)(AAudioStream *); | |||
static AAudioLoader* getInstance(); // singleton | |||
/** | |||
* Open the AAudio shared library and load the function pointers. | |||
* This can be called multiple times. | |||
* It should only be called from one thread. | |||
* | |||
* The destructor will clean up after the open. | |||
* | |||
* @return 0 if successful or negative error. | |||
*/ | |||
int open(); | |||
// Function pointers into the AAudio shared library. | |||
signature_I_PPB createStreamBuilder = nullptr; | |||
signature_I_PBPPS builder_openStream = nullptr; | |||
signature_V_PBI builder_setBufferCapacityInFrames = nullptr; | |||
signature_V_PBI builder_setChannelCount = nullptr; | |||
signature_V_PBI builder_setDeviceId = nullptr; | |||
signature_V_PBI builder_setDirection = nullptr; | |||
signature_V_PBI builder_setFormat = nullptr; | |||
signature_V_PBI builder_setFramesPerDataCallback = nullptr; | |||
signature_V_PBI builder_setPerformanceMode = nullptr; | |||
signature_V_PBI builder_setSampleRate = nullptr; | |||
signature_V_PBI builder_setSharingMode = nullptr; | |||
signature_V_PBI builder_setUsage = nullptr; | |||
signature_V_PBI builder_setContentType = nullptr; | |||
signature_V_PBI builder_setInputPreset = nullptr; | |||
signature_V_PBI builder_setSessionId = nullptr; | |||
signature_V_PBPDPV builder_setDataCallback = nullptr; | |||
signature_V_PBPEPV builder_setErrorCallback = nullptr; | |||
signature_I_PB builder_delete = nullptr; | |||
signature_F_PS stream_getFormat = nullptr; | |||
signature_I_PSPVIL stream_read = nullptr; | |||
signature_I_PSCPVIL stream_write = nullptr; | |||
signature_I_PSTPTL stream_waitForStateChange = nullptr; | |||
signature_I_PSKPLPL stream_getTimestamp = nullptr; | |||
signature_B_PS stream_isMMapUsed = nullptr; | |||
signature_I_PS stream_close = nullptr; | |||
signature_I_PS stream_getChannelCount = nullptr; | |||
signature_I_PS stream_getDeviceId = nullptr; | |||
signature_I_PS stream_getBufferSize = nullptr; | |||
signature_I_PS stream_getBufferCapacity = nullptr; | |||
signature_I_PS stream_getFramesPerBurst = nullptr; | |||
signature_I_PS stream_getState = nullptr; | |||
signature_I_PS stream_getPerformanceMode = nullptr; | |||
signature_I_PS stream_getSampleRate = nullptr; | |||
signature_I_PS stream_getSharingMode = nullptr; | |||
signature_I_PS stream_getXRunCount = nullptr; | |||
signature_I_PSI stream_setBufferSize = nullptr; | |||
signature_I_PS stream_requestStart = nullptr; | |||
signature_I_PS stream_requestPause = nullptr; | |||
signature_I_PS stream_requestFlush = nullptr; | |||
signature_I_PS stream_requestStop = nullptr; | |||
signature_L_PS stream_getFramesRead = nullptr; | |||
signature_L_PS stream_getFramesWritten = nullptr; | |||
signature_CPH_I convertResultToText = nullptr; | |||
signature_I_PS stream_getUsage = nullptr; | |||
signature_I_PS stream_getContentType = nullptr; | |||
signature_I_PS stream_getInputPreset = nullptr; | |||
signature_I_PS stream_getSessionId = nullptr; | |||
private: | |||
AAudioLoader() {} | |||
~AAudioLoader(); | |||
// Load function pointers for specific signatures. | |||
signature_I_PPB load_I_PPB(const char *name); | |||
signature_CPH_I load_CPH_I(const char *name); | |||
signature_V_PBI load_V_PBI(const char *name); | |||
signature_V_PBPDPV load_V_PBPDPV(const char *name); | |||
signature_V_PBPEPV load_V_PBPEPV(const char *name); | |||
signature_I_PB load_I_PB(const char *name); | |||
signature_I_PBPPS load_I_PBPPS(const char *name); | |||
signature_I_PS load_I_PS(const char *name); | |||
signature_L_PS load_L_PS(const char *name); | |||
signature_F_PS load_F_PS(const char *name); | |||
signature_B_PS load_B_PS(const char *name); | |||
signature_I_PSI load_I_PSI(const char *name); | |||
signature_I_PSPVIL load_I_PSPVIL(const char *name); | |||
signature_I_PSCPVIL load_I_PSCPVIL(const char *name); | |||
signature_I_PSTPTL load_I_PSTPTL(const char *name); | |||
signature_I_PSKPLPL load_I_PSKPLPL(const char *name); | |||
void *mLibHandle = nullptr; | |||
}; | |||
} // namespace oboe | |||
#endif //OBOE_AAUDIO_LOADER_H_ |
@@ -1,620 +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 <cassert> | |||
#include <stdint.h> | |||
#include <stdlib.h> | |||
#include "aaudio/AAudioLoader.h" | |||
#include "aaudio/AudioStreamAAudio.h" | |||
#include "common/AudioClock.h" | |||
#include "common/OboeDebug.h" | |||
#include "oboe/Utilities.h" | |||
#ifdef __ANDROID__ | |||
#include <sys/system_properties.h> | |||
#endif | |||
#ifndef OBOE_FIX_FORCE_STARTING_TO_STARTED | |||
// Workaround state problems in AAudio | |||
// TODO Which versions does this occur in? Verify fixed in Q. | |||
#define OBOE_FIX_FORCE_STARTING_TO_STARTED 1 | |||
#endif // OBOE_FIX_FORCE_STARTING_TO_STARTED | |||
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 = reinterpret_cast<AudioStreamAAudio*>(userData); | |||
if (oboeStream != nullptr) { | |||
return static_cast<aaudio_data_callback_result_t>( | |||
oboeStream->callOnAudioReady(stream, audioData, numFrames)); | |||
} else { | |||
return static_cast<aaudio_data_callback_result_t>(DataCallbackResult::Stop); | |||
} | |||
} | |||
// This runs in its own thread. | |||
// Only one of these threads will be launched from internalErrorCallback(). | |||
// It calls app error callbacks from a static function in case the stream gets deleted. | |||
static void oboe_aaudio_error_thread_proc(AudioStreamAAudio *oboeStream, | |||
Result error) { | |||
LOGD("%s() - entering >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", __func__); | |||
oboeStream->requestStop(); | |||
if (oboeStream->getCallback() != nullptr) { | |||
oboeStream->getCallback()->onErrorBeforeClose(oboeStream, error); | |||
} | |||
oboeStream->close(); | |||
if (oboeStream->getCallback() != nullptr) { | |||
// Warning, oboeStream may get deleted by this callback. | |||
oboeStream->getCallback()->onErrorAfterClose(oboeStream, error); | |||
} | |||
LOGD("%s() - exiting <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", __func__); | |||
} | |||
namespace oboe { | |||
/* | |||
* Create a stream that uses Oboe Audio API. | |||
*/ | |||
AudioStreamAAudio::AudioStreamAAudio(const AudioStreamBuilder &builder) | |||
: AudioStream(builder) | |||
, mAAudioStream(nullptr) { | |||
mCallbackThreadEnabled.store(false); | |||
LOGD("AudioStreamAAudio() call isSupported()"); | |||
isSupported(); | |||
} | |||
bool AudioStreamAAudio::isSupported() { | |||
mLibLoader = AAudioLoader::getInstance(); | |||
int openResult = mLibLoader->open(); | |||
return openResult == 0; | |||
} | |||
// Static 'C' wrapper for the error callback method. | |||
// Launch a thread to handle the error. | |||
// That other thread can safely stop, close and delete the stream. | |||
void AudioStreamAAudio::internalErrorCallback( | |||
AAudioStream *stream, | |||
void *userData, | |||
aaudio_result_t error) { | |||
AudioStreamAAudio *oboeStream = reinterpret_cast<AudioStreamAAudio*>(userData); | |||
// These checks should be enough because we assume that the stream close() | |||
// will join() any active callback threads and will not allow new callbacks. | |||
if (oboeStream->wasErrorCallbackCalled()) { // block extra error callbacks | |||
LOGE("%s() multiple error callbacks called!", __func__); | |||
} else if (stream != oboeStream->getUnderlyingStream()) { | |||
LOGD("%s() stream already closed", __func__); // can happen if there are bugs | |||
} else { | |||
// Handle error on a separate thread. | |||
std::thread t(oboe_aaudio_error_thread_proc, oboeStream, | |||
static_cast<Result>(error)); | |||
t.detach(); | |||
} | |||
} | |||
void AudioStreamAAudio::logUnsupportedAttributes() { | |||
int sdkVersion = getSdkVersion(); | |||
// These attributes are not supported pre Android "P" | |||
if (sdkVersion < __ANDROID_API_P__) { | |||
if (mUsage != Usage::Media) { | |||
LOGW("Usage [AudioStreamBuilder::setUsage()] " | |||
"is not supported on AAudio streams running on pre-Android P versions."); | |||
} | |||
if (mContentType != ContentType::Music) { | |||
LOGW("ContentType [AudioStreamBuilder::setContentType()] " | |||
"is not supported on AAudio streams running on pre-Android P versions."); | |||
} | |||
if (mSessionId != SessionId::None) { | |||
LOGW("SessionId [AudioStreamBuilder::setSessionId()] " | |||
"is not supported on AAudio streams running on pre-Android P versions."); | |||
} | |||
} | |||
} | |||
Result AudioStreamAAudio::open() { | |||
Result result = Result::OK; | |||
if (mAAudioStream != nullptr) { | |||
return Result::ErrorInvalidState; | |||
} | |||
result = AudioStream::open(); | |||
if (result != Result::OK) { | |||
return result; | |||
} | |||
AAudioStreamBuilder *aaudioBuilder; | |||
result = static_cast<Result>(mLibLoader->createStreamBuilder(&aaudioBuilder)); | |||
if (result != Result::OK) { | |||
return result; | |||
} | |||
// Do not set INPUT capacity below 4096 because that prevents us from getting a FAST track | |||
// when using the Legacy data path. | |||
// If the app requests > 4096 then we allow it but we are less likely to get LowLatency. | |||
// See internal bug b/80308183 for more details. | |||
// Fixed in Q but let's still clip the capacity because high input capacity | |||
// does not increase latency. | |||
int32_t capacity = mBufferCapacityInFrames; | |||
constexpr int kCapacityRequiredForFastLegacyTrack = 4096; // matches value in AudioFinger | |||
if (mDirection == oboe::Direction::Input | |||
&& capacity != oboe::Unspecified | |||
&& capacity < kCapacityRequiredForFastLegacyTrack | |||
&& mPerformanceMode == oboe::PerformanceMode::LowLatency) { | |||
capacity = kCapacityRequiredForFastLegacyTrack; | |||
LOGD("AudioStreamAAudio.open() capacity changed from %d to %d for lower latency", | |||
static_cast<int>(mBufferCapacityInFrames), capacity); | |||
} | |||
mLibLoader->builder_setBufferCapacityInFrames(aaudioBuilder, capacity); | |||
mLibLoader->builder_setChannelCount(aaudioBuilder, mChannelCount); | |||
mLibLoader->builder_setDeviceId(aaudioBuilder, mDeviceId); | |||
mLibLoader->builder_setDirection(aaudioBuilder, static_cast<aaudio_direction_t>(mDirection)); | |||
mLibLoader->builder_setFormat(aaudioBuilder, static_cast<aaudio_format_t>(mFormat)); | |||
mLibLoader->builder_setSampleRate(aaudioBuilder, mSampleRate); | |||
mLibLoader->builder_setSharingMode(aaudioBuilder, | |||
static_cast<aaudio_sharing_mode_t>(mSharingMode)); | |||
mLibLoader->builder_setPerformanceMode(aaudioBuilder, | |||
static_cast<aaudio_performance_mode_t>(mPerformanceMode)); | |||
// These were added in P so we have to check for the function pointer. | |||
if (mLibLoader->builder_setUsage != nullptr) { | |||
mLibLoader->builder_setUsage(aaudioBuilder, | |||
static_cast<aaudio_usage_t>(mUsage)); | |||
} | |||
if (mLibLoader->builder_setContentType != nullptr) { | |||
mLibLoader->builder_setContentType(aaudioBuilder, | |||
static_cast<aaudio_content_type_t>(mContentType)); | |||
} | |||
if (mLibLoader->builder_setInputPreset != nullptr) { | |||
mLibLoader->builder_setInputPreset(aaudioBuilder, | |||
static_cast<aaudio_input_preset_t>(mInputPreset)); | |||
} | |||
if (mLibLoader->builder_setSessionId != nullptr) { | |||
mLibLoader->builder_setSessionId(aaudioBuilder, | |||
static_cast<aaudio_session_id_t>(mSessionId)); | |||
} | |||
// 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()); | |||
// If the data callback is not being used then the write method will return an error | |||
// and the app can stop and close the stream. | |||
mLibLoader->builder_setErrorCallback(aaudioBuilder, internalErrorCallback, this); | |||
} | |||
// ============= OPEN THE STREAM ================ | |||
{ | |||
AAudioStream *stream = nullptr; | |||
result = static_cast<Result>(mLibLoader->builder_openStream(aaudioBuilder, &stream)); | |||
mAAudioStream.store(stream); | |||
} | |||
if (result != Result::OK) { | |||
goto error2; | |||
} | |||
// Query and cache the stream properties | |||
mDeviceId = mLibLoader->stream_getDeviceId(mAAudioStream); | |||
mChannelCount = mLibLoader->stream_getChannelCount(mAAudioStream); | |||
mSampleRate = mLibLoader->stream_getSampleRate(mAAudioStream); | |||
mFormat = static_cast<AudioFormat>(mLibLoader->stream_getFormat(mAAudioStream)); | |||
mSharingMode = static_cast<SharingMode>(mLibLoader->stream_getSharingMode(mAAudioStream)); | |||
mPerformanceMode = static_cast<PerformanceMode>( | |||
mLibLoader->stream_getPerformanceMode(mAAudioStream)); | |||
mBufferCapacityInFrames = mLibLoader->stream_getBufferCapacity(mAAudioStream); | |||
mBufferSizeInFrames = mLibLoader->stream_getBufferSize(mAAudioStream); | |||
// These were added in P so we have to check for the function pointer. | |||
if (mLibLoader->stream_getUsage != nullptr) { | |||
mUsage = static_cast<Usage>(mLibLoader->stream_getUsage(mAAudioStream)); | |||
} | |||
if (mLibLoader->stream_getContentType != nullptr) { | |||
mContentType = static_cast<ContentType>(mLibLoader->stream_getContentType(mAAudioStream)); | |||
} | |||
if (mLibLoader->stream_getInputPreset != nullptr) { | |||
mInputPreset = static_cast<InputPreset>(mLibLoader->stream_getInputPreset(mAAudioStream)); | |||
} | |||
if (mLibLoader->stream_getSessionId != nullptr) { | |||
mSessionId = static_cast<SessionId>(mLibLoader->stream_getSessionId(mAAudioStream)); | |||
} else { | |||
mSessionId = SessionId::None; | |||
} | |||
LOGD("AudioStreamAAudio.open() app format = %d", static_cast<int>(mFormat)); | |||
LOGD("AudioStreamAAudio.open() sample rate = %d", static_cast<int>(mSampleRate)); | |||
LOGD("AudioStreamAAudio.open() capacity = %d", static_cast<int>(mBufferCapacityInFrames)); | |||
error2: | |||
mLibLoader->builder_delete(aaudioBuilder); | |||
LOGD("AudioStreamAAudio.open: AAudioStream_Open() returned %s, mAAudioStream = %p", | |||
mLibLoader->convertResultToText(static_cast<aaudio_result_t>(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<std::mutex> lock(mLock); | |||
AudioStream::close(); | |||
// This will delete the AAudio stream object so we need to null out the pointer. | |||
AAudioStream *stream = mAAudioStream.exchange(nullptr); | |||
if (stream != nullptr) { | |||
return static_cast<Result>(mLibLoader->stream_close(stream)); | |||
} else { | |||
return Result::ErrorClosed; | |||
} | |||
} | |||
DataCallbackResult AudioStreamAAudio::callOnAudioReady(AAudioStream *stream, | |||
void *audioData, | |||
int32_t numFrames) { | |||
DataCallbackResult result = fireDataCallback(audioData, numFrames); | |||
if (result == DataCallbackResult::Continue) { | |||
return result; | |||
} else { | |||
if (result == DataCallbackResult::Stop) { | |||
LOGE("Oboe callback returned DataCallbackResult::Stop"); | |||
} else { | |||
LOGE("Oboe callback returned unexpected value = %d", result); | |||
} | |||
if (getSdkVersion() <= __ANDROID_API_P__) { | |||
launchStopThread(); | |||
if (isMMapUsed()) { | |||
return DataCallbackResult::Stop; | |||
} else { | |||
// Legacy stream <= API_P cannot be restarted after returning Stop. | |||
return DataCallbackResult::Continue; | |||
} | |||
} else { | |||
return DataCallbackResult::Stop; // OK >= API_Q | |||
} | |||
} | |||
} | |||
Result AudioStreamAAudio::requestStart() { | |||
std::lock_guard<std::mutex> lock(mLock); | |||
AAudioStream *stream = mAAudioStream.load(); | |||
if (stream != nullptr) { | |||
// Avoid state machine errors in O_MR1. | |||
if (getSdkVersion() <= __ANDROID_API_O_MR1__) { | |||
StreamState state = static_cast<StreamState>(mLibLoader->stream_getState(stream)); | |||
if (state == StreamState::Starting || state == StreamState::Started) { | |||
// WARNING: On P, AAudio is returning ErrorInvalidState for Output and OK for Input. | |||
return Result::OK; | |||
} | |||
} | |||
if (mStreamCallback != nullptr) { // Was a callback requested? | |||
setDataCallbackEnabled(true); | |||
} | |||
return static_cast<Result>(mLibLoader->stream_requestStart(stream)); | |||
} else { | |||
return Result::ErrorClosed; | |||
} | |||
} | |||
Result AudioStreamAAudio::requestPause() { | |||
std::lock_guard<std::mutex> lock(mLock); | |||
AAudioStream *stream = mAAudioStream.load(); | |||
if (stream != nullptr) { | |||
// Avoid state machine errors in O_MR1. | |||
if (getSdkVersion() <= __ANDROID_API_O_MR1__) { | |||
StreamState state = static_cast<StreamState>(mLibLoader->stream_getState(stream)); | |||
if (state == StreamState::Pausing || state == StreamState::Paused) { | |||
return Result::OK; | |||
} | |||
} | |||
return static_cast<Result>(mLibLoader->stream_requestPause(stream)); | |||
} else { | |||
return Result::ErrorClosed; | |||
} | |||
} | |||
Result AudioStreamAAudio::requestFlush() { | |||
std::lock_guard<std::mutex> lock(mLock); | |||
AAudioStream *stream = mAAudioStream.load(); | |||
if (stream != nullptr) { | |||
// Avoid state machine errors in O_MR1. | |||
if (getSdkVersion() <= __ANDROID_API_O_MR1__) { | |||
StreamState state = static_cast<StreamState>(mLibLoader->stream_getState(stream)); | |||
if (state == StreamState::Flushing || state == StreamState::Flushed) { | |||
return Result::OK; | |||
} | |||
} | |||
return static_cast<Result>(mLibLoader->stream_requestFlush(stream)); | |||
} else { | |||
return Result::ErrorClosed; | |||
} | |||
} | |||
Result AudioStreamAAudio::requestStop() { | |||
std::lock_guard<std::mutex> lock(mLock); | |||
AAudioStream *stream = mAAudioStream.load(); | |||
if (stream != nullptr) { | |||
// Avoid state machine errors in O_MR1. | |||
if (getSdkVersion() <= __ANDROID_API_O_MR1__) { | |||
StreamState state = static_cast<StreamState>(mLibLoader->stream_getState(stream)); | |||
if (state == StreamState::Stopping || state == StreamState::Stopped) { | |||
return Result::OK; | |||
} | |||
} | |||
return static_cast<Result>(mLibLoader->stream_requestStop(stream)); | |||
} else { | |||
return Result::ErrorClosed; | |||
} | |||
} | |||
ResultWithValue<int32_t> AudioStreamAAudio::write(const void *buffer, | |||
int32_t numFrames, | |||
int64_t timeoutNanoseconds) { | |||
AAudioStream *stream = mAAudioStream.load(); | |||
if (stream != nullptr) { | |||
int32_t result = mLibLoader->stream_write(mAAudioStream, buffer, | |||
numFrames, timeoutNanoseconds); | |||
return ResultWithValue<int32_t>::createBasedOnSign(result); | |||
} else { | |||
return ResultWithValue<int32_t>(Result::ErrorClosed); | |||
} | |||
} | |||
ResultWithValue<int32_t> AudioStreamAAudio::read(void *buffer, | |||
int32_t numFrames, | |||
int64_t timeoutNanoseconds) { | |||
AAudioStream *stream = mAAudioStream.load(); | |||
if (stream != nullptr) { | |||
int32_t result = mLibLoader->stream_read(mAAudioStream, buffer, | |||
numFrames, timeoutNanoseconds); | |||
return ResultWithValue<int32_t>::createBasedOnSign(result); | |||
} else { | |||
return ResultWithValue<int32_t>(Result::ErrorClosed); | |||
} | |||
} | |||
// AAudioStream_waitForStateChange() can crash if it is waiting on a stream and that stream | |||
// is closed from another thread. We do not want to lock the stream for the duration of the call. | |||
// So we call AAudioStream_waitForStateChange() with a timeout of zero so that it will not block. | |||
// Then we can do our own sleep with the lock unlocked. | |||
Result AudioStreamAAudio::waitForStateChange(StreamState currentState, | |||
StreamState *nextState, | |||
int64_t timeoutNanoseconds) { | |||
Result oboeResult = Result::ErrorTimeout; | |||
int64_t sleepTimeNanos = 20 * kNanosPerMillisecond; // arbitrary | |||
aaudio_stream_state_t currentAAudioState = static_cast<aaudio_stream_state_t>(currentState); | |||
aaudio_result_t result = AAUDIO_OK; | |||
int64_t timeLeftNanos = timeoutNanoseconds; | |||
mLock.lock(); | |||
while (true) { | |||
// Do we still have an AAudio stream? If not then stream must have been closed. | |||
AAudioStream *stream = mAAudioStream.load(); | |||
if (stream == nullptr) { | |||
if (nextState != nullptr) { | |||
*nextState = StreamState::Closed; | |||
} | |||
oboeResult = Result::ErrorClosed; | |||
break; | |||
} | |||
// Update and query state change with no blocking. | |||
aaudio_stream_state_t aaudioNextState; | |||
result = mLibLoader->stream_waitForStateChange( | |||
mAAudioStream, | |||
currentAAudioState, | |||
&aaudioNextState, | |||
0); // timeout=0 for non-blocking | |||
// AAudio will return AAUDIO_ERROR_TIMEOUT if timeout=0 and the state does not change. | |||
if (result != AAUDIO_OK && result != AAUDIO_ERROR_TIMEOUT) { | |||
oboeResult = static_cast<Result>(result); | |||
break; | |||
} | |||
#if OBOE_FIX_FORCE_STARTING_TO_STARTED | |||
if (aaudioNextState == static_cast<aaudio_stream_state_t >(StreamState::Starting)) { | |||
aaudioNextState = static_cast<aaudio_stream_state_t >(StreamState::Started); | |||
} | |||
#endif // OBOE_FIX_FORCE_STARTING_TO_STARTED | |||
if (nextState != nullptr) { | |||
*nextState = static_cast<StreamState>(aaudioNextState); | |||
} | |||
if (currentAAudioState != aaudioNextState) { // state changed? | |||
oboeResult = Result::OK; | |||
break; | |||
} | |||
// Did we timeout or did user ask for non-blocking? | |||
if (timeLeftNanos <= 0) { | |||
break; | |||
} | |||
// No change yet so sleep. | |||
mLock.unlock(); // Don't sleep while locked. | |||
if (sleepTimeNanos > timeLeftNanos) { | |||
sleepTimeNanos = timeLeftNanos; // last little bit | |||
} | |||
AudioClock::sleepForNanos(sleepTimeNanos); | |||
timeLeftNanos -= sleepTimeNanos; | |||
mLock.lock(); | |||
} | |||
mLock.unlock(); | |||
return oboeResult; | |||
} | |||
ResultWithValue<int32_t> AudioStreamAAudio::setBufferSizeInFrames(int32_t requestedFrames) { | |||
AAudioStream *stream = mAAudioStream.load(); | |||
if (stream != nullptr) { | |||
if (requestedFrames > mBufferCapacityInFrames) { | |||
requestedFrames = mBufferCapacityInFrames; | |||
} | |||
int32_t newBufferSize = mLibLoader->stream_setBufferSize(mAAudioStream, requestedFrames); | |||
// Cache the result if it's valid | |||
if (newBufferSize > 0) mBufferSizeInFrames = newBufferSize; | |||
return ResultWithValue<int32_t>::createBasedOnSign(newBufferSize); | |||
} else { | |||
return ResultWithValue<int32_t>(Result::ErrorClosed); | |||
} | |||
} | |||
StreamState AudioStreamAAudio::getState() const { | |||
AAudioStream *stream = mAAudioStream.load(); | |||
if (stream != nullptr) { | |||
aaudio_stream_state_t aaudioState = mLibLoader->stream_getState(stream); | |||
#if OBOE_FIX_FORCE_STARTING_TO_STARTED | |||
if (aaudioState == AAUDIO_STREAM_STATE_STARTING) { | |||
aaudioState = AAUDIO_STREAM_STATE_STARTED; | |||
} | |||
#endif // OBOE_FIX_FORCE_STARTING_TO_STARTED | |||
return static_cast<StreamState>(aaudioState); | |||
} else { | |||
return StreamState::Closed; | |||
} | |||
} | |||
int32_t AudioStreamAAudio::getBufferSizeInFrames() { | |||
AAudioStream *stream = mAAudioStream.load(); | |||
if (stream != nullptr) { | |||
mBufferSizeInFrames = mLibLoader->stream_getBufferSize(stream); | |||
} | |||
return mBufferSizeInFrames; | |||
} | |||
int32_t AudioStreamAAudio::getFramesPerBurst() { | |||
AAudioStream *stream = mAAudioStream.load(); | |||
if (stream != nullptr) { | |||
mFramesPerBurst = mLibLoader->stream_getFramesPerBurst(stream); | |||
} | |||
return mFramesPerBurst; | |||
} | |||
void AudioStreamAAudio::updateFramesRead() { | |||
AAudioStream *stream = mAAudioStream.load(); | |||
if (stream != nullptr) { | |||
mFramesRead = mLibLoader->stream_getFramesRead(stream); | |||
} | |||
} | |||
void AudioStreamAAudio::updateFramesWritten() { | |||
AAudioStream *stream = mAAudioStream.load(); | |||
if (stream != nullptr) { | |||
mFramesWritten = mLibLoader->stream_getFramesWritten(stream); | |||
} | |||
} | |||
ResultWithValue<int32_t> AudioStreamAAudio::getXRunCount() const { | |||
AAudioStream *stream = mAAudioStream.load(); | |||
if (stream != nullptr) { | |||
return ResultWithValue<int32_t>::createBasedOnSign(mLibLoader->stream_getXRunCount(stream)); | |||
} else { | |||
return ResultWithValue<int32_t>(Result::ErrorNull); | |||
} | |||
} | |||
Result AudioStreamAAudio::getTimestamp(clockid_t clockId, | |||
int64_t *framePosition, | |||
int64_t *timeNanoseconds) { | |||
AAudioStream *stream = mAAudioStream.load(); | |||
if (stream != nullptr) { | |||
if (getState() != StreamState::Started) { | |||
return Result::ErrorInvalidState; | |||
} | |||
return static_cast<Result>(mLibLoader->stream_getTimestamp(stream, clockId, | |||
framePosition, timeNanoseconds)); | |||
} else { | |||
return Result::ErrorNull; | |||
} | |||
} | |||
ResultWithValue<double> AudioStreamAAudio::calculateLatencyMillis() { | |||
AAudioStream *stream = mAAudioStream.load(); | |||
if (stream == nullptr) { | |||
return ResultWithValue<double>(Result::ErrorClosed); | |||
} | |||
// Get the time that a known audio frame was presented. | |||
int64_t hardwareFrameIndex; | |||
int64_t hardwareFrameHardwareTime; | |||
auto result = getTimestamp(CLOCK_MONOTONIC, | |||
&hardwareFrameIndex, | |||
&hardwareFrameHardwareTime); | |||
if (result != oboe::Result::OK) { | |||
return ResultWithValue<double>(static_cast<Result>(result)); | |||
} | |||
// Get counter closest to the app. | |||
bool isOutput = (getDirection() == oboe::Direction::Output); | |||
int64_t appFrameIndex = isOutput ? getFramesWritten() : getFramesRead(); | |||
// Assume that the next frame will be processed at the current time | |||
using namespace std::chrono; | |||
int64_t appFrameAppTime = | |||
duration_cast<nanoseconds>(steady_clock::now().time_since_epoch()).count(); | |||
// Calculate the number of frames between app and hardware | |||
int64_t frameIndexDelta = appFrameIndex - hardwareFrameIndex; | |||
// Calculate the time which the next frame will be or was presented | |||
int64_t frameTimeDelta = (frameIndexDelta * oboe::kNanosPerSecond) / getSampleRate(); | |||
int64_t appFrameHardwareTime = hardwareFrameHardwareTime + frameTimeDelta; | |||
// The current latency is the difference in time between when the current frame is at | |||
// the app and when it is at the hardware. | |||
double latencyNanos = static_cast<double>(isOutput | |||
? (appFrameHardwareTime - appFrameAppTime) // hardware is later | |||
: (appFrameAppTime - appFrameHardwareTime)); // hardware is earlier | |||
double latencyMillis = latencyNanos / kNanosPerMillisecond; | |||
return ResultWithValue<double>(latencyMillis); | |||
} | |||
bool AudioStreamAAudio::isMMapUsed() { | |||
AAudioStream *stream = mAAudioStream.load(); | |||
if (stream != nullptr) { | |||
return mLibLoader->stream_isMMapUsed(stream); | |||
} else { | |||
return false; | |||
} | |||
} | |||
} // namespace oboe |
@@ -1,123 +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_STREAM_AAUDIO_H_ | |||
#define OBOE_STREAM_AAUDIO_H_ | |||
#include <atomic> | |||
#include <mutex> | |||
#include <thread> | |||
#include "oboe/AudioStreamBuilder.h" | |||
#include "oboe/AudioStream.h" | |||
#include "oboe/Definitions.h" | |||
#include "AAudioLoader.h" | |||
namespace oboe { | |||
/** | |||
* Implementation of OboeStream that uses AAudio. | |||
* | |||
* Do not create this class directly. | |||
* Use an OboeStreamBuilder to create one. | |||
*/ | |||
class AudioStreamAAudio : public AudioStream { | |||
public: | |||
AudioStreamAAudio(); | |||
explicit AudioStreamAAudio(const AudioStreamBuilder &builder); | |||
virtual ~AudioStreamAAudio() = default; | |||
/** | |||
* | |||
* @return true if AAudio is supported on this device. | |||
*/ | |||
static bool isSupported(); | |||
// These functions override methods in AudioStream. | |||
// See AudioStream for documentation. | |||
Result open() override; | |||
Result close() override; | |||
Result requestStart() override; | |||
Result requestPause() override; | |||
Result requestFlush() override; | |||
Result requestStop() override; | |||
ResultWithValue<int32_t> write(const void *buffer, | |||
int32_t numFrames, | |||
int64_t timeoutNanoseconds) override; | |||
ResultWithValue<int32_t> read(void *buffer, | |||
int32_t numFrames, | |||
int64_t timeoutNanoseconds) override; | |||
ResultWithValue<int32_t> setBufferSizeInFrames(int32_t requestedFrames) override; | |||
int32_t getBufferSizeInFrames() override; | |||
int32_t getFramesPerBurst() override; | |||
ResultWithValue<int32_t> getXRunCount() const override; | |||
bool isXRunCountSupported() const override { return true; } | |||
ResultWithValue<double> calculateLatencyMillis() override; | |||
Result waitForStateChange(StreamState currentState, | |||
StreamState *nextState, | |||
int64_t timeoutNanoseconds) override; | |||
Result getTimestamp(clockid_t clockId, | |||
int64_t *framePosition, | |||
int64_t *timeNanoseconds) override; | |||
StreamState getState() const override; | |||
AudioApi getAudioApi() const override { | |||
return AudioApi::AAudio; | |||
} | |||
DataCallbackResult callOnAudioReady(AAudioStream *stream, | |||
void *audioData, | |||
int32_t numFrames); | |||
protected: | |||
static void internalErrorCallback( | |||
AAudioStream *stream, | |||
void *userData, | |||
aaudio_result_t error); | |||
void *getUnderlyingStream() const override { | |||
return mAAudioStream.load(); | |||
} | |||
void updateFramesRead() override; | |||
void updateFramesWritten() override; | |||
void logUnsupportedAttributes(); | |||
private: | |||
bool isMMapUsed(); | |||
std::atomic<bool> mCallbackThreadEnabled; | |||
// pointer to the underlying AAudio stream, valid if open, null if closed | |||
std::atomic<AAudioStream *> mAAudioStream{nullptr}; | |||
static AAudioLoader *mLibLoader; | |||
}; | |||
} // namespace oboe | |||
#endif // OBOE_STREAM_AAUDIO_H_ |
@@ -1,75 +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_AUDIO_CLOCK_H | |||
#define OBOE_AUDIO_CLOCK_H | |||
#include <sys/types.h> | |||
#include <ctime> | |||
#include "oboe/Definitions.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) { | |||
struct timespec time; | |||
int result = clock_gettime(clockId, &time); | |||
if (result < 0) { | |||
return result; | |||
} | |||
return (time.tv_sec * kNanosPerSecond) + time.tv_nsec; | |||
} | |||
/** | |||
* Sleep until the specified time. | |||
* | |||
* @param nanoTime time to wake up | |||
* @param clockId CLOCK_MONOTONIC is default | |||
* @return 0 or a negative error, eg. -EINTR | |||
*/ | |||
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); | |||
return 0 - clock_nanosleep(clockId, TIMER_ABSTIME, &time, NULL); | |||
} | |||
/** | |||
* Sleep for the specified number of nanoseconds in real-time. | |||
* Return immediately with 0 if a negative nanoseconds is specified. | |||
* | |||
* @param nanoseconds time to sleep | |||
* @param clockId CLOCK_REALTIME is default | |||
* @return 0 or a negative error, eg. -EINTR | |||
*/ | |||
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); | |||
return 0 - clock_nanosleep(clockId, 0, &time, NULL); | |||
} | |||
return 0; | |||
} | |||
}; | |||
} // namespace oboe | |||
#endif //OBOE_AUDIO_CLOCK_H |
@@ -1,38 +0,0 @@ | |||
/* | |||
* Copyright 2019 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 "AudioSourceCaller.h" | |||
using namespace oboe; | |||
using namespace flowgraph; | |||
int32_t AudioSourceCaller::onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) { | |||
oboe::AudioStreamCallback *callback = mStream->getCallback(); | |||
int32_t result = 0; | |||
int32_t numFrames = numBytes / mStream->getBytesPerFrame(); | |||
if (callback != nullptr) { | |||
DataCallbackResult callbackResult = callback->onAudioReady(mStream, buffer, numFrames); | |||
// onAudioReady() does not return the number of bytes processed so we have to assume all. | |||
result = (callbackResult == DataCallbackResult::Continue) | |||
? numBytes | |||
: -1; | |||
} else { | |||
auto readResult = mStream->read(buffer, numFrames, mTimeoutNanos); | |||
if (!readResult) return (int32_t) readResult.error(); | |||
result = readResult.value() * mStream->getBytesPerFrame(); | |||
} | |||
return result; | |||
} |
@@ -1,83 +0,0 @@ | |||
/* | |||
* Copyright 2019 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_SOURCE_CALLER_H | |||
#define OBOE_AUDIO_SOURCE_CALLER_H | |||
#include "OboeDebug.h" | |||
#include "oboe/Oboe.h" | |||
#include "flowgraph/FlowGraphNode.h" | |||
#include "FixedBlockReader.h" | |||
namespace oboe { | |||
class AudioStreamCallback; | |||
class AudioStream; | |||
/** | |||
* For output streams that use a callback, call the application for more data. | |||
* For input streams that do not use a callback, read from the stream. | |||
*/ | |||
class AudioSourceCaller : public flowgraph::FlowGraphSource, public FixedBlockProcessor { | |||
public: | |||
AudioSourceCaller(int32_t channelCount, int32_t framesPerCallback, int32_t bytesPerSample) | |||
: FlowGraphSource(channelCount) | |||
, mBlockReader(*this) { | |||
mBlockReader.open(channelCount * framesPerCallback * bytesPerSample); | |||
} | |||
/** | |||
* Set the stream to use as a source of data. | |||
* @param stream | |||
*/ | |||
void setStream(oboe::AudioStream *stream) { | |||
mStream = stream; | |||
} | |||
oboe::AudioStream *getStream() { | |||
return mStream; | |||
} | |||
/** | |||
* Timeout value to use when calling audioStream->read(). | |||
* @param timeoutNanos Zero for no timeout or time in nanoseconds. | |||
*/ | |||
void setTimeoutNanos(int64_t timeoutNanos) { | |||
mTimeoutNanos = timeoutNanos; | |||
} | |||
int64_t getTimeoutNanos() const { | |||
return mTimeoutNanos; | |||
} | |||
/** | |||
* Called internally for block size adaptation. | |||
* @param buffer | |||
* @param numBytes | |||
* @return | |||
*/ | |||
int32_t onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) override; | |||
protected: | |||
oboe::AudioStream *mStream = nullptr; | |||
int64_t mTimeoutNanos = 0; | |||
FixedBlockReader mBlockReader; | |||
}; | |||
} | |||
#endif //OBOE_AUDIO_SOURCE_CALLER_H |
@@ -1,211 +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 <sys/types.h> | |||
#include <pthread.h> | |||
#include <thread> | |||
#include <oboe/AudioStream.h> | |||
#include "OboeDebug.h" | |||
#include "AudioClock.h" | |||
#include <oboe/Utilities.h> | |||
namespace oboe { | |||
/* | |||
* AudioStream | |||
*/ | |||
AudioStream::AudioStream(const AudioStreamBuilder &builder) | |||
: AudioStreamBase(builder) { | |||
} | |||
Result AudioStream::close() { | |||
// Update local counters so they can be read after the close. | |||
updateFramesWritten(); | |||
updateFramesRead(); | |||
return Result::OK; | |||
} | |||
DataCallbackResult AudioStream::fireDataCallback(void *audioData, int32_t numFrames) { | |||
if (!isDataCallbackEnabled()) { | |||
LOGW("AudioStream::%s() called with data callback disabled!", __func__); | |||
return DataCallbackResult::Stop; // We should not be getting called any more. | |||
} | |||
// TODO remove | |||
int scheduler = sched_getscheduler(0) & ~SCHED_RESET_ON_FORK; // for current thread | |||
if (scheduler != mPreviousScheduler) { | |||
LOGD("AudioStream::%s() scheduler = %s", __func__, | |||
((scheduler == SCHED_FIFO) ? "SCHED_FIFO" : | |||
((scheduler == SCHED_OTHER) ? "SCHED_OTHER" : | |||
((scheduler == SCHED_RR) ? "SCHED_RR" : "UNKNOWN"))) | |||
); | |||
mPreviousScheduler = scheduler; | |||
} | |||
DataCallbackResult result; | |||
if (mStreamCallback == nullptr) { | |||
result = onDefaultCallback(audioData, numFrames); | |||
} else { | |||
result = mStreamCallback->onAudioReady(this, audioData, numFrames); | |||
} | |||
// On Oreo, we might get called after returning stop. | |||
// So block that here. | |||
setDataCallbackEnabled(result == DataCallbackResult::Continue); | |||
return result; | |||
} | |||
Result AudioStream::waitForStateTransition(StreamState startingState, | |||
StreamState endingState, | |||
int64_t timeoutNanoseconds) | |||
{ | |||
StreamState state; | |||
{ | |||
std::lock_guard<std::mutex> lock(mLock); | |||
state = getState(); | |||
if (state == StreamState::Closed) { | |||
return Result::ErrorClosed; | |||
} else if (state == StreamState::Disconnected) { | |||
return Result::ErrorDisconnected; | |||
} | |||
} | |||
StreamState nextState = state; | |||
// TODO Should this be a while()?! | |||
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; | |||
if (timeoutNanoseconds <= 0) return result; | |||
return waitForStateTransition(StreamState::Starting, | |||
StreamState::Started, timeoutNanoseconds); | |||
} | |||
Result AudioStream::pause(int64_t timeoutNanoseconds) | |||
{ | |||
Result result = requestPause(); | |||
if (result != Result::OK) return result; | |||
if (timeoutNanoseconds <= 0) return result; | |||
return waitForStateTransition(StreamState::Pausing, | |||
StreamState::Paused, timeoutNanoseconds); | |||
} | |||
Result AudioStream::flush(int64_t timeoutNanoseconds) | |||
{ | |||
Result result = requestFlush(); | |||
if (result != Result::OK) return result; | |||
if (timeoutNanoseconds <= 0) return result; | |||
return waitForStateTransition(StreamState::Flushing, | |||
StreamState::Flushed, timeoutNanoseconds); | |||
} | |||
Result AudioStream::stop(int64_t timeoutNanoseconds) | |||
{ | |||
Result result = requestStop(); | |||
if (result != Result::OK) return result; | |||
if (timeoutNanoseconds <= 0) return result; | |||
return waitForStateTransition(StreamState::Stopping, | |||
StreamState::Stopped, timeoutNanoseconds); | |||
} | |||
int32_t AudioStream::getBytesPerSample() const { | |||
return convertFormatToSizeInBytes(mFormat); | |||
} | |||
int64_t AudioStream::getFramesRead() { | |||
updateFramesRead(); | |||
return mFramesRead; | |||
} | |||
int64_t AudioStream::getFramesWritten() { | |||
updateFramesWritten(); | |||
return mFramesWritten; | |||
} | |||
ResultWithValue<int32_t> AudioStream::getAvailableFrames() { | |||
int64_t readCounter = getFramesRead(); | |||
if (readCounter < 0) return ResultWithValue<int32_t>::createBasedOnSign(readCounter); | |||
int64_t writeCounter = getFramesWritten(); | |||
if (writeCounter < 0) return ResultWithValue<int32_t>::createBasedOnSign(writeCounter); | |||
int32_t framesAvailable = writeCounter - readCounter; | |||
return ResultWithValue<int32_t>(framesAvailable); | |||
} | |||
ResultWithValue<int32_t> AudioStream::waitForAvailableFrames(int32_t numFrames, | |||
int64_t timeoutNanoseconds) { | |||
if (numFrames == 0) return Result::OK; | |||
if (numFrames < 0) return Result::ErrorOutOfRange; | |||
int64_t framesAvailable = 0; | |||
int64_t burstInNanos = getFramesPerBurst() * kNanosPerSecond / getSampleRate(); | |||
bool ready = false; | |||
int64_t deadline = AudioClock::getNanoseconds() + timeoutNanoseconds; | |||
do { | |||
ResultWithValue<int32_t> result = getAvailableFrames(); | |||
if (!result) return result; | |||
framesAvailable = result.value(); | |||
ready = (framesAvailable >= numFrames); | |||
if (!ready) { | |||
int64_t now = AudioClock::getNanoseconds(); | |||
if (now > deadline) break; | |||
AudioClock::sleepForNanos(burstInNanos); | |||
} | |||
} while (!ready); | |||
return (!ready) | |||
? ResultWithValue<int32_t>(Result::ErrorTimeout) | |||
: ResultWithValue<int32_t>(framesAvailable); | |||
} | |||
ResultWithValue<FrameTimestamp> AudioStream::getTimestamp(clockid_t clockId) { | |||
FrameTimestamp frame; | |||
Result result = getTimestamp(clockId, &frame.position, &frame.timestamp); | |||
if (result == Result::OK){ | |||
return ResultWithValue<FrameTimestamp>(frame); | |||
} else { | |||
return ResultWithValue<FrameTimestamp>(static_cast<Result>(result)); | |||
} | |||
} | |||
static void oboe_stop_thread_proc(AudioStream *oboeStream) { | |||
LOGD("%s() called ----)))))", __func__); | |||
if (oboeStream != nullptr) { | |||
oboeStream->requestStop(); | |||
} | |||
LOGD("%s() returning (((((----", __func__); | |||
} | |||
void AudioStream::launchStopThread() { | |||
// Stop this stream on a separate thread | |||
std::thread t(oboe_stop_thread_proc, this); | |||
t.detach(); | |||
} | |||
} // namespace oboe |
@@ -1,187 +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 <sys/types.h> | |||
#include "aaudio/AudioStreamAAudio.h" | |||
#include "FilterAudioStream.h" | |||
#include "OboeDebug.h" | |||
#include "oboe/Oboe.h" | |||
#include "oboe/AudioStreamBuilder.h" | |||
#include "opensles/AudioInputStreamOpenSLES.h" | |||
#include "opensles/AudioOutputStreamOpenSLES.h" | |||
#include "opensles/AudioStreamOpenSLES.h" | |||
#include "QuirksManager.h" | |||
namespace oboe { | |||
/** | |||
* The following default values are used when oboe does not have any better way of determining the optimal values | |||
* for an audio stream. This can happen when: | |||
* | |||
* - Client is creating a stream on API < 26 (OpenSLES) but has not supplied the optimal sample | |||
* rate and/or frames per burst | |||
* - Client is creating a stream on API 16 (OpenSLES) where AudioManager.PROPERTY_OUTPUT_* values | |||
* are not available | |||
*/ | |||
int32_t DefaultStreamValues::SampleRate = 48000; // Common rate for mobile audio and video | |||
int32_t DefaultStreamValues::FramesPerBurst = 192; // 4 msec at 48000 Hz | |||
int32_t DefaultStreamValues::ChannelCount = 2; // Stereo | |||
constexpr int kBufferSizeInBurstsForLowLatencyStreams = 2; | |||
#ifndef OBOE_ENABLE_AAUDIO | |||
// Set OBOE_ENABLE_AAUDIO to 0 if you want to disable the AAudio API. | |||
// This might be useful if you want to force all the unit tests to use OpenSL ES. | |||
#define OBOE_ENABLE_AAUDIO 1 | |||
#endif | |||
bool AudioStreamBuilder::isAAudioSupported() { | |||
return AudioStreamAAudio::isSupported() && OBOE_ENABLE_AAUDIO; | |||
} | |||
bool AudioStreamBuilder::isAAudioRecommended() { | |||
// See https://github.com/google/oboe/issues/40, | |||
// AAudio may not be stable on Android O, depending on how it is used. | |||
// To be safe, use AAudio only on O_MR1 and above. | |||
return (getSdkVersion() >= __ANDROID_API_O_MR1__) && isAAudioSupported(); | |||
} | |||
AudioStream *AudioStreamBuilder::build() { | |||
AudioStream *stream = nullptr; | |||
if (isAAudioRecommended() && mAudioApi != AudioApi::OpenSLES) { | |||
stream = new AudioStreamAAudio(*this); | |||
} else if (isAAudioSupported() && mAudioApi == AudioApi::AAudio) { | |||
stream = new AudioStreamAAudio(*this); | |||
LOGE("Creating AAudio stream on 8.0 because it was specified. This is error prone."); | |||
} else { | |||
if (getDirection() == oboe::Direction::Output) { | |||
stream = new AudioOutputStreamOpenSLES(*this); | |||
} else if (getDirection() == oboe::Direction::Input) { | |||
stream = new AudioInputStreamOpenSLES(*this); | |||
} | |||
} | |||
return stream; | |||
} | |||
bool AudioStreamBuilder::isCompatible(AudioStreamBase &other) { | |||
return getSampleRate() == other.getSampleRate() | |||
&& getFormat() == other.getFormat() | |||
&& getChannelCount() == other.getChannelCount(); | |||
} | |||
Result AudioStreamBuilder::openStream(AudioStream **streamPP) { | |||
Result result = Result::OK; | |||
LOGD("%s() %s -------- %s --------", | |||
__func__, getDirection() == Direction::Input ? "INPUT" : "OUTPUT", getVersionText()); | |||
if (streamPP == nullptr) { | |||
return Result::ErrorNull; | |||
} | |||
*streamPP = nullptr; | |||
AudioStream *streamP = nullptr; | |||
// Maybe make a FilterInputStream. | |||
AudioStreamBuilder childBuilder(*this); | |||
// Check need for conversion and modify childBuilder for optimal stream. | |||
bool conversionNeeded = QuirksManager::getInstance().isConversionNeeded(*this, childBuilder); | |||
// Do we need to make a child stream and convert. | |||
if (conversionNeeded) { | |||
AudioStream *tempStream; | |||
result = childBuilder.openStream(&tempStream); | |||
if (result != Result::OK) { | |||
return result; | |||
} | |||
if (isCompatible(*tempStream)) { | |||
// Everything matches so we can just use the child stream directly. | |||
*streamPP = tempStream; | |||
return result; | |||
} else { | |||
AudioStreamBuilder parentBuilder = *this; | |||
// Build a stream that is as close as possible to the childStream. | |||
if (getFormat() == oboe::AudioFormat::Unspecified) { | |||
parentBuilder.setFormat(tempStream->getFormat()); | |||
} | |||
if (getChannelCount() == oboe::Unspecified) { | |||
parentBuilder.setChannelCount(tempStream->getChannelCount()); | |||
} | |||
if (getSampleRate() == oboe::Unspecified) { | |||
parentBuilder.setSampleRate(tempStream->getSampleRate()); | |||
} | |||
// Use childStream in a FilterAudioStream. | |||
LOGD("%s() create a FilterAudioStream for data conversion.", __func__); | |||
FilterAudioStream *filterStream = new FilterAudioStream(parentBuilder, tempStream); | |||
result = filterStream->configureFlowGraph(); | |||
if (result != Result::OK) { | |||
filterStream->close(); | |||
delete filterStream; | |||
// Just open streamP the old way. | |||
} else { | |||
streamP = static_cast<AudioStream *>(filterStream); | |||
} | |||
} | |||
} | |||
if (streamP == nullptr) { | |||
streamP = build(); | |||
if (streamP == nullptr) { | |||
return Result::ErrorNull; | |||
} | |||
} | |||
result = streamP->open(); // TODO review API | |||
if (result == Result::OK) { | |||
int32_t optimalBufferSize = -1; | |||
// Use a reasonable default buffer size. | |||
if (streamP->getDirection() == Direction::Input) { | |||
// For input, small size does not improve latency because the stream is usually | |||
// run close to empty. And a low size can result in XRuns so always use the maximum. | |||
optimalBufferSize = streamP->getBufferCapacityInFrames(); | |||
} else if (streamP->getPerformanceMode() == PerformanceMode::LowLatency | |||
&& streamP->getDirection() == Direction::Output) { // Output check is redundant. | |||
optimalBufferSize = streamP->getFramesPerBurst() * | |||
kBufferSizeInBurstsForLowLatencyStreams; | |||
} | |||
if (optimalBufferSize >= 0) { | |||
auto setBufferResult = streamP->setBufferSizeInFrames(optimalBufferSize); | |||
if (!setBufferResult) { | |||
LOGW("Failed to setBufferSizeInFrames(%d). Error was %s", | |||
optimalBufferSize, | |||
convertToText(setBufferResult.error())); | |||
} | |||
} | |||
*streamPP = streamP; | |||
} else { | |||
delete streamP; | |||
} | |||
return result; | |||
} | |||
Result AudioStreamBuilder::openManagedStream(oboe::ManagedStream &stream) { | |||
stream.reset(); | |||
AudioStream *streamptr; | |||
auto result = openStream(&streamptr); | |||
stream.reset(streamptr); | |||
return result; | |||
} | |||
} // namespace oboe |
@@ -1,210 +0,0 @@ | |||
/* | |||
* Copyright (C) 2019 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 <memory> | |||
#include "OboeDebug.h" | |||
#include "DataConversionFlowGraph.h" | |||
#include "SourceFloatCaller.h" | |||
#include "SourceI16Caller.h" | |||
#include <flowgraph/ClipToRange.h> | |||
#include <flowgraph/MonoToMultiConverter.h> | |||
#include <flowgraph/RampLinear.h> | |||
#include <flowgraph/SinkFloat.h> | |||
#include <flowgraph/SinkI16.h> | |||
#include <flowgraph/SinkI24.h> | |||
#include <flowgraph/SourceFloat.h> | |||
#include <flowgraph/SourceI16.h> | |||
#include <flowgraph/SourceI24.h> | |||
#include <flowgraph/SampleRateConverter.h> | |||
using namespace oboe; | |||
using namespace flowgraph; | |||
using namespace resampler; | |||
void DataConversionFlowGraph::setSource(const void *buffer, int32_t numFrames) { | |||
mSource->setData(buffer, numFrames); | |||
} | |||
static MultiChannelResampler::Quality convertOboeSRQualityToMCR(SampleRateConversionQuality quality) { | |||
switch (quality) { | |||
case SampleRateConversionQuality::Fastest: | |||
return MultiChannelResampler::Quality::Fastest; | |||
case SampleRateConversionQuality::Low: | |||
return MultiChannelResampler::Quality::Low; | |||
default: | |||
case SampleRateConversionQuality::Medium: | |||
return MultiChannelResampler::Quality::Medium; | |||
case SampleRateConversionQuality::High: | |||
return MultiChannelResampler::Quality::High; | |||
case SampleRateConversionQuality::Best: | |||
return MultiChannelResampler::Quality::Best; | |||
} | |||
} | |||
// Chain together multiple processors. | |||
// Callback Output | |||
// Use SourceCaller that calls original app callback from the flowgraph. | |||
// The child callback from FilteredAudioStream read()s from the flowgraph. | |||
// Callback Input | |||
// Child callback from FilteredAudioStream writes()s to the flowgraph. | |||
// The output of the flowgraph goes through a BlockWriter to the app callback. | |||
// Blocking Write | |||
// Write buffer is set on an AudioSource. | |||
// Data is pulled through the graph and written to the child stream. | |||
// Blocking Read | |||
// Reads in a loop from the flowgraph Sink to fill the read buffer. | |||
// A SourceCaller then does a blocking read from the child Stream. | |||
// | |||
Result DataConversionFlowGraph::configure(AudioStream *sourceStream, AudioStream *sinkStream) { | |||
FlowGraphPortFloatOutput *lastOutput = nullptr; | |||
bool isOutput = sourceStream->getDirection() == Direction::Output; | |||
mFilterStream = isOutput ? sourceStream : sinkStream; | |||
AudioFormat sourceFormat = sourceStream->getFormat(); | |||
int32_t sourceChannelCount = sourceStream->getChannelCount(); | |||
int32_t sourceSampleRate = sourceStream->getSampleRate(); | |||
AudioFormat sinkFormat = sinkStream->getFormat(); | |||
int32_t sinkChannelCount = sinkStream->getChannelCount(); | |||
int32_t sinkSampleRate = sinkStream->getSampleRate(); | |||
LOGD("%s() flowgraph converts channels: %d to %d, format: %d to %d, rate: %d to %d, qual = %d", | |||
__func__, | |||
sourceChannelCount, sinkChannelCount, | |||
sourceFormat, sinkFormat, | |||
sourceSampleRate, sinkSampleRate, | |||
sourceStream->getSampleRateConversionQuality()); | |||
int32_t framesPerCallback = (sourceStream->getFramesPerCallback() == kUnspecified) | |||
? sourceStream->getFramesPerBurst() | |||
: sourceStream->getFramesPerCallback(); | |||
// Source | |||
if ((sourceStream->getCallback() != nullptr && isOutput) | |||
|| (sourceStream->getCallback() == nullptr && !isOutput)) { | |||
switch (sourceFormat) { | |||
case AudioFormat::Float: | |||
mSourceCaller = std::make_unique<SourceFloatCaller>(sourceChannelCount, | |||
framesPerCallback); | |||
break; | |||
case AudioFormat::I16: | |||
mSourceCaller = std::make_unique<SourceI16Caller>(sourceChannelCount, | |||
framesPerCallback); | |||
break; | |||
default: | |||
LOGE("%s() Unsupported source caller format = %d", __func__, sourceFormat); | |||
return Result::ErrorIllegalArgument; | |||
} | |||
mSourceCaller->setStream(sourceStream); | |||
lastOutput = &mSourceCaller->output; | |||
} else { | |||
switch (sourceFormat) { | |||
case AudioFormat::Float: | |||
mSource = std::make_unique<SourceFloat>(sourceChannelCount); | |||
break; | |||
case AudioFormat::I16: | |||
mSource = std::make_unique<SourceI16>(sourceChannelCount); | |||
break; | |||
default: | |||
LOGE("%s() Unsupported source format = %d", __func__, sourceFormat); | |||
return Result::ErrorIllegalArgument; | |||
} | |||
if (!isOutput) { | |||
// The BlockWriter is after the Sink so use the SinkStream size. | |||
mBlockWriter.open(framesPerCallback * sinkStream->getBytesPerFrame()); | |||
mAppBuffer = std::make_unique<uint8_t[]>( | |||
kDefaultBufferSize * sinkStream->getBytesPerFrame()); | |||
} | |||
lastOutput = &mSource->output; | |||
} | |||
// Sample Rate conversion | |||
if (sourceSampleRate != sinkSampleRate) { | |||
mResampler.reset(MultiChannelResampler::make(sourceChannelCount, | |||
sourceSampleRate, | |||
sinkSampleRate, | |||
convertOboeSRQualityToMCR( | |||
sourceStream->getSampleRateConversionQuality()))); | |||
mRateConverter = std::make_unique<SampleRateConverter>(sourceChannelCount, | |||
*mResampler.get()); | |||
lastOutput->connect(&mRateConverter->input); | |||
lastOutput = &mRateConverter->output; | |||
} | |||
// Expand the number of channels if required. | |||
if (sourceChannelCount == 1 && sinkChannelCount > 1) { | |||
mChannelConverter = std::make_unique<MonoToMultiConverter>(sinkChannelCount); | |||
lastOutput->connect(&mChannelConverter->input); | |||
lastOutput = &mChannelConverter->output; | |||
} else if (sourceChannelCount != sinkChannelCount) { | |||
LOGW("%s() Channel reduction not supported.", __func__); | |||
return Result::ErrorUnimplemented; // TODO | |||
} | |||
// Sink | |||
switch (sinkFormat) { | |||
case AudioFormat::Float: | |||
mSink = std::make_unique<SinkFloat>(sinkChannelCount); | |||
break; | |||
case AudioFormat::I16: | |||
mSink = std::make_unique<SinkI16>(sinkChannelCount); | |||
break; | |||
default: | |||
LOGE("%s() Unsupported sink format = %d", __func__, sinkFormat); | |||
return Result::ErrorIllegalArgument;; | |||
} | |||
lastOutput->connect(&mSink->input); | |||
mFramePosition = 0; | |||
return Result::OK; | |||
} | |||
int32_t DataConversionFlowGraph::read(void *buffer, int32_t numFrames, int64_t timeoutNanos) { | |||
if (mSourceCaller) { | |||
mSourceCaller->setTimeoutNanos(timeoutNanos); | |||
} | |||
int32_t numRead = mSink->read(mFramePosition, buffer, numFrames); | |||
mFramePosition += numRead; | |||
return numRead; | |||
} | |||
// This is similar to pushing data through the flowgraph. | |||
int32_t DataConversionFlowGraph::write(void *inputBuffer, int32_t numFrames) { | |||
// Put the data from the Source at the head of the flowgraph. | |||
mSource->setData(inputBuffer, numFrames); | |||
while (true) { | |||
// Pull and read some data in app format into a small buffer. | |||
int32_t framesRead = mSink->read(mFramePosition, mAppBuffer.get(), flowgraph::kDefaultBufferSize); | |||
mFramePosition += framesRead; | |||
if (framesRead <= 0) break; | |||
// Write to a block adapter, which will call the app whenever it has enough data. | |||
int32_t bytesRead = mBlockWriter.write(mAppBuffer.get(), | |||
framesRead * mFilterStream->getBytesPerFrame()); | |||
if (bytesRead < 0) return bytesRead; // TODO review | |||
} | |||
return numFrames; | |||
} | |||
int32_t DataConversionFlowGraph::onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) { | |||
int32_t numFrames = numBytes / mFilterStream->getBytesPerFrame(); | |||
mCallbackResult = mFilterStream->getCallback()->onAudioReady(mFilterStream, buffer, numFrames); | |||
// TODO handle STOP from callback, process data remaining in the block adapter | |||
return numBytes; | |||
} |
@@ -1,84 +0,0 @@ | |||
/* | |||
* Copyright (C) 2019 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_FLOW_GRAPH_H | |||
#define OBOE_OBOE_FLOW_GRAPH_H | |||
#include <memory> | |||
#include <stdint.h> | |||
#include <sys/types.h> | |||
#include <flowgraph/MonoToMultiConverter.h> | |||
#include <flowgraph/SampleRateConverter.h> | |||
#include <oboe/Definitions.h> | |||
#include "AudioSourceCaller.h" | |||
#include "FixedBlockWriter.h" | |||
namespace oboe { | |||
class AudioStream; | |||
class AudioSourceCaller; | |||
/** | |||
* Convert PCM channels, format and sample rate for optimal latency. | |||
*/ | |||
class DataConversionFlowGraph : public FixedBlockProcessor { | |||
public: | |||
DataConversionFlowGraph() | |||
: mBlockWriter(*this) {} | |||
void setSource(const void *buffer, int32_t numFrames); | |||
/** Connect several modules together to convert from source to sink. | |||
* This should only be called once for each instance. | |||
* | |||
* @param sourceFormat | |||
* @param sourceChannelCount | |||
* @param sinkFormat | |||
* @param sinkChannelCount | |||
* @return | |||
*/ | |||
oboe::Result configure(oboe::AudioStream *sourceStream, oboe::AudioStream *sinkStream); | |||
int32_t read(void *buffer, int32_t numFrames, int64_t timeoutNanos); | |||
int32_t write(void *buffer, int32_t numFrames); | |||
int32_t onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) override; | |||
DataCallbackResult getDataCallbackResult() { | |||
return mCallbackResult; | |||
} | |||
private: | |||
std::unique_ptr<flowgraph::FlowGraphSourceBuffered> mSource; | |||
std::unique_ptr<AudioSourceCaller> mSourceCaller; | |||
std::unique_ptr<flowgraph::MonoToMultiConverter> mChannelConverter; | |||
std::unique_ptr<resampler::MultiChannelResampler> mResampler; | |||
std::unique_ptr<flowgraph::SampleRateConverter> mRateConverter; | |||
std::unique_ptr<flowgraph::FlowGraphSink> mSink; | |||
FixedBlockWriter mBlockWriter; | |||
DataCallbackResult mCallbackResult = DataCallbackResult::Continue; | |||
AudioStream *mFilterStream = nullptr; | |||
std::unique_ptr<uint8_t[]> mAppBuffer; | |||
int64_t mFramePosition = 0; | |||
}; | |||
} | |||
#endif //OBOE_OBOE_FLOW_GRAPH_H |
@@ -1,72 +0,0 @@ | |||
/* | |||
* Copyright 2019 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 <memory> | |||
#include "FilterAudioStream.h" | |||
using namespace oboe; | |||
using namespace flowgraph; | |||
Result FilterAudioStream::configureFlowGraph() { | |||
mFlowGraph = std::make_unique<DataConversionFlowGraph>(); | |||
bool isOutput = getDirection() == Direction::Output; | |||
AudioStream *sourceStream = isOutput ? this : mChildStream.get(); | |||
AudioStream *sinkStream = isOutput ? mChildStream.get() : this; | |||
mRateScaler = ((double) sourceStream->getSampleRate()) / sinkStream->getSampleRate(); | |||
return mFlowGraph->configure(sourceStream, sinkStream); | |||
} | |||
// Put the data to be written at the source end of the flowgraph. | |||
// Then read (pull) the data from the flowgraph and write it to the | |||
// child stream. | |||
ResultWithValue<int32_t> FilterAudioStream::write(const void *buffer, | |||
int32_t numFrames, | |||
int64_t timeoutNanoseconds) { | |||
int32_t framesWritten = 0; | |||
mFlowGraph->setSource(buffer, numFrames); | |||
while (true) { | |||
int32_t numRead = mFlowGraph->read(mBlockingBuffer.get(), | |||
getFramesPerBurst(), | |||
timeoutNanoseconds); | |||
if (numRead < 0) { | |||
return ResultWithValue<int32_t>::createBasedOnSign(numRead); | |||
} | |||
if (numRead == 0) { | |||
break; // finished processing the source buffer | |||
} | |||
auto writeResult = mChildStream->write(mBlockingBuffer.get(), | |||
numRead, | |||
timeoutNanoseconds); | |||
if (!writeResult) { | |||
return writeResult; | |||
} | |||
framesWritten += writeResult.value(); | |||
} | |||
return ResultWithValue<int32_t>::createBasedOnSign(framesWritten); | |||
} | |||
// Read (pull) the data we want from the sink end of the flowgraph. | |||
// The necessary data will be read from the child stream using a flowgraph callback. | |||
ResultWithValue<int32_t> FilterAudioStream::read(void *buffer, | |||
int32_t numFrames, | |||
int64_t timeoutNanoseconds) { | |||
int32_t framesRead = mFlowGraph->read(buffer, numFrames, timeoutNanoseconds); | |||
return ResultWithValue<int32_t>::createBasedOnSign(framesRead); | |||
} |
@@ -1,204 +0,0 @@ | |||
/* | |||
* Copyright 2019 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_FILTER_AUDIO_STREAM_H | |||
#define OBOE_FILTER_AUDIO_STREAM_H | |||
#include <memory> | |||
#include <oboe/AudioStream.h> | |||
#include "DataConversionFlowGraph.h" | |||
namespace oboe { | |||
/** | |||
* An AudioStream that wraps another AudioStream and provides audio data conversion. | |||
* Operations may include channel conversion, data format conversion and/or sample rate conversion. | |||
*/ | |||
class FilterAudioStream : public AudioStream, AudioStreamCallback { | |||
public: | |||
/** | |||
* Construct an `AudioStream` using the given `AudioStreamBuilder` and a child AudioStream. | |||
* | |||
* This should only be called internally by AudioStreamBuilder. | |||
* Ownership of childStream will be passed to this object. | |||
* | |||
* @param builder containing all the stream's attributes | |||
*/ | |||
FilterAudioStream(const AudioStreamBuilder &builder, AudioStream *childStream) | |||
: AudioStream(builder) | |||
, mChildStream(childStream) { | |||
// Intercept the callback if used. | |||
if (builder.getCallback() != nullptr) { | |||
mStreamCallback = mChildStream->swapCallback(this); | |||
} else { | |||
const int size = childStream->getFramesPerBurst() * childStream->getBytesPerFrame(); | |||
mBlockingBuffer = std::make_unique<uint8_t[]>(size); | |||
} | |||
// Copy parameters that may not match builder. | |||
mBufferCapacityInFrames = mChildStream->getBufferCapacityInFrames(); | |||
mPerformanceMode = mChildStream->getPerformanceMode(); | |||
} | |||
virtual ~FilterAudioStream() = default; | |||
AudioStream *getChildStream() const { | |||
return mChildStream.get(); | |||
} | |||
Result configureFlowGraph(); | |||
// Close child and parent. | |||
Result close() override { | |||
const Result result1 = mChildStream->close(); | |||
const Result result2 = AudioStream::close(); | |||
return (result1 != Result::OK ? result1 : result2); | |||
} | |||
/** | |||
* Start the stream asynchronously. Returns immediately (does not block). Equivalent to calling | |||
* `start(0)`. | |||
*/ | |||
Result requestStart() override { | |||
return mChildStream->requestStart(); | |||
} | |||
/** | |||
* Pause the stream asynchronously. Returns immediately (does not block). Equivalent to calling | |||
* `pause(0)`. | |||
*/ | |||
Result requestPause() override { | |||
return mChildStream->requestPause(); | |||
} | |||
/** | |||
* Flush the stream asynchronously. Returns immediately (does not block). Equivalent to calling | |||
* `flush(0)`. | |||
*/ | |||
Result requestFlush() override { | |||
return mChildStream->requestFlush(); | |||
} | |||
/** | |||
* Stop the stream asynchronously. Returns immediately (does not block). Equivalent to calling | |||
* `stop(0)`. | |||
*/ | |||
Result requestStop() override { | |||
return mChildStream->requestStop(); | |||
} | |||
ResultWithValue<int32_t> read(void *buffer, | |||
int32_t numFrames, | |||
int64_t timeoutNanoseconds) override; | |||
ResultWithValue<int32_t> write(const void *buffer, | |||
int32_t numFrames, | |||
int64_t timeoutNanoseconds) override; | |||
StreamState getState() const override { | |||
return mChildStream->getState(); | |||
} | |||
Result waitForStateChange( | |||
StreamState inputState, | |||
StreamState *nextState, | |||
int64_t timeoutNanoseconds) override { | |||
return mChildStream->waitForStateChange(inputState, nextState, timeoutNanoseconds); | |||
} | |||
bool isXRunCountSupported() const override { | |||
return mChildStream->isXRunCountSupported(); | |||
} | |||
int32_t getFramesPerBurst() override { | |||
return mChildStream->getFramesPerBurst(); | |||
} | |||
AudioApi getAudioApi() const override { | |||
return mChildStream->getAudioApi(); | |||
} | |||
void updateFramesWritten() override { | |||
// TODO for output, just count local writes? | |||
mFramesWritten = static_cast<int64_t>(mChildStream->getFramesWritten() * mRateScaler); | |||
} | |||
void updateFramesRead() override { | |||
// TODO for input, just count local reads? | |||
mFramesRead = static_cast<int64_t>(mChildStream->getFramesRead() * mRateScaler); | |||
} | |||
void *getUnderlyingStream() const override { | |||
return mChildStream->getUnderlyingStream(); | |||
} | |||
ResultWithValue<int32_t> setBufferSizeInFrames(int32_t requestedFrames) override { | |||
return mChildStream->setBufferSizeInFrames(requestedFrames); | |||
} | |||
int32_t getBufferSizeInFrames() override { | |||
mBufferSizeInFrames = mChildStream->getBufferSizeInFrames(); | |||
return mBufferSizeInFrames; | |||
} | |||
ResultWithValue<int32_t> getXRunCount() const override { | |||
return mChildStream->getXRunCount(); | |||
} | |||
ResultWithValue<double> calculateLatencyMillis() override { | |||
// This will automatically include the latency of the flowgraph? | |||
return mChildStream->calculateLatencyMillis(); | |||
} | |||
Result getTimestamp(clockid_t clockId, | |||
int64_t *framePosition, | |||
int64_t *timeNanoseconds) override { | |||
int64_t childPosition = 0; | |||
Result result = mChildStream->getTimestamp(clockId, &childPosition, timeNanoseconds); | |||
*framePosition = childPosition * mRateScaler; | |||
return result; | |||
} | |||
DataCallbackResult onAudioReady(AudioStream *oboeStream, | |||
void *audioData, | |||
int32_t numFrames) override { | |||
int32_t framesProcessed; | |||
if (oboeStream->getDirection() == Direction::Output) { | |||
framesProcessed = mFlowGraph->read(audioData, numFrames, 0 /* timeout */); | |||
} else { | |||
framesProcessed = mFlowGraph->write(audioData, numFrames); | |||
} | |||
return (framesProcessed < numFrames) | |||
? DataCallbackResult::Stop | |||
: mFlowGraph->getDataCallbackResult(); | |||
} | |||
void onErrorBeforeClose(AudioStream *oboeStream, Result error) override {} | |||
void onErrorAfterClose(AudioStream *oboeStream, Result error) override {} | |||
private: | |||
std::unique_ptr<AudioStream> mChildStream; // this stream wraps the child stream | |||
std::unique_ptr<DataConversionFlowGraph> mFlowGraph; // for converting data | |||
std::unique_ptr<uint8_t[]> mBlockingBuffer; // temp buffer for write() | |||
double mRateScaler = 1.0; // ratio parent/child sample rates | |||
}; | |||
} // oboe | |||
#endif //OBOE_FILTER_AUDIO_STREAM_H |
@@ -1,38 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
#include <stdint.h> | |||
#include "FixedBlockAdapter.h" | |||
FixedBlockAdapter::~FixedBlockAdapter() { | |||
} | |||
int32_t FixedBlockAdapter::open(int32_t bytesPerFixedBlock) | |||
{ | |||
mSize = bytesPerFixedBlock; | |||
mStorage = std::make_unique<uint8_t[]>(bytesPerFixedBlock); | |||
mPosition = 0; | |||
return 0; | |||
} | |||
int32_t FixedBlockAdapter::close() | |||
{ | |||
mStorage.reset(nullptr); | |||
mSize = 0; | |||
mPosition = 0; | |||
return 0; | |||
} |
@@ -1,66 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
#ifndef AAUDIO_FIXED_BLOCK_ADAPTER_H | |||
#define AAUDIO_FIXED_BLOCK_ADAPTER_H | |||
#include <memory> | |||
#include <stdint.h> | |||
/** | |||
* Interface for a class that needs fixed-size blocks. | |||
*/ | |||
class FixedBlockProcessor { | |||
public: | |||
virtual ~FixedBlockProcessor() = default; | |||
/** | |||
* | |||
* @param buffer Pointer to first byte of data. | |||
* @param numBytes This will be a fixed size specified in FixedBlockAdapter::open(). | |||
* @return Number of bytes processed or a negative error code. | |||
*/ | |||
virtual int32_t onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) = 0; | |||
}; | |||
/** | |||
* Base class for a variable-to-fixed-size block adapter. | |||
*/ | |||
class FixedBlockAdapter | |||
{ | |||
public: | |||
FixedBlockAdapter(FixedBlockProcessor &fixedBlockProcessor) | |||
: mFixedBlockProcessor(fixedBlockProcessor) {} | |||
virtual ~FixedBlockAdapter(); | |||
/** | |||
* Allocate internal resources needed for buffering data. | |||
*/ | |||
virtual int32_t open(int32_t bytesPerFixedBlock); | |||
/** | |||
* Free internal resources. | |||
*/ | |||
int32_t close(); | |||
protected: | |||
FixedBlockProcessor &mFixedBlockProcessor; | |||
std::unique_ptr<uint8_t[]> mStorage; // Store data here while assembling buffers. | |||
int32_t mSize = 0; // Size in bytes of the fixed size buffer. | |||
int32_t mPosition = 0; // Offset of the last byte read or written. | |||
}; | |||
#endif /* AAUDIO_FIXED_BLOCK_ADAPTER_H */ |
@@ -1,73 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
#include <stdint.h> | |||
#include <memory.h> | |||
#include "FixedBlockAdapter.h" | |||
#include "FixedBlockReader.h" | |||
FixedBlockReader::FixedBlockReader(FixedBlockProcessor &fixedBlockProcessor) | |||
: FixedBlockAdapter(fixedBlockProcessor) { | |||
mPosition = mSize; | |||
} | |||
int32_t FixedBlockReader::open(int32_t bytesPerFixedBlock) { | |||
int32_t result = FixedBlockAdapter::open(bytesPerFixedBlock); | |||
mPosition = 0; | |||
mValid = 0; | |||
return result; | |||
} | |||
int32_t FixedBlockReader::readFromStorage(uint8_t *buffer, int32_t numBytes) { | |||
int32_t bytesToRead = numBytes; | |||
int32_t dataAvailable = mValid - mPosition; | |||
if (bytesToRead > dataAvailable) { | |||
bytesToRead = dataAvailable; | |||
} | |||
memcpy(buffer, mStorage.get() + mPosition, bytesToRead); | |||
mPosition += bytesToRead; | |||
return bytesToRead; | |||
} | |||
int32_t FixedBlockReader::read(uint8_t *buffer, int32_t numBytes) { | |||
int32_t bytesRead; | |||
int32_t bytesLeft = numBytes; | |||
while(bytesLeft > 0) { | |||
if (mPosition < mValid) { | |||
// Use up bytes currently in storage. | |||
bytesRead = readFromStorage(buffer, bytesLeft); | |||
buffer += bytesRead; | |||
bytesLeft -= bytesRead; | |||
} else if (bytesLeft >= mSize) { | |||
// Nothing in storage. Read through if enough for a complete block. | |||
bytesRead = mFixedBlockProcessor.onProcessFixedBlock(buffer, mSize); | |||
if (bytesRead < 0) return bytesRead; | |||
buffer += bytesRead; | |||
bytesLeft -= bytesRead; | |||
} else { | |||
// Just need a partial block so we have to reload storage. | |||
bytesRead = mFixedBlockProcessor.onProcessFixedBlock(mStorage.get(), mSize); | |||
if (bytesRead < 0) return bytesRead; | |||
mPosition = 0; | |||
mValid = bytesRead; | |||
if (bytesRead == 0) break; | |||
} | |||
} | |||
return numBytes - bytesLeft; | |||
} |
@@ -1,60 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
#ifndef AAUDIO_FIXED_BLOCK_READER_H | |||
#define AAUDIO_FIXED_BLOCK_READER_H | |||
#include <stdint.h> | |||
#include "FixedBlockAdapter.h" | |||
/** | |||
* Read from a fixed-size block to a variable sized block. | |||
* | |||
* This can be used to convert a pull data flow from fixed sized buffers to variable sized buffers. | |||
* An example would be an audio output callback that reads from the app. | |||
*/ | |||
class FixedBlockReader : public FixedBlockAdapter | |||
{ | |||
public: | |||
FixedBlockReader(FixedBlockProcessor &fixedBlockProcessor); | |||
virtual ~FixedBlockReader() = default; | |||
int32_t open(int32_t bytesPerFixedBlock) override; | |||
/** | |||
* Read into a variable sized block. | |||
* | |||
* Note that if the fixed-sized blocks must be aligned, then the variable-sized blocks | |||
* must have the same alignment. | |||
* For example, if the fixed-size blocks must be a multiple of 8, then the variable-sized | |||
* blocks must also be a multiple of 8. | |||
* | |||
* @param buffer | |||
* @param numBytes | |||
* @return Number of bytes read or a negative error code. | |||
*/ | |||
int32_t read(uint8_t *buffer, int32_t numBytes); | |||
private: | |||
int32_t readFromStorage(uint8_t *buffer, int32_t numBytes); | |||
int32_t mValid = 0; // Number of valid bytes in mStorage. | |||
}; | |||
#endif /* AAUDIO_FIXED_BLOCK_READER_H */ |
@@ -1,73 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
#include <stdint.h> | |||
#include <memory.h> | |||
#include "FixedBlockAdapter.h" | |||
#include "FixedBlockWriter.h" | |||
FixedBlockWriter::FixedBlockWriter(FixedBlockProcessor &fixedBlockProcessor) | |||
: FixedBlockAdapter(fixedBlockProcessor) {} | |||
int32_t FixedBlockWriter::writeToStorage(uint8_t *buffer, int32_t numBytes) { | |||
int32_t bytesToStore = numBytes; | |||
int32_t roomAvailable = mSize - mPosition; | |||
if (bytesToStore > roomAvailable) { | |||
bytesToStore = roomAvailable; | |||
} | |||
memcpy(mStorage.get() + mPosition, buffer, bytesToStore); | |||
mPosition += bytesToStore; | |||
return bytesToStore; | |||
} | |||
int32_t FixedBlockWriter::write(uint8_t *buffer, int32_t numBytes) { | |||
int32_t bytesLeft = numBytes; | |||
// If we already have data in storage then add to it. | |||
if (mPosition > 0) { | |||
int32_t bytesWritten = writeToStorage(buffer, bytesLeft); | |||
buffer += bytesWritten; | |||
bytesLeft -= bytesWritten; | |||
// If storage full then flush it out | |||
if (mPosition == mSize) { | |||
bytesWritten = mFixedBlockProcessor.onProcessFixedBlock(mStorage.get(), mSize); | |||
if (bytesWritten < 0) return bytesWritten; | |||
mPosition = 0; | |||
if (bytesWritten < mSize) { | |||
// Only some of the data was written! This should not happen. | |||
return -1; | |||
} | |||
} | |||
} | |||
// Write through if enough for a complete block. | |||
while(bytesLeft > mSize) { | |||
int32_t bytesWritten = mFixedBlockProcessor.onProcessFixedBlock(buffer, mSize); | |||
if (bytesWritten < 0) return bytesWritten; | |||
buffer += bytesWritten; | |||
bytesLeft -= bytesWritten; | |||
} | |||
// Save any remaining partial blocks for next time. | |||
if (bytesLeft > 0) { | |||
int32_t bytesWritten = writeToStorage(buffer, bytesLeft); | |||
bytesLeft -= bytesWritten; | |||
} | |||
return numBytes - bytesLeft; | |||
} |
@@ -1,54 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
#ifndef AAUDIO_FIXED_BLOCK_WRITER_H | |||
#define AAUDIO_FIXED_BLOCK_WRITER_H | |||
#include <stdint.h> | |||
#include "FixedBlockAdapter.h" | |||
/** | |||
* This can be used to convert a push data flow from variable sized buffers to fixed sized buffers. | |||
* An example would be an audio input callback. | |||
*/ | |||
class FixedBlockWriter : public FixedBlockAdapter | |||
{ | |||
public: | |||
FixedBlockWriter(FixedBlockProcessor &fixedBlockProcessor); | |||
virtual ~FixedBlockWriter() = default; | |||
/** | |||
* Write from a variable sized block. | |||
* | |||
* Note that if the fixed-sized blocks must be aligned, then the variable-sized blocks | |||
* must have the same alignment. | |||
* For example, if the fixed-size blocks must be a multiple of 8, then the variable-sized | |||
* blocks must also be a multiple of 8. | |||
* | |||
* @param buffer | |||
* @param numBytes | |||
* @return Number of bytes written or a negative error code. | |||
*/ | |||
int32_t write(uint8_t *buffer, int32_t numBytes); | |||
private: | |||
int32_t writeToStorage(uint8_t *buffer, int32_t numBytes); | |||
}; | |||
#endif /* AAUDIO_FIXED_BLOCK_WRITER_H */ |
@@ -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 "oboe/LatencyTuner.h" | |||
using namespace oboe; | |||
LatencyTuner::LatencyTuner(AudioStream &stream) | |||
: LatencyTuner(stream, stream.getBufferCapacityInFrames()){ | |||
} | |||
LatencyTuner::LatencyTuner(oboe::AudioStream &stream, int32_t maximumBufferSize) | |||
: mStream(stream) | |||
, mMaxBufferSize(maximumBufferSize) { | |||
reset(); | |||
} | |||
Result LatencyTuner::tune() { | |||
if (mState == State::Unsupported) { | |||
return Result::ErrorUnimplemented; | |||
} | |||
Result result = Result::OK; | |||
// Process reset requests. | |||
int32_t numRequests = mLatencyTriggerRequests.load(); | |||
if (numRequests != mLatencyTriggerResponses.load()) { | |||
mLatencyTriggerResponses.store(numRequests); | |||
reset(); | |||
} | |||
// Set state to Active if the idle countdown has reached zero. | |||
if (mState == State::Idle && --mIdleCountDown <= 0) { | |||
mState = State::Active; | |||
} | |||
// When state is Active attempt to change the buffer size if the number of xRuns has increased. | |||
if (mState == State::Active) { | |||
auto xRunCountResult = mStream.getXRunCount(); | |||
if (xRunCountResult == Result::OK) { | |||
if ((xRunCountResult.value() - mPreviousXRuns) > 0) { | |||
mPreviousXRuns = xRunCountResult.value(); | |||
int32_t oldBufferSize = mStream.getBufferSizeInFrames(); | |||
int32_t requestedBufferSize = oldBufferSize + mStream.getFramesPerBurst(); | |||
// Do not request more than the maximum buffer size (which was either user-specified | |||
// or was from stream->getBufferCapacityInFrames()) | |||
if (requestedBufferSize > mMaxBufferSize) requestedBufferSize = mMaxBufferSize; | |||
auto setBufferResult = mStream.setBufferSizeInFrames(requestedBufferSize); | |||
if (setBufferResult != Result::OK) { | |||
result = setBufferResult; | |||
mState = State::Unsupported; | |||
} else if (setBufferResult.value() == oldBufferSize) { | |||
mState = State::AtMax; | |||
} | |||
} | |||
} else { | |||
mState = State::Unsupported; | |||
} | |||
} | |||
if (mState == State::Unsupported) { | |||
result = Result::ErrorUnimplemented; | |||
} | |||
if (mState == State::AtMax) { | |||
result = Result::OK; | |||
} | |||
return result; | |||
} | |||
void LatencyTuner::requestReset() { | |||
if (mState != State::Unsupported) { | |||
mLatencyTriggerRequests++; | |||
} | |||
} | |||
void LatencyTuner::reset() { | |||
mState = State::Idle; | |||
mIdleCountDown = kIdleCount; | |||
// Set to minimal latency | |||
mStream.setBufferSizeInFrames(2 * mStream.getFramesPerBurst()); | |||
} | |||
bool LatencyTuner::isAtMaximumBufferSize() { | |||
return mState == State::AtMax; | |||
} |
@@ -1,112 +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 COMMON_MONOTONIC_COUNTER_H | |||
#define COMMON_MONOTONIC_COUNTER_H | |||
#include <cstdint> | |||
/** | |||
* Maintain a 64-bit monotonic counter. | |||
* Can be used to track a 32-bit counter that wraps or gets reset. | |||
* | |||
* Note that this is not atomic and has no interior locks. | |||
* A caller will need to provide their own exterior locking | |||
* if they need to use it from multiple threads. | |||
*/ | |||
class MonotonicCounter { | |||
public: | |||
MonotonicCounter() {} | |||
virtual ~MonotonicCounter() {} | |||
/** | |||
* @return current value of the counter | |||
*/ | |||
int64_t get() const { | |||
return mCounter64; | |||
} | |||
/** | |||
* set the current value of the counter | |||
*/ | |||
void set(int64_t counter) { | |||
mCounter64 = counter; | |||
} | |||
/** | |||
* Advance the counter if delta is positive. | |||
* @return current value of the counter | |||
*/ | |||
int64_t increment(int64_t delta) { | |||
if (delta > 0) { | |||
mCounter64 += delta; | |||
} | |||
return mCounter64; | |||
} | |||
/** | |||
* Advance the 64-bit counter if (current32 - previousCurrent32) > 0. | |||
* This can be used to convert a 32-bit counter that may be wrapping into | |||
* a monotonic 64-bit counter. | |||
* | |||
* This counter32 should NOT be allowed to advance by more than 0x7FFFFFFF between calls. | |||
* Think of the wrapping counter like a sine wave. If the frequency of the signal | |||
* is more than half the sampling rate (Nyquist rate) then you cannot measure it properly. | |||
* If the counter wraps around every 24 hours then we should measure it with a period | |||
* of less than 12 hours. | |||
* | |||
* @return current value of the 64-bit counter | |||
*/ | |||
int64_t update32(int32_t counter32) { | |||
int32_t delta = counter32 - mCounter32; | |||
// protect against the mCounter64 going backwards | |||
if (delta > 0) { | |||
mCounter64 += delta; | |||
mCounter32 = counter32; | |||
} | |||
return mCounter64; | |||
} | |||
/** | |||
* Reset the stored value of the 32-bit counter. | |||
* This is used if your counter32 has been reset to zero. | |||
*/ | |||
void reset32() { | |||
mCounter32 = 0; | |||
} | |||
/** | |||
* Round 64-bit counter up to a multiple of the period. | |||
* | |||
* The period must be positive. | |||
* | |||
* @param period might be, for example, a buffer capacity | |||
*/ | |||
void roundUp64(int32_t period) { | |||
if (period > 0) { | |||
int64_t numPeriods = (mCounter64 + period - 1) / period; | |||
mCounter64 = numPeriods * period; | |||
} | |||
} | |||
private: | |||
int64_t mCounter64 = 0; | |||
int32_t mCounter32 = 0; | |||
}; | |||
#endif //COMMON_MONOTONIC_COUNTER_H |
@@ -1,46 +0,0 @@ | |||
/* | |||
* Copyright (C) 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_DEBUG_H | |||
#define OBOE_DEBUG_H | |||
#if OBOE_ENABLE_LOGGING | |||
#include <android/log.h> | |||
#ifndef MODULE_NAME | |||
#define MODULE_NAME "OboeAudio" | |||
#endif | |||
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, MODULE_NAME, __VA_ARGS__) | |||
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, MODULE_NAME, __VA_ARGS__) | |||
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, MODULE_NAME, __VA_ARGS__) | |||
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, MODULE_NAME, __VA_ARGS__) | |||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, MODULE_NAME, __VA_ARGS__) | |||
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, MODULE_NAME, __VA_ARGS__) | |||
#else | |||
#define LOGV(...) | |||
#define LOGD(...) | |||
#define LOGI(...) | |||
#define LOGW(...) | |||
#define LOGE(...) | |||
#define LOGF(...) | |||
#endif | |||
#endif //OBOE_DEBUG_H |
@@ -1,70 +0,0 @@ | |||
/* | |||
* Copyright 2019 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 <oboe/AudioStreamBuilder.h> | |||
#include <oboe/Oboe.h> | |||
#include "QuirksManager.h" | |||
using namespace oboe; | |||
bool QuirksManager::isConversionNeeded( | |||
const AudioStreamBuilder &builder, | |||
AudioStreamBuilder &childBuilder) { | |||
bool conversionNeeded = false; | |||
const bool isLowLatency = builder.getPerformanceMode() == PerformanceMode::LowLatency; | |||
const bool isInput = builder.getDirection() == Direction::Input; | |||
const bool isFloat = builder.getFormat() == AudioFormat::Float; | |||
// If a SAMPLE RATE is specified for low latency then let the native code choose an optimal rate. | |||
// TODO There may be a problem if the devices supports low latency | |||
// at a higher rate than the default. | |||
if (builder.getSampleRate() != oboe::Unspecified | |||
&& builder.getSampleRateConversionQuality() != SampleRateConversionQuality::None | |||
&& isLowLatency | |||
) { | |||
childBuilder.setSampleRate(oboe::Unspecified); // native API decides the best sample rate | |||
conversionNeeded = true; | |||
} | |||
// Data Format | |||
// OpenSL ES and AAudio before P do not support FAST path for FLOAT capture. | |||
if (isFloat | |||
&& isInput | |||
&& builder.isFormatConversionAllowed() | |||
&& isLowLatency | |||
&& (!builder.willUseAAudio() || (getSdkVersion() < __ANDROID_API_P__)) | |||
) { | |||
childBuilder.setFormat(AudioFormat::I16); // needed for FAST track | |||
conversionNeeded = true; | |||
} | |||
// Channel Count | |||
if (builder.getChannelCount() != oboe::Unspecified | |||
&& builder.isChannelConversionAllowed()) { | |||
if (builder.getChannelCount() == 2 // stereo? | |||
&& isInput | |||
&& isLowLatency | |||
&& (!builder.willUseAAudio() && (getSdkVersion() == __ANDROID_API_O__))) { | |||
// workaround for temporary heap size regression, b/66967812 | |||
childBuilder.setChannelCount(1); | |||
conversionNeeded = true; | |||
} | |||
// Note that MMAP does not support mono in 8.1. But that would only matter on Pixel 1 | |||
// phones and they have almost all been updated to 9.0. | |||
} | |||
return conversionNeeded; | |||
} |
@@ -1,52 +0,0 @@ | |||
/* | |||
* Copyright 2019 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_QUIRKS_MANAGER_H | |||
#define OBOE_QUIRKS_MANAGER_H | |||
#include <memory> | |||
#include <oboe/AudioStreamBuilder.h> | |||
namespace oboe { | |||
/** | |||
* Based on manufacturer, model and Android version number | |||
* decide whether data conversion needs to occur. | |||
* | |||
* This also manages device and version specific workarounds. | |||
*/ | |||
class QuirksManager { | |||
public: | |||
static QuirksManager &getInstance() { | |||
static QuirksManager instance; | |||
return instance; | |||
} | |||
/** | |||
* | |||
* @param builder builder provided by application | |||
* @param childBuilder modified builder appropriate for the underlying device | |||
* @return true if conversion is needed | |||
*/ | |||
bool isConversionNeeded(const AudioStreamBuilder &builder, AudioStreamBuilder &childBuilder); | |||
private: | |||
}; | |||
} | |||
#endif //OBOE_QUIRKS_MANAGER_H |
@@ -1,30 +0,0 @@ | |||
/* | |||
* Copyright 2019 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 <algorithm> | |||
#include <unistd.h> | |||
#include "flowgraph/FlowGraphNode.h" | |||
#include "SourceFloatCaller.h" | |||
using namespace oboe; | |||
using namespace flowgraph; | |||
int32_t SourceFloatCaller::onProcess(int32_t numFrames) { | |||
int32_t numBytes = mStream->getBytesPerFrame() * numFrames; | |||
int32_t bytesRead = mBlockReader.read((uint8_t *) output.getBuffer(), numBytes); | |||
int32_t framesRead = bytesRead / mStream->getBytesPerFrame(); | |||
return framesRead; | |||
} |
@@ -1,44 +0,0 @@ | |||
/* | |||
* Copyright 2019 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_SOURCE_FLOAT_CALLER_H | |||
#define OBOE_SOURCE_FLOAT_CALLER_H | |||
#include <unistd.h> | |||
#include <sys/types.h> | |||
#include "flowgraph/FlowGraphNode.h" | |||
#include "AudioSourceCaller.h" | |||
#include "FixedBlockReader.h" | |||
namespace oboe { | |||
/** | |||
* AudioSource that uses callback to get more float data. | |||
*/ | |||
class SourceFloatCaller : public AudioSourceCaller { | |||
public: | |||
SourceFloatCaller(int32_t channelCount, int32_t framesPerCallback) | |||
: AudioSourceCaller(channelCount, framesPerCallback, (int32_t)sizeof(float)) {} | |||
int32_t onProcess(int32_t numFrames) override; | |||
const char *getName() override { | |||
return "SourceFloatCaller"; | |||
} | |||
}; | |||
} | |||
#endif //OBOE_SOURCE_FLOAT_CALLER_H |
@@ -1,47 +0,0 @@ | |||
/* | |||
* Copyright 2019 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 <algorithm> | |||
#include <unistd.h> | |||
#include "flowgraph/FlowGraphNode.h" | |||
#include "SourceI16Caller.h" | |||
#if FLOWGRAPH_ANDROID_INTERNAL | |||
#include <audio_utils/primitives.h> | |||
#endif | |||
using namespace oboe; | |||
using namespace flowgraph; | |||
int32_t SourceI16Caller::onProcess(int32_t numFrames) { | |||
int32_t numBytes = mStream->getBytesPerFrame() * numFrames; | |||
int32_t bytesRead = mBlockReader.read((uint8_t *) mConversionBuffer.get(), numBytes); | |||
int32_t framesRead = bytesRead / mStream->getBytesPerFrame(); | |||
float *floatData = output.getBuffer(); | |||
const int16_t *shortData = mConversionBuffer.get(); | |||
int32_t numSamples = framesRead * output.getSamplesPerFrame(); | |||
#if FLOWGRAPH_ANDROID_INTERNAL | |||
memcpy_to_float_from_i16(floatData, shortData, numSamples); | |||
#else | |||
for (int i = 0; i < numSamples; i++) { | |||
*floatData++ = *shortData++ * (1.0f / 32768); | |||
} | |||
#endif | |||
return framesRead; | |||
} |
@@ -1,48 +0,0 @@ | |||
/* | |||
* Copyright 2019 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_SOURCE_I16_CALLER_H | |||
#define OBOE_SOURCE_I16_CALLER_H | |||
#include <unistd.h> | |||
#include <sys/types.h> | |||
#include "flowgraph/FlowGraphNode.h" | |||
#include "AudioSourceCaller.h" | |||
#include "FixedBlockReader.h" | |||
namespace oboe { | |||
/** | |||
* AudioSource that uses callback to get more data. | |||
*/ | |||
class SourceI16Caller : public AudioSourceCaller { | |||
public: | |||
SourceI16Caller(int32_t channelCount, int32_t framesPerCallback) | |||
: AudioSourceCaller(channelCount, framesPerCallback, sizeof(int16_t)) { | |||
mConversionBuffer = std::make_unique<int16_t[]>(channelCount * output.getFramesPerBuffer()); | |||
} | |||
int32_t onProcess(int32_t numFrames) override; | |||
const char *getName() override { | |||
return "SourceI16Caller"; | |||
} | |||
private: | |||
std::unique_ptr<int16_t[]> mConversionBuffer; | |||
}; | |||
} | |||
#endif //OBOE_SOURCE_I16_CALLER_H |
@@ -1,112 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
#include "oboe/StabilizedCallback.h" | |||
#include "common/AudioClock.h" | |||
#include "common/Trace.h" | |||
constexpr int32_t kLoadGenerationStepSizeNanos = 20000; | |||
constexpr float kPercentageOfCallbackToUse = 0.8; | |||
using namespace oboe; | |||
StabilizedCallback::StabilizedCallback(AudioStreamCallback *callback) : mCallback(callback){ | |||
Trace::initialize(); | |||
} | |||
/** | |||
* An audio callback which attempts to do work for a fixed amount of time. | |||
* | |||
* @param oboeStream | |||
* @param audioData | |||
* @param numFrames | |||
* @return | |||
*/ | |||
DataCallbackResult | |||
StabilizedCallback::onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numFrames) { | |||
int64_t startTimeNanos = AudioClock::getNanoseconds(); | |||
if (mFrameCount == 0){ | |||
mEpochTimeNanos = startTimeNanos; | |||
} | |||
int64_t durationSinceEpochNanos = startTimeNanos - mEpochTimeNanos; | |||
// In an ideal world the callback start time will be exactly the same as the duration of the | |||
// frames already read/written into the stream. In reality the callback can start early | |||
// or late. By finding the delta we can calculate the target duration for our stabilized | |||
// callback. | |||
int64_t idealStartTimeNanos = (mFrameCount * kNanosPerSecond) / oboeStream->getSampleRate(); | |||
int64_t lateStartNanos = durationSinceEpochNanos - idealStartTimeNanos; | |||
if (lateStartNanos < 0){ | |||
// This was an early start which indicates that our previous epoch was a late callback. | |||
// Update our epoch to this more accurate time. | |||
mEpochTimeNanos = startTimeNanos; | |||
mFrameCount = 0; | |||
} | |||
int64_t numFramesAsNanos = (numFrames * kNanosPerSecond) / oboeStream->getSampleRate(); | |||
int64_t targetDurationNanos = static_cast<int64_t>( | |||
(numFramesAsNanos * kPercentageOfCallbackToUse) - lateStartNanos); | |||
Trace::beginSection("Actual load"); | |||
DataCallbackResult result = mCallback->onAudioReady(oboeStream, audioData, numFrames); | |||
Trace::endSection(); | |||
int64_t executionDurationNanos = AudioClock::getNanoseconds() - startTimeNanos; | |||
int64_t stabilizingLoadDurationNanos = targetDurationNanos - executionDurationNanos; | |||
Trace::beginSection("Stabilized load for %lldns", stabilizingLoadDurationNanos); | |||
generateLoad(stabilizingLoadDurationNanos); | |||
Trace::endSection(); | |||
// Wraparound: At 48000 frames per second mFrameCount wraparound will occur after 6m years, | |||
// significantly longer than the average lifetime of an Android phone. | |||
mFrameCount += numFrames; | |||
return result; | |||
} | |||
void StabilizedCallback::generateLoad(int64_t durationNanos) { | |||
int64_t currentTimeNanos = AudioClock::getNanoseconds(); | |||
int64_t deadlineTimeNanos = currentTimeNanos + durationNanos; | |||
// opsPerStep gives us an estimated number of operations which need to be run to fully utilize | |||
// the CPU for a fixed amount of time (specified by kLoadGenerationStepSizeNanos). | |||
// After each step the opsPerStep value is re-calculated based on the actual time taken to | |||
// execute those operations. | |||
auto opsPerStep = (int)(mOpsPerNano * kLoadGenerationStepSizeNanos); | |||
int64_t stepDurationNanos = 0; | |||
int64_t previousTimeNanos = 0; | |||
while (currentTimeNanos <= deadlineTimeNanos){ | |||
for (int i = 0; i < opsPerStep; i++) cpu_relax(); | |||
previousTimeNanos = currentTimeNanos; | |||
currentTimeNanos = AudioClock::getNanoseconds(); | |||
stepDurationNanos = currentTimeNanos - previousTimeNanos; | |||
// Calculate exponential moving average to smooth out values, this acts as a low pass filter. | |||
// @see https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average | |||
static const float kFilterCoefficient = 0.1; | |||
auto measuredOpsPerNano = (double) opsPerStep / stepDurationNanos; | |||
mOpsPerNano = kFilterCoefficient * measuredOpsPerNano + (1.0 - kFilterCoefficient) * mOpsPerNano; | |||
opsPerStep = (int) (mOpsPerNano * kLoadGenerationStepSizeNanos); | |||
} | |||
} |
@@ -1,75 +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 <dlfcn.h> | |||
#include <cstdio> | |||
#include "Trace.h" | |||
#include "OboeDebug.h" | |||
static char buffer[256]; | |||
// Tracing functions | |||
static void *(*ATrace_beginSection)(const char *sectionName); | |||
static void *(*ATrace_endSection)(); | |||
typedef void *(*fp_ATrace_beginSection)(const char *sectionName); | |||
typedef void *(*fp_ATrace_endSection)(); | |||
bool Trace::mIsTracingSupported = false; | |||
void Trace::beginSection(const char *format, ...){ | |||
if (mIsTracingSupported) { | |||
va_list va; | |||
va_start(va, format); | |||
vsprintf(buffer, format, va); | |||
ATrace_beginSection(buffer); | |||
va_end(va); | |||
} else { | |||
LOGE("Tracing is either not initialized (call Trace::initialize()) " | |||
"or not supported on this device"); | |||
} | |||
} | |||
void Trace::endSection() { | |||
if (mIsTracingSupported) { | |||
ATrace_endSection(); | |||
} | |||
} | |||
void Trace::initialize() { | |||
// Using dlsym allows us to use tracing on API 21+ without needing android/trace.h which wasn't | |||
// published until API 23 | |||
void *lib = dlopen("libandroid.so", RTLD_NOW | RTLD_LOCAL); | |||
if (lib == nullptr) { | |||
LOGE("Could not open libandroid.so to dynamically load tracing symbols"); | |||
} else { | |||
ATrace_beginSection = | |||
reinterpret_cast<fp_ATrace_beginSection >( | |||
dlsym(lib, "ATrace_beginSection")); | |||
ATrace_endSection = | |||
reinterpret_cast<fp_ATrace_endSection >( | |||
dlsym(lib, "ATrace_endSection")); | |||
if (ATrace_beginSection != nullptr && ATrace_endSection != nullptr){ | |||
mIsTracingSupported = true; | |||
} | |||
} | |||
} |
@@ -1,31 +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. | |||
*/ | |||
#ifndef OBOE_TRACE_H | |||
#define OBOE_TRACE_H | |||
class Trace { | |||
public: | |||
static void beginSection(const char *format, ...); | |||
static void endSection(); | |||
static void initialize(); | |||
private: | |||
static bool mIsTracingSupported; | |||
}; | |||
#endif //OBOE_TRACE_H |
@@ -1,283 +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 <stdlib.h> | |||
#include <unistd.h> | |||
#include <sstream> | |||
#ifdef __ANDROID__ | |||
#include <sys/system_properties.h> | |||
#endif | |||
#include <oboe/AudioStream.h> | |||
#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 = static_cast<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] = static_cast<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>(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"; | |||
case Result::ErrorClosed: return "ErrorClosed"; | |||
default: return "Unrecognized result"; | |||
} | |||
} | |||
template<> | |||
const char *convertToText<AudioFormat>(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>(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>(SharingMode mode) { | |||
switch (mode) { | |||
case SharingMode::Exclusive: return "Exclusive"; | |||
case SharingMode::Shared: return "Shared"; | |||
default: return "Unrecognized sharing mode"; | |||
} | |||
} | |||
template<> | |||
const char *convertToText<DataCallbackResult>(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 direction) { | |||
switch (direction) { | |||
case Direction::Input: return "Input"; | |||
case Direction::Output: return "Output"; | |||
default: return "Unrecognized direction"; | |||
} | |||
} | |||
template<> | |||
const char *convertToText<StreamState>(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 audioApi) { | |||
switch (audioApi) { | |||
case AudioApi::Unspecified: return "Unspecified"; | |||
case AudioApi::OpenSLES: return "OpenSLES"; | |||
case AudioApi::AAudio: return "AAudio"; | |||
default: return "Unrecognized audio API"; | |||
} | |||
} | |||
template<> | |||
const char *convertToText<AudioStream*>(AudioStream* stream) { | |||
static std::string streamText; | |||
std::stringstream s; | |||
s<<"StreamID: "<< static_cast<void*>(stream)<<std::endl | |||
<<"DeviceId: "<<stream->getDeviceId()<<std::endl | |||
<<"Direction: "<<oboe::convertToText(stream->getDirection())<<std::endl | |||
<<"API type: "<<oboe::convertToText(stream->getAudioApi())<<std::endl | |||
<<"BufferCapacity: "<<stream->getBufferCapacityInFrames()<<std::endl | |||
<<"BufferSize: "<<stream->getBufferSizeInFrames()<<std::endl | |||
<<"FramesPerBurst: "<< stream->getFramesPerBurst()<<std::endl | |||
<<"FramesPerCallback: "<<stream->getFramesPerCallback()<<std::endl | |||
<<"SampleRate: "<<stream->getSampleRate()<<std::endl | |||
<<"ChannelCount: "<<stream->getChannelCount()<<std::endl | |||
<<"Format: "<<oboe::convertToText(stream->getFormat())<<std::endl | |||
<<"SharingMode: "<<oboe::convertToText(stream->getSharingMode())<<std::endl | |||
<<"PerformanceMode: "<<oboe::convertToText(stream->getPerformanceMode()) | |||
<<std::endl | |||
<<"CurrentState: "<<oboe::convertToText(stream->getState())<<std::endl | |||
<<"XRunCount: "<<stream->getXRunCount()<<std::endl | |||
<<"FramesRead: "<<stream->getFramesRead()<<std::endl | |||
<<"FramesWritten: "<<stream->getFramesWritten()<<std::endl; | |||
streamText = s.str(); | |||
return streamText.c_str(); | |||
} | |||
template<> | |||
const char *convertToText<Usage>(Usage usage) { | |||
switch (usage) { | |||
case Usage::Media: return "Media"; | |||
case Usage::VoiceCommunication: return "VoiceCommunication"; | |||
case Usage::VoiceCommunicationSignalling: return "VoiceCommunicationSignalling"; | |||
case Usage::Alarm: return "Alarm"; | |||
case Usage::Notification: return "Notification"; | |||
case Usage::NotificationRingtone: return "NotificationRingtone"; | |||
case Usage::NotificationEvent: return "NotificationEvent"; | |||
case Usage::AssistanceAccessibility: return "AssistanceAccessibility"; | |||
case Usage::AssistanceNavigationGuidance: return "AssistanceNavigationGuidance"; | |||
case Usage::AssistanceSonification: return "AssistanceSonification"; | |||
case Usage::Game: return "Game"; | |||
case Usage::Assistant: return "Assistant"; | |||
default: return "Unrecognized usage"; | |||
} | |||
} | |||
template<> | |||
const char *convertToText<ContentType>(ContentType contentType) { | |||
switch (contentType) { | |||
case ContentType::Speech: return "Speech"; | |||
case ContentType::Music: return "Music"; | |||
case ContentType::Movie: return "Movie"; | |||
case ContentType::Sonification: return "Sonification"; | |||
default: return "Unrecognized content type"; | |||
} | |||
} | |||
template<> | |||
const char *convertToText<InputPreset>(InputPreset inputPreset) { | |||
switch (inputPreset) { | |||
case InputPreset::Generic: return "Generic"; | |||
case InputPreset::Camcorder: return "Camcorder"; | |||
case InputPreset::VoiceRecognition: return "VoiceRecognition"; | |||
case InputPreset::VoiceCommunication: return "VoiceCommunication"; | |||
case InputPreset::Unprocessed: return "Unprocessed"; | |||
case InputPreset::VoicePerformance: return "VoicePerformance"; | |||
default: return "Unrecognized input preset"; | |||
} | |||
} | |||
template<> | |||
const char *convertToText<SessionId>(SessionId sessionId) { | |||
switch (sessionId) { | |||
case SessionId::None: return "None"; | |||
case SessionId::Allocate: return "Allocate"; | |||
default: return "Unrecognized session id"; | |||
} | |||
} | |||
template<> | |||
const char *convertToText<ChannelCount>(ChannelCount channelCount) { | |||
switch (channelCount) { | |||
case ChannelCount::Unspecified: return "Unspecified"; | |||
case ChannelCount::Mono: return "Mono"; | |||
case ChannelCount::Stereo: return "Stereo"; | |||
default: return "Unrecognized channel count"; | |||
} | |||
} | |||
int getSdkVersion() { | |||
#ifdef __ANDROID__ | |||
static int sCachedSdkVersion = -1; | |||
if (sCachedSdkVersion == -1) { | |||
char sdk[PROP_VALUE_MAX] = {0}; | |||
if (__system_property_get("ro.build.version.sdk", sdk) != 0) { | |||
sCachedSdkVersion = atoi(sdk); | |||
} | |||
} | |||
return sCachedSdkVersion; | |||
#endif | |||
return -1; | |||
} | |||
}// namespace oboe |
@@ -1,28 +0,0 @@ | |||
/* | |||
* Copyright 2019 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 "oboe/Version.h" | |||
namespace oboe { | |||
// This variable enables the version information to be read from the resulting binary e.g. | |||
// by running `objdump -s --section=.data <binary>` | |||
// Please do not optimize or change in any way. | |||
char kVersionText[] = "OboeVersion" OBOE_VERSION_TEXT; | |||
const char * getVersionText(){ | |||
return kVersionText; | |||
} | |||
} // namespace oboe |
@@ -1,186 +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 <stdint.h> | |||
#include <time.h> | |||
#include <memory.h> | |||
#include <cassert> | |||
#include <algorithm> | |||
#include "common/OboeDebug.h" | |||
#include "fifo/FifoControllerBase.h" | |||
#include "fifo/FifoController.h" | |||
#include "fifo/FifoControllerIndirect.h" | |||
#include "fifo/FifoBuffer.h" | |||
#include "common/AudioClock.h" | |||
namespace oboe { | |||
FifoBuffer::FifoBuffer(uint32_t bytesPerFrame, uint32_t capacityInFrames) | |||
: mBytesPerFrame(bytesPerFrame) | |||
, mStorage(nullptr) | |||
, mFramesReadCount(0) | |||
, mFramesUnderrunCount(0) | |||
{ | |||
mFifo = std::make_unique<FifoController>(capacityInFrames); | |||
// allocate buffer | |||
int32_t bytesPerBuffer = bytesPerFrame * capacityInFrames; | |||
mStorage = new uint8_t[bytesPerBuffer]; | |||
mStorageOwned = true; | |||
LOGD("%s() capacityInFrames = %d, bytesPerFrame = %d", | |||
__func__, capacityInFrames, bytesPerFrame); | |||
} | |||
FifoBuffer::FifoBuffer( uint32_t bytesPerFrame, | |||
uint32_t capacityInFrames, | |||
std::atomic<uint64_t> *readCounterAddress, | |||
std::atomic<uint64_t> *writeCounterAddress, | |||
uint8_t *dataStorageAddress | |||
) | |||
: mBytesPerFrame(bytesPerFrame) | |||
, mStorage(dataStorageAddress) | |||
, mFramesReadCount(0) | |||
, mFramesUnderrunCount(0) | |||
{ | |||
mFifo = std::make_unique<FifoControllerIndirect>(capacityInFrames, | |||
readCounterAddress, | |||
writeCounterAddress); | |||
mStorage = dataStorageAddress; | |||
mStorageOwned = false; | |||
LOGD("%s(*) capacityInFrames = %d, bytesPerFrame = %d", | |||
__func__, capacityInFrames, bytesPerFrame); | |||
} | |||
FifoBuffer::~FifoBuffer() { | |||
if (mStorageOwned) { | |||
delete[] mStorage; | |||
} | |||
} | |||
int32_t FifoBuffer::convertFramesToBytes(int32_t frames) { | |||
return frames * mBytesPerFrame; | |||
} | |||
int32_t FifoBuffer::read(void *buffer, int32_t numFrames) { | |||
if (numFrames <= 0) { | |||
return 0; | |||
} | |||
// safe because numFrames is guaranteed positive | |||
uint32_t framesToRead = static_cast<uint32_t>(numFrames); | |||
uint32_t framesAvailable = mFifo->getFullFramesAvailable(); | |||
framesToRead = std::min(framesToRead, framesAvailable); | |||
uint32_t readIndex = mFifo->getReadIndex(); // ranges 0 to capacity | |||
uint8_t *destination = reinterpret_cast<uint8_t *>(buffer); | |||
uint8_t *source = &mStorage[convertFramesToBytes(readIndex)]; | |||
if ((readIndex + framesToRead) > mFifo->getFrameCapacity()) { | |||
// read in two parts, first part here is at the end of the mStorage buffer | |||
int32_t frames1 = static_cast<int32_t>(mFifo->getFrameCapacity() - readIndex); | |||
int32_t numBytes = convertFramesToBytes(frames1); | |||
if (numBytes < 0) { | |||
return static_cast<int32_t>(Result::ErrorOutOfRange); | |||
} | |||
memcpy(destination, source, static_cast<size_t>(numBytes)); | |||
destination += numBytes; | |||
// read second part, which is at the beginning of mStorage | |||
source = &mStorage[0]; | |||
int32_t frames2 = static_cast<uint32_t>(framesToRead - frames1); | |||
numBytes = convertFramesToBytes(frames2); | |||
if (numBytes < 0) { | |||
return static_cast<int32_t>(Result::ErrorOutOfRange); | |||
} | |||
memcpy(destination, source, static_cast<size_t>(numBytes)); | |||
} else { | |||
// just read in one shot | |||
int32_t numBytes = convertFramesToBytes(framesToRead); | |||
if (numBytes < 0) { | |||
return static_cast<int32_t>(Result::ErrorOutOfRange); | |||
} | |||
memcpy(destination, source, static_cast<size_t>(numBytes)); | |||
} | |||
mFifo->advanceReadIndex(framesToRead); | |||
return framesToRead; | |||
} | |||
int32_t FifoBuffer::write(const void *buffer, int32_t numFrames) { | |||
if (numFrames <= 0) { | |||
return 0; | |||
} | |||
// Guaranteed positive. | |||
uint32_t framesToWrite = static_cast<uint32_t>(numFrames); | |||
uint32_t framesAvailable = mFifo->getEmptyFramesAvailable(); | |||
framesToWrite = std::min(framesToWrite, framesAvailable); | |||
uint32_t writeIndex = mFifo->getWriteIndex(); | |||
int byteIndex = convertFramesToBytes(writeIndex); | |||
const uint8_t *source = reinterpret_cast<const uint8_t *>(buffer); | |||
uint8_t *destination = &mStorage[byteIndex]; | |||
if ((writeIndex + framesToWrite) > mFifo->getFrameCapacity()) { | |||
// write in two parts, first part here | |||
int32_t frames1 = static_cast<uint32_t>(mFifo->getFrameCapacity() - writeIndex); | |||
int32_t numBytes = convertFramesToBytes(frames1); | |||
if (numBytes < 0) { | |||
return static_cast<int32_t>(Result::ErrorOutOfRange); | |||
} | |||
memcpy(destination, source, static_cast<size_t>(numBytes)); | |||
// read second part | |||
source += convertFramesToBytes(frames1); | |||
destination = &mStorage[0]; | |||
int frames2 = static_cast<uint32_t>(framesToWrite - frames1); | |||
numBytes = convertFramesToBytes(frames2); | |||
if (numBytes < 0) { | |||
return static_cast<int32_t>(Result::ErrorOutOfRange); | |||
} | |||
memcpy(destination, source, static_cast<size_t>(numBytes)); | |||
} else { | |||
// just write in one shot | |||
int32_t numBytes = convertFramesToBytes(framesToWrite); | |||
if (numBytes < 0) { | |||
return static_cast<int32_t>(Result::ErrorOutOfRange); | |||
} | |||
memcpy(destination, source, static_cast<size_t>(numBytes)); | |||
} | |||
mFifo->advanceWriteIndex(framesToWrite); | |||
return framesToWrite; | |||
} | |||
int32_t FifoBuffer::readNow(void *buffer, int32_t numFrames) { | |||
int32_t framesRead = read(buffer, numFrames); | |||
if (framesRead < 0) { | |||
return framesRead; | |||
} | |||
int32_t framesLeft = numFrames - framesRead; | |||
mFramesReadCount += framesRead; | |||
mFramesUnderrunCount += framesLeft; | |||
// Zero out any samples we could not set. | |||
if (framesLeft > 0) { | |||
uint8_t *destination = reinterpret_cast<uint8_t *>(buffer); | |||
destination += convertFramesToBytes(framesRead); // point to first byte not set | |||
int32_t bytesToZero = convertFramesToBytes(framesLeft); | |||
memset(destination, 0, static_cast<size_t>(bytesToZero)); | |||
} | |||
return framesRead; | |||
} | |||
uint32_t FifoBuffer::getBufferCapacityInFrames() const { | |||
return mFifo->getFrameCapacity(); | |||
} | |||
} // namespace oboe |
@@ -1,99 +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_FIFOPROCESSOR_H | |||
#define OBOE_FIFOPROCESSOR_H | |||
#include <unistd.h> | |||
#include <sys/types.h> | |||
#include "common/OboeDebug.h" | |||
#include "FifoControllerBase.h" | |||
#include "oboe/Definitions.h" | |||
namespace oboe { | |||
class FifoBuffer { | |||
public: | |||
FifoBuffer(uint32_t bytesPerFrame, uint32_t capacityInFrames); | |||
FifoBuffer(uint32_t bytesPerFrame, | |||
uint32_t capacityInFrames, | |||
std::atomic<uint64_t> *readCounterAddress, | |||
std::atomic<uint64_t> *writeCounterAddress, | |||
uint8_t *dataStorageAddress); | |||
~FifoBuffer(); | |||
int32_t convertFramesToBytes(int32_t frames); | |||
/** | |||
* Read framesToRead or, if not enough, then read as many as are available. | |||
* @param destination | |||
* @param framesToRead number of frames requested | |||
* @return number of frames actually read | |||
*/ | |||
int32_t read(void *destination, int32_t framesToRead); | |||
int32_t write(const void *source, int32_t framesToWrite); | |||
uint32_t getBufferCapacityInFrames() const; | |||
/** | |||
* Calls read(). If all of the frames cannot be read then the remainder of the buffer | |||
* is set to zero. | |||
* | |||
* @param destination | |||
* @param framesToRead number of frames requested | |||
* @return number of frames actually read | |||
*/ | |||
int32_t readNow(void *destination, int32_t numFrames); | |||
uint32_t getFullFramesAvailable() { | |||
return mFifo->getFullFramesAvailable(); | |||
} | |||
uint32_t getBytesPerFrame() const { | |||
return mBytesPerFrame; | |||
} | |||
uint64_t getReadCounter() const { | |||
return mFifo->getReadCounter(); | |||
} | |||
void setReadCounter(uint64_t n) { | |||
mFifo->setReadCounter(n); | |||
} | |||
uint64_t getWriteCounter() { | |||
return mFifo->getWriteCounter(); | |||
} | |||
void setWriteCounter(uint64_t n) { | |||
mFifo->setWriteCounter(n); | |||
} | |||
private: | |||
uint32_t mBytesPerFrame; | |||
uint8_t* mStorage; | |||
bool mStorageOwned; // did this object allocate the storage? | |||
std::unique_ptr<FifoControllerBase> mFifo; | |||
uint64_t mFramesReadCount; | |||
uint64_t mFramesUnderrunCount; | |||
}; | |||
} // namespace oboe | |||
#endif //OBOE_FIFOPROCESSOR_H |
@@ -1,31 +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 <cassert> | |||
#include <sys/types.h> | |||
#include "FifoControllerBase.h" | |||
#include "FifoController.h" | |||
namespace oboe { | |||
FifoController::FifoController(uint32_t numFrames) | |||
: FifoControllerBase(numFrames) | |||
{ | |||
setReadCounter(0); | |||
setWriteCounter(0); | |||
} | |||
} // namespace oboe |
@@ -1,61 +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 NATIVEOBOE_FIFOCONTROLLER_H | |||
#define NATIVEOBOE_FIFOCONTROLLER_H | |||
#include <sys/types.h> | |||
#include "FifoControllerBase.h" | |||
#include <atomic> | |||
namespace oboe { | |||
/** | |||
* A FifoControllerBase with counters contained in the class. | |||
*/ | |||
class FifoController : public FifoControllerBase | |||
{ | |||
public: | |||
FifoController(uint32_t bufferSize); | |||
virtual ~FifoController() = default; | |||
virtual uint64_t getReadCounter() const override { | |||
return mReadCounter.load(std::memory_order_acquire); | |||
} | |||
virtual void setReadCounter(uint64_t n) override { | |||
mReadCounter.store(n, std::memory_order_release); | |||
} | |||
virtual void incrementReadCounter(uint64_t n) override { | |||
mReadCounter.fetch_add(n, std::memory_order_acq_rel); | |||
} | |||
virtual uint64_t getWriteCounter() const override { | |||
return mWriteCounter.load(std::memory_order_acquire); | |||
} | |||
virtual void setWriteCounter(uint64_t n) override { | |||
mWriteCounter.store(n, std::memory_order_release); | |||
} | |||
virtual void incrementWriteCounter(uint64_t n) override { | |||
mWriteCounter.fetch_add(n, std::memory_order_acq_rel); | |||
} | |||
private: | |||
std::atomic<uint64_t> mReadCounter{}; | |||
std::atomic<uint64_t> mWriteCounter{}; | |||
}; | |||
} // namespace oboe | |||
#endif //NATIVEOBOE_FIFOCONTROLLER_H |
@@ -1,71 +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 "FifoControllerBase.h" | |||
#include <cassert> | |||
#include <sys/types.h> | |||
#include <algorithm> | |||
#include "FifoControllerBase.h" | |||
#include "common/OboeDebug.h" | |||
namespace oboe { | |||
FifoControllerBase::FifoControllerBase(uint32_t capacityInFrames) | |||
: mTotalFrames(capacityInFrames) | |||
{ | |||
// Avoid ridiculously large buffers and the arithmetic wraparound issues that can follow. | |||
assert(capacityInFrames <= (UINT32_MAX / 4)); | |||
} | |||
uint32_t FifoControllerBase::getFullFramesAvailable() const { | |||
uint64_t writeCounter = getWriteCounter(); | |||
uint64_t readCounter = getReadCounter(); | |||
if (readCounter > writeCounter) { | |||
return 0; | |||
} | |||
uint64_t delta = writeCounter - readCounter; | |||
if (delta >= mTotalFrames) { | |||
return mTotalFrames; | |||
} | |||
// delta is now guaranteed to fit within the range of a uint32_t | |||
return static_cast<uint32_t>(delta); | |||
} | |||
uint32_t FifoControllerBase::getReadIndex() const { | |||
// % works with non-power of two sizes | |||
return static_cast<uint32_t>(getReadCounter() % mTotalFrames); | |||
} | |||
void FifoControllerBase::advanceReadIndex(uint32_t numFrames) { | |||
incrementReadCounter(numFrames); | |||
} | |||
uint32_t FifoControllerBase::getEmptyFramesAvailable() const { | |||
return static_cast<uint32_t>(mTotalFrames - getFullFramesAvailable()); | |||
} | |||
uint32_t FifoControllerBase::getWriteIndex() const { | |||
// % works with non-power of two sizes | |||
return static_cast<uint32_t>(getWriteCounter() % mTotalFrames); | |||
} | |||
void FifoControllerBase::advanceWriteIndex(uint32_t numFrames) { | |||
incrementWriteCounter(numFrames); | |||
} | |||
} // namespace oboe |
@@ -1,93 +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 NATIVEOBOE_FIFOCONTROLLERBASE_H | |||
#define NATIVEOBOE_FIFOCONTROLLERBASE_H | |||
#include <stdint.h> | |||
#include <sys/types.h> | |||
namespace oboe { | |||
/** | |||
* Manage the read/write indices of a circular buffer. | |||
* | |||
* The caller is responsible for reading and writing the actual data. | |||
* Note that the span of available frames may not be contiguous. They | |||
* may wrap around from the end to the beginning of the buffer. In that | |||
* case the data must be read or written in at least two blocks of frames. | |||
* | |||
*/ | |||
class FifoControllerBase { | |||
public: | |||
/** | |||
* @param totalFrames capacity of the circular buffer in frames. | |||
*/ | |||
FifoControllerBase(uint32_t totalFrames); | |||
virtual ~FifoControllerBase() = default; | |||
/** | |||
* The frames available to read will be calculated from the read and write counters. | |||
* The result will be clipped to the capacity of the buffer. | |||
* If the buffer has underflowed then this will return zero. | |||
* @return number of valid frames available to read. | |||
*/ | |||
uint32_t getFullFramesAvailable() const; | |||
/** | |||
* The index in a circular buffer of the next frame to read. | |||
*/ | |||
uint32_t getReadIndex() const; | |||
/** | |||
* @param numFrames number of frames to advance the read index | |||
*/ | |||
void advanceReadIndex(uint32_t numFrames); | |||
/** | |||
* @return maximum number of frames that can be written without exceeding the threshold. | |||
*/ | |||
uint32_t getEmptyFramesAvailable() const; | |||
/** | |||
* The index in a circular buffer of the next frame to write. | |||
*/ | |||
uint32_t getWriteIndex() const; | |||
/** | |||
* @param numFrames number of frames to advance the write index | |||
*/ | |||
void advanceWriteIndex(uint32_t numFrames); | |||
uint32_t getFrameCapacity() const { return mTotalFrames; } | |||
virtual uint64_t getReadCounter() const = 0; | |||
virtual void setReadCounter(uint64_t n) = 0; | |||
virtual void incrementReadCounter(uint64_t n) = 0; | |||
virtual uint64_t getWriteCounter() const = 0; | |||
virtual void setWriteCounter(uint64_t n) = 0; | |||
virtual void incrementWriteCounter(uint64_t n) = 0; | |||
private: | |||
uint32_t mTotalFrames; | |||
}; | |||
} // namespace oboe | |||
#endif //NATIVEOBOE_FIFOCONTROLLERBASE_H |
@@ -1,31 +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 "FifoControllerIndirect.h" | |||
namespace oboe { | |||
FifoControllerIndirect::FifoControllerIndirect(uint32_t numFrames, | |||
std::atomic<uint64_t> *readCounterAddress, | |||
std::atomic<uint64_t> *writeCounterAddress) | |||
: FifoControllerBase(numFrames) | |||
, mReadCounterAddress(readCounterAddress) | |||
, mWriteCounterAddress(writeCounterAddress) | |||
{ | |||
} | |||
} |
@@ -1,64 +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 NATIVEOBOE_FIFOCONTROLLERINDIRECT_H | |||
#define NATIVEOBOE_FIFOCONTROLLERINDIRECT_H | |||
#include "FifoControllerBase.h" | |||
#include <atomic> | |||
namespace oboe { | |||
/** | |||
* A FifoControllerBase with counters external to the class. | |||
*/ | |||
class FifoControllerIndirect : public FifoControllerBase { | |||
public: | |||
FifoControllerIndirect(uint32_t bufferSize, | |||
std::atomic<uint64_t> *readCounterAddress, | |||
std::atomic<uint64_t> *writeCounterAddress); | |||
virtual ~FifoControllerIndirect() = default; | |||
virtual uint64_t getReadCounter() const override { | |||
return mReadCounterAddress->load(std::memory_order_acquire); | |||
} | |||
virtual void setReadCounter(uint64_t n) override { | |||
mReadCounterAddress->store(n, std::memory_order_release); | |||
} | |||
virtual void incrementReadCounter(uint64_t n) override { | |||
mReadCounterAddress->fetch_add(n, std::memory_order_acq_rel); | |||
} | |||
virtual uint64_t getWriteCounter() const override { | |||
return mWriteCounterAddress->load(std::memory_order_acquire); | |||
} | |||
virtual void setWriteCounter(uint64_t n) override { | |||
mWriteCounterAddress->store(n, std::memory_order_release); | |||
} | |||
virtual void incrementWriteCounter(uint64_t n) override { | |||
mWriteCounterAddress->fetch_add(n, std::memory_order_acq_rel); | |||
} | |||
private: | |||
std::atomic<uint64_t> *mReadCounterAddress; | |||
std::atomic<uint64_t> *mWriteCounterAddress; | |||
}; | |||
} // namespace oboe | |||
#endif //NATIVEOBOE_FIFOCONTROLLERINDIRECT_H |
@@ -1,38 +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 <algorithm> | |||
#include <unistd.h> | |||
#include "FlowGraphNode.h" | |||
#include "ClipToRange.h" | |||
using namespace flowgraph; | |||
ClipToRange::ClipToRange(int32_t channelCount) | |||
: FlowGraphFilter(channelCount) { | |||
} | |||
int32_t ClipToRange::onProcess(int32_t numFrames) { | |||
const float *inputBuffer = input.getBuffer(); | |||
float *outputBuffer = output.getBuffer(); | |||
int32_t numSamples = numFrames * output.getSamplesPerFrame(); | |||
for (int32_t i = 0; i < numSamples; i++) { | |||
*outputBuffer++ = std::min(mMaximum, std::max(mMinimum, *inputBuffer++)); | |||
} | |||
return numFrames; | |||
} |
@@ -1,68 +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 FLOWGRAPH_CLIP_TO_RANGE_H | |||
#define FLOWGRAPH_CLIP_TO_RANGE_H | |||
#include <atomic> | |||
#include <unistd.h> | |||
#include <sys/types.h> | |||
#include "FlowGraphNode.h" | |||
namespace flowgraph { | |||
// This is 3 dB, (10^(3/20)), to match the maximum headroom in AudioTrack for float data. | |||
// It is designed to allow occasional transient peaks. | |||
constexpr float kDefaultMaxHeadroom = 1.41253754f; | |||
constexpr float kDefaultMinHeadroom = -kDefaultMaxHeadroom; | |||
class ClipToRange : public FlowGraphFilter { | |||
public: | |||
explicit ClipToRange(int32_t channelCount); | |||
virtual ~ClipToRange() = default; | |||
int32_t onProcess(int32_t numFrames) override; | |||
void setMinimum(float min) { | |||
mMinimum = min; | |||
} | |||
float getMinimum() const { | |||
return mMinimum; | |||
} | |||
void setMaximum(float min) { | |||
mMaximum = min; | |||
} | |||
float getMaximum() const { | |||
return mMaximum; | |||
} | |||
const char *getName() override { | |||
return "ClipToRange"; | |||
} | |||
private: | |||
float mMinimum = kDefaultMinHeadroom; | |||
float mMaximum = kDefaultMaxHeadroom; | |||
}; | |||
} /* namespace flowgraph */ | |||
#endif //FLOWGRAPH_CLIP_TO_RANGE_H |
@@ -1,111 +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 "stdio.h" | |||
#include <algorithm> | |||
#include <sys/types.h> | |||
#include "FlowGraphNode.h" | |||
using namespace flowgraph; | |||
/***************************************************************************/ | |||
int32_t FlowGraphNode::pullData(int64_t framePosition, int32_t numFrames) { | |||
int32_t frameCount = numFrames; | |||
// Prevent recursion and multiple execution of nodes. | |||
if (framePosition <= mLastFramePosition && !mBlockRecursion) { | |||
mBlockRecursion = true; // for cyclic graphs | |||
if (mDataPulledAutomatically) { | |||
// Pull from all the upstream nodes. | |||
for (auto &port : mInputPorts) { | |||
// TODO fix bug of leaving unused data in some ports if using multiple AudioSource | |||
frameCount = port.get().pullData(framePosition, frameCount); | |||
} | |||
} | |||
if (frameCount > 0) { | |||
frameCount = onProcess(frameCount); | |||
} | |||
mLastFramePosition += frameCount; | |||
mBlockRecursion = false; | |||
mLastFrameCount = frameCount; | |||
} else { | |||
frameCount = mLastFrameCount; | |||
} | |||
return frameCount; | |||
} | |||
void FlowGraphNode::pullReset() { | |||
if (!mBlockRecursion) { | |||
mBlockRecursion = true; // for cyclic graphs | |||
// Pull reset from all the upstream nodes. | |||
for (auto &port : mInputPorts) { | |||
port.get().pullReset(); | |||
} | |||
mBlockRecursion = false; | |||
reset(); | |||
} | |||
} | |||
void FlowGraphNode::reset() { | |||
mLastFrameCount = 0; | |||
} | |||
/***************************************************************************/ | |||
FlowGraphPortFloat::FlowGraphPortFloat(FlowGraphNode &parent, | |||
int32_t samplesPerFrame, | |||
int32_t framesPerBuffer) | |||
: FlowGraphPort(parent, samplesPerFrame) | |||
, mFramesPerBuffer(framesPerBuffer) | |||
, mBuffer(nullptr) { | |||
size_t numFloats = static_cast<size_t>(framesPerBuffer * getSamplesPerFrame()); | |||
mBuffer = std::make_unique<float[]>(numFloats); | |||
} | |||
/***************************************************************************/ | |||
int32_t FlowGraphPortFloatOutput::pullData(int64_t framePosition, int32_t numFrames) { | |||
numFrames = std::min(getFramesPerBuffer(), numFrames); | |||
return mContainingNode.pullData(framePosition, numFrames); | |||
} | |||
void FlowGraphPortFloatOutput::pullReset() { | |||
mContainingNode.pullReset(); | |||
} | |||
// These need to be in the .cpp file because of forward cross references. | |||
void FlowGraphPortFloatOutput::connect(FlowGraphPortFloatInput *port) { | |||
port->connect(this); | |||
} | |||
void FlowGraphPortFloatOutput::disconnect(FlowGraphPortFloatInput *port) { | |||
port->disconnect(this); | |||
} | |||
/***************************************************************************/ | |||
int32_t FlowGraphPortFloatInput::pullData(int64_t framePosition, int32_t numFrames) { | |||
return (mConnected == nullptr) | |||
? std::min(getFramesPerBuffer(), numFrames) | |||
: mConnected->pullData(framePosition, numFrames); | |||
} | |||
void FlowGraphPortFloatInput::pullReset() { | |||
if (mConnected != nullptr) mConnected->pullReset(); | |||
} | |||
float *FlowGraphPortFloatInput::getBuffer() { | |||
if (mConnected == nullptr) { | |||
return FlowGraphPortFloat::getBuffer(); // loaded using setValue() | |||
} else { | |||
return mConnected->getBuffer(); | |||
} | |||
} |
@@ -1,422 +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. | |||
*/ | |||
/* | |||
* FlowGraph.h | |||
* | |||
* Processing node and ports that can be used in a simple data flow graph. | |||
* This was designed to work with audio but could be used for other | |||
* types of data. | |||
*/ | |||
#ifndef FLOWGRAPH_FLOW_GRAPH_NODE_H | |||
#define FLOWGRAPH_FLOW_GRAPH_NODE_H | |||
#include <cassert> | |||
#include <cstring> | |||
#include <math.h> | |||
#include <memory> | |||
#include <sys/types.h> | |||
#include <time.h> | |||
#include <unistd.h> | |||
#include <vector> | |||
// TODO Move these classes into separate files. | |||
// TODO Review use of raw pointers for connect(). Maybe use smart pointers but need to avoid | |||
// run-time deallocation in audio thread. | |||
// Set this to 1 if using it inside the Android framework. | |||
// This code is kept here so that it can be moved easily between Oboe and AAudio. | |||
#define FLOWGRAPH_ANDROID_INTERNAL 0 | |||
namespace flowgraph { | |||
// Default block size that can be overridden when the FlowGraphPortFloat is created. | |||
// If it is too small then we will have too much overhead from switching between nodes. | |||
// If it is too high then we will thrash the caches. | |||
constexpr int kDefaultBufferSize = 8; // arbitrary | |||
class FlowGraphPort; | |||
class FlowGraphPortFloatInput; | |||
/***************************************************************************/ | |||
/** | |||
* Base class for all nodes in the flowgraph. | |||
*/ | |||
class FlowGraphNode { | |||
public: | |||
FlowGraphNode() {} | |||
virtual ~FlowGraphNode() = default; | |||
/** | |||
* Read from the input ports, | |||
* generate multiple frames of data then write the results to the output ports. | |||
* | |||
* @param numFrames maximum number of frames requested for processing | |||
* @return number of frames actually processed | |||
*/ | |||
virtual int32_t onProcess(int32_t numFrames) = 0; | |||
/** | |||
* If the framePosition is at or after the last frame position then call onProcess(). | |||
* This prevents infinite recursion in case of cyclic graphs. | |||
* It also prevents nodes upstream from a branch from being executed twice. | |||
* | |||
* @param framePosition | |||
* @param numFrames | |||
* @return number of frames valid | |||
*/ | |||
int32_t pullData(int64_t framePosition, int32_t numFrames); | |||
/** | |||
* Recursively reset all the nodes in the graph, starting from a Sink. | |||
* | |||
* This must not be called at the same time as pullData! | |||
*/ | |||
void pullReset(); | |||
/** | |||
* Reset framePosition counters. | |||
*/ | |||
virtual void reset(); | |||
void addInputPort(FlowGraphPort &port) { | |||
mInputPorts.push_back(port); | |||
} | |||
bool isDataPulledAutomatically() const { | |||
return mDataPulledAutomatically; | |||
} | |||
/** | |||
* Set true if you want the data pulled through the graph automatically. | |||
* This is the default. | |||
* | |||
* Set false if you want to pull the data from the input ports in the onProcess() method. | |||
* You might do this, for example, in a sample rate converting node. | |||
* | |||
* @param automatic | |||
*/ | |||
void setDataPulledAutomatically(bool automatic) { | |||
mDataPulledAutomatically = automatic; | |||
} | |||
virtual const char *getName() { | |||
return "FlowGraph"; | |||
} | |||
int64_t getLastFramePosition() { | |||
return mLastFramePosition; | |||
} | |||
protected: | |||
int64_t mLastFramePosition = 0; | |||
std::vector<std::reference_wrapper<FlowGraphPort>> mInputPorts; | |||
private: | |||
bool mDataPulledAutomatically = true; | |||
bool mBlockRecursion = false; | |||
int32_t mLastFrameCount = 0; | |||
}; | |||
/***************************************************************************/ | |||
/** | |||
* This is a connector that allows data to flow between modules. | |||
* | |||
* The ports are the primary means of interacting with a module. | |||
* So they are generally declared as public. | |||
* | |||
*/ | |||
class FlowGraphPort { | |||
public: | |||
FlowGraphPort(FlowGraphNode &parent, int32_t samplesPerFrame) | |||
: mContainingNode(parent) | |||
, mSamplesPerFrame(samplesPerFrame) { | |||
} | |||
// Ports are often declared public. So let's make them non-copyable. | |||
FlowGraphPort(const FlowGraphPort&) = delete; | |||
FlowGraphPort& operator=(const FlowGraphPort&) = delete; | |||
int32_t getSamplesPerFrame() const { | |||
return mSamplesPerFrame; | |||
} | |||
virtual int32_t pullData(int64_t framePosition, int32_t numFrames) = 0; | |||
virtual void pullReset() {} | |||
protected: | |||
FlowGraphNode &mContainingNode; | |||
private: | |||
const int32_t mSamplesPerFrame = 1; | |||
}; | |||
/***************************************************************************/ | |||
/** | |||
* This port contains a 32-bit float buffer that can contain several frames of data. | |||
* Processing the data in a block improves performance. | |||
* | |||
* The size is framesPerBuffer * samplesPerFrame). | |||
*/ | |||
class FlowGraphPortFloat : public FlowGraphPort { | |||
public: | |||
FlowGraphPortFloat(FlowGraphNode &parent, | |||
int32_t samplesPerFrame, | |||
int32_t framesPerBuffer = kDefaultBufferSize | |||
); | |||
virtual ~FlowGraphPortFloat() = default; | |||
int32_t getFramesPerBuffer() const { | |||
return mFramesPerBuffer; | |||
} | |||
protected: | |||
/** | |||
* @return buffer internal to the port or from a connected port | |||
*/ | |||
virtual float *getBuffer() { | |||
return mBuffer.get(); | |||
} | |||
private: | |||
const int32_t mFramesPerBuffer = 1; | |||
std::unique_ptr<float[]> mBuffer; // allocated in constructor | |||
}; | |||
/***************************************************************************/ | |||
/** | |||
* The results of a node's processing are stored in the buffers of the output ports. | |||
*/ | |||
class FlowGraphPortFloatOutput : public FlowGraphPortFloat { | |||
public: | |||
FlowGraphPortFloatOutput(FlowGraphNode &parent, int32_t samplesPerFrame) | |||
: FlowGraphPortFloat(parent, samplesPerFrame) { | |||
} | |||
virtual ~FlowGraphPortFloatOutput() = default; | |||
using FlowGraphPortFloat::getBuffer; | |||
/** | |||
* Connect to the input of another module. | |||
* An input port can only have one connection. | |||
* An output port can have multiple connections. | |||
* If you connect a second output port to an input port | |||
* then it overwrites the previous connection. | |||
* | |||
* This not thread safe. Do not modify the graph topology from another thread while running. | |||
* Also do not delete a module while it is connected to another port if the graph is running. | |||
*/ | |||
void connect(FlowGraphPortFloatInput *port); | |||
/** | |||
* Disconnect from the input of another module. | |||
* This not thread safe. | |||
*/ | |||
void disconnect(FlowGraphPortFloatInput *port); | |||
/** | |||
* Call the parent module's onProcess() method. | |||
* That may pull data from its inputs and recursively | |||
* process the entire graph. | |||
* @return number of frames actually pulled | |||
*/ | |||
int32_t pullData(int64_t framePosition, int32_t numFrames) override; | |||
void pullReset() override; | |||
}; | |||
/***************************************************************************/ | |||
/** | |||
* An input port for streaming audio data. | |||
* You can set a value that will be used for processing. | |||
* If you connect an output port to this port then its value will be used instead. | |||
*/ | |||
class FlowGraphPortFloatInput : public FlowGraphPortFloat { | |||
public: | |||
FlowGraphPortFloatInput(FlowGraphNode &parent, int32_t samplesPerFrame) | |||
: FlowGraphPortFloat(parent, samplesPerFrame) { | |||
// Add to parent so it can pull data from each input. | |||
parent.addInputPort(*this); | |||
} | |||
virtual ~FlowGraphPortFloatInput() = default; | |||
/** | |||
* If connected to an output port then this will return | |||
* that output ports buffers. | |||
* If not connected then it returns the input ports own buffer | |||
* which can be loaded using setValue(). | |||
*/ | |||
float *getBuffer() override; | |||
/** | |||
* Write every value of the float buffer. | |||
* This value will be ignored if an output port is connected | |||
* to this port. | |||
*/ | |||
void setValue(float value) { | |||
int numFloats = kDefaultBufferSize * getSamplesPerFrame(); | |||
float *buffer = getBuffer(); | |||
for (int i = 0; i < numFloats; i++) { | |||
*buffer++ = value; | |||
} | |||
} | |||
/** | |||
* Connect to the output of another module. | |||
* An input port can only have one connection. | |||
* An output port can have multiple connections. | |||
* This not thread safe. | |||
*/ | |||
void connect(FlowGraphPortFloatOutput *port) { | |||
assert(getSamplesPerFrame() == port->getSamplesPerFrame()); | |||
mConnected = port; | |||
} | |||
void disconnect(FlowGraphPortFloatOutput *port) { | |||
assert(mConnected == port); | |||
(void) port; | |||
mConnected = nullptr; | |||
} | |||
void disconnect() { | |||
mConnected = nullptr; | |||
} | |||
/** | |||
* Pull data from any output port that is connected. | |||
*/ | |||
int32_t pullData(int64_t framePosition, int32_t numFrames) override; | |||
void pullReset() override; | |||
private: | |||
FlowGraphPortFloatOutput *mConnected = nullptr; | |||
}; | |||
/***************************************************************************/ | |||
/** | |||
* Base class for an edge node in a graph that has no upstream nodes. | |||
* It outputs data but does not consume data. | |||
* By default, it will read its data from an external buffer. | |||
*/ | |||
class FlowGraphSource : public FlowGraphNode { | |||
public: | |||
explicit FlowGraphSource(int32_t channelCount) | |||
: output(*this, channelCount) { | |||
} | |||
virtual ~FlowGraphSource() = default; | |||
FlowGraphPortFloatOutput output; | |||
}; | |||
/***************************************************************************/ | |||
/** | |||
* Base class for an edge node in a graph that has no upstream nodes. | |||
* It outputs data but does not consume data. | |||
* By default, it will read its data from an external buffer. | |||
*/ | |||
class FlowGraphSourceBuffered : public FlowGraphSource { | |||
public: | |||
explicit FlowGraphSourceBuffered(int32_t channelCount) | |||
: FlowGraphSource(channelCount) {} | |||
virtual ~FlowGraphSourceBuffered() = default; | |||
/** | |||
* Specify buffer that the node will read from. | |||
* | |||
* @param data TODO Consider using std::shared_ptr. | |||
* @param numFrames | |||
*/ | |||
void setData(const void *data, int32_t numFrames) { | |||
mData = data; | |||
mSizeInFrames = numFrames; | |||
mFrameIndex = 0; | |||
} | |||
protected: | |||
const void *mData = nullptr; | |||
int32_t mSizeInFrames = 0; // number of frames in mData | |||
int32_t mFrameIndex = 0; // index of next frame to be processed | |||
}; | |||
/***************************************************************************/ | |||
/** | |||
* Base class for an edge node in a graph that has no downstream nodes. | |||
* It consumes data but does not output data. | |||
* This graph will be executed when data is read() from this node | |||
* by pulling data from upstream nodes. | |||
*/ | |||
class FlowGraphSink : public FlowGraphNode { | |||
public: | |||
explicit FlowGraphSink(int32_t channelCount) | |||
: input(*this, channelCount) { | |||
} | |||
virtual ~FlowGraphSink() = default; | |||
FlowGraphPortFloatInput input; | |||
/** | |||
* Dummy processor. The work happens in the read() method. | |||
* | |||
* @param numFrames | |||
* @return number of frames actually processed | |||
*/ | |||
int32_t onProcess(int32_t numFrames) override { | |||
return numFrames; | |||
} | |||
virtual int32_t read(int64_t framePosition, void *data, int32_t numFrames) = 0; | |||
}; | |||
/***************************************************************************/ | |||
/** | |||
* Base class for a node that has an input and an output with the same number of channels. | |||
* This may include traditional filters, eg. FIR, but also include | |||
* any processing node that converts input to output. | |||
*/ | |||
class FlowGraphFilter : public FlowGraphNode { | |||
public: | |||
explicit FlowGraphFilter(int32_t channelCount) | |||
: input(*this, channelCount) | |||
, output(*this, channelCount) { | |||
} | |||
virtual ~FlowGraphFilter() = default; | |||
FlowGraphPortFloatInput input; | |||
FlowGraphPortFloatOutput output; | |||
}; | |||
} /* namespace flowgraph */ | |||
#endif /* FLOWGRAPH_FLOW_GRAPH_NODE_H */ |
@@ -1,47 +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 <unistd.h> | |||
#include "ManyToMultiConverter.h" | |||
using namespace flowgraph; | |||
ManyToMultiConverter::ManyToMultiConverter(int32_t channelCount) | |||
: inputs(channelCount) | |||
, output(*this, channelCount) { | |||
for (int i = 0; i < channelCount; i++) { | |||
inputs[i] = std::make_unique<FlowGraphPortFloatInput>(*this, 1); | |||
} | |||
} | |||
int32_t ManyToMultiConverter::onProcess(int32_t numFrames) { | |||
int32_t channelCount = output.getSamplesPerFrame(); | |||
for (int ch = 0; ch < channelCount; ch++) { | |||
const float *inputBuffer = inputs[ch]->getBuffer(); | |||
float *outputBuffer = output.getBuffer() + ch; | |||
for (int i = 0; i < numFrames; i++) { | |||
// read one, write into the proper interleaved output channel | |||
float sample = *inputBuffer++; | |||
*outputBuffer = sample; | |||
outputBuffer += channelCount; // advance to next multichannel frame | |||
} | |||
} | |||
return numFrames; | |||
} | |||
@@ -1,49 +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. | |||
*/ | |||
#ifndef FLOWGRAPH_MANY_TO_MULTI_CONVERTER_H | |||
#define FLOWGRAPH_MANY_TO_MULTI_CONVERTER_H | |||
#include <unistd.h> | |||
#include <sys/types.h> | |||
#include <vector> | |||
#include "FlowGraphNode.h" | |||
/** | |||
* Combine multiple mono inputs into one interleaved multi-channel output. | |||
*/ | |||
class ManyToMultiConverter : public flowgraph::FlowGraphNode { | |||
public: | |||
explicit ManyToMultiConverter(int32_t channelCount); | |||
virtual ~ManyToMultiConverter() = default; | |||
int32_t onProcess(int numFrames) override; | |||
void setEnabled(bool enabled) {} | |||
std::vector<std::unique_ptr<flowgraph::FlowGraphPortFloatInput>> inputs; | |||
flowgraph::FlowGraphPortFloatOutput output; | |||
const char *getName() override { | |||
return "ManyToMultiConverter"; | |||
} | |||
private: | |||
}; | |||
#endif //FLOWGRAPH_MANY_TO_MULTI_CONVERTER_H |
@@ -1,43 +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 <unistd.h> | |||
#include "FlowGraphNode.h" | |||
#include "MonoToMultiConverter.h" | |||
using namespace flowgraph; | |||
MonoToMultiConverter::MonoToMultiConverter(int32_t channelCount) | |||
: input(*this, 1) | |||
, output(*this, channelCount) { | |||
} | |||
MonoToMultiConverter::~MonoToMultiConverter() { } | |||
int32_t MonoToMultiConverter::onProcess(int32_t numFrames) { | |||
const float *inputBuffer = input.getBuffer(); | |||
float *outputBuffer = output.getBuffer(); | |||
int32_t channelCount = output.getSamplesPerFrame(); | |||
for (int i = 0; i < numFrames; i++) { | |||
// read one, write many | |||
float sample = *inputBuffer++; | |||
for (int channel = 0; channel < channelCount; channel++) { | |||
*outputBuffer++ = sample; | |||
} | |||
} | |||
return numFrames; | |||
} | |||
@@ -1,49 +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 FLOWGRAPH_MONO_TO_MULTI_CONVERTER_H | |||
#define FLOWGRAPH_MONO_TO_MULTI_CONVERTER_H | |||
#include <unistd.h> | |||
#include <sys/types.h> | |||
#include "FlowGraphNode.h" | |||
namespace flowgraph { | |||
/** | |||
* Convert a monophonic stream to a multi-channel stream | |||
* with the same signal on each channel. | |||
*/ | |||
class MonoToMultiConverter : public FlowGraphNode { | |||
public: | |||
explicit MonoToMultiConverter(int32_t channelCount); | |||
virtual ~MonoToMultiConverter(); | |||
int32_t onProcess(int32_t numFrames) override; | |||
const char *getName() override { | |||
return "MonoToMultiConverter"; | |||
} | |||
FlowGraphPortFloatInput input; | |||
FlowGraphPortFloatOutput output; | |||
}; | |||
} /* namespace flowgraph */ | |||
#endif //FLOWGRAPH_MONO_TO_MULTI_CONVERTER_H |
@@ -1,77 +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 <algorithm> | |||
#include <unistd.h> | |||
#include "FlowGraphNode.h" | |||
#include "RampLinear.h" | |||
using namespace flowgraph; | |||
RampLinear::RampLinear(int32_t channelCount) | |||
: FlowGraphFilter(channelCount) { | |||
mTarget.store(1.0f); | |||
} | |||
void RampLinear::setLengthInFrames(int32_t frames) { | |||
mLengthInFrames = frames; | |||
} | |||
void RampLinear::setTarget(float target) { | |||
mTarget.store(target); | |||
} | |||
float RampLinear::interpolateCurrent() { | |||
return mLevelTo - (mRemaining * mScaler); | |||
} | |||
int32_t RampLinear::onProcess(int32_t numFrames) { | |||
const float *inputBuffer = input.getBuffer(); | |||
float *outputBuffer = output.getBuffer(); | |||
int32_t channelCount = output.getSamplesPerFrame(); | |||
float target = getTarget(); | |||
if (target != mLevelTo) { | |||
// Start new ramp. Continue from previous level. | |||
mLevelFrom = interpolateCurrent(); | |||
mLevelTo = target; | |||
mRemaining = mLengthInFrames; | |||
mScaler = (mLevelTo - mLevelFrom) / mLengthInFrames; // for interpolation | |||
} | |||
int32_t framesLeft = numFrames; | |||
if (mRemaining > 0) { // Ramping? This doesn't happen very often. | |||
int32_t framesToRamp = std::min(framesLeft, mRemaining); | |||
framesLeft -= framesToRamp; | |||
while (framesToRamp > 0) { | |||
float currentLevel = interpolateCurrent(); | |||
for (int ch = 0; ch < channelCount; ch++) { | |||
*outputBuffer++ = *inputBuffer++ * currentLevel; | |||
} | |||
mRemaining--; | |||
framesToRamp--; | |||
} | |||
} | |||
// Process any frames after the ramp. | |||
int32_t samplesLeft = framesLeft * channelCount; | |||
for (int i = 0; i < samplesLeft; i++) { | |||
*outputBuffer++ = *inputBuffer++ * mLevelTo; | |||
} | |||
return numFrames; | |||
} |
@@ -1,96 +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 FLOWGRAPH_RAMP_LINEAR_H | |||
#define FLOWGRAPH_RAMP_LINEAR_H | |||
#include <atomic> | |||
#include <unistd.h> | |||
#include <sys/types.h> | |||
#include "FlowGraphNode.h" | |||
namespace flowgraph { | |||
/** | |||
* When the target is modified then the output will ramp smoothly | |||
* between the original and the new target value. | |||
* This can be used to smooth out control values and reduce pops. | |||
* | |||
* The target may be updated while a ramp is in progress, which will trigger | |||
* a new ramp from the current value. | |||
*/ | |||
class RampLinear : public FlowGraphFilter { | |||
public: | |||
explicit RampLinear(int32_t channelCount); | |||
virtual ~RampLinear() = default; | |||
int32_t onProcess(int32_t numFrames) override; | |||
/** | |||
* This is used for the next ramp. | |||
* Calling this does not affect a ramp that is in progress. | |||
*/ | |||
void setLengthInFrames(int32_t frames); | |||
int32_t getLengthInFrames() const { | |||
return mLengthInFrames; | |||
} | |||
/** | |||
* This may be safely called by another thread. | |||
* @param target | |||
*/ | |||
void setTarget(float target); | |||
float getTarget() const { | |||
return mTarget.load(); | |||
} | |||
/** | |||
* Force the nextSegment to start from this level. | |||
* | |||
* WARNING: this can cause a discontinuity if called while the ramp is being used. | |||
* Only call this when setting the initial ramp. | |||
* | |||
* @param level | |||
*/ | |||
void forceCurrent(float level) { | |||
mLevelFrom = level; | |||
mLevelTo = level; | |||
} | |||
const char *getName() override { | |||
return "RampLinear"; | |||
} | |||
private: | |||
float interpolateCurrent(); | |||
std::atomic<float> mTarget; | |||
int32_t mLengthInFrames = 48000.0f / 100.0f ; // 10 msec at 48000 Hz; | |||
int32_t mRemaining = 0; | |||
float mScaler = 0.0f; | |||
float mLevelFrom = 0.0f; | |||
float mLevelTo = 0.0f; | |||
}; | |||
} /* namespace flowgraph */ | |||
#endif //FLOWGRAPH_RAMP_LINEAR_H |
@@ -1,64 +0,0 @@ | |||
/* | |||
* Copyright 2019 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 "SampleRateConverter.h" | |||
using namespace flowgraph; | |||
using namespace resampler; | |||
SampleRateConverter::SampleRateConverter(int32_t channelCount, MultiChannelResampler &resampler) | |||
: FlowGraphFilter(channelCount) | |||
, mResampler(resampler) { | |||
setDataPulledAutomatically(false); | |||
} | |||
// Return true if there is a sample available. | |||
bool SampleRateConverter::isInputAvailable() { | |||
if (mInputCursor >= mNumValidInputFrames) { | |||
mNumValidInputFrames = input.pullData(mInputFramePosition, input.getFramesPerBuffer()); | |||
mInputFramePosition += mNumValidInputFrames; | |||
mInputCursor = 0; | |||
} | |||
return (mInputCursor < mNumValidInputFrames); | |||
} | |||
const float *SampleRateConverter::getNextInputFrame() { | |||
const float *inputBuffer = input.getBuffer(); | |||
return &inputBuffer[mInputCursor++ * input.getSamplesPerFrame()]; | |||
} | |||
int32_t SampleRateConverter::onProcess(int32_t numFrames) { | |||
float *outputBuffer = output.getBuffer(); | |||
int32_t channelCount = output.getSamplesPerFrame(); | |||
int framesLeft = numFrames; | |||
while (framesLeft > 0) { | |||
// Gather input samples as needed. | |||
if(mResampler.isWriteNeeded()) { | |||
if (isInputAvailable()) { | |||
const float *frame = getNextInputFrame(); | |||
mResampler.writeNextFrame(frame); | |||
} else { | |||
break; | |||
} | |||
} else { | |||
// Output frame is interpolated from input samples. | |||
mResampler.readNextFrame(outputBuffer); | |||
outputBuffer += channelCount; | |||
framesLeft--; | |||
} | |||
} | |||
return numFrames - framesLeft; | |||
} |
@@ -1,56 +0,0 @@ | |||
/* | |||
* Copyright 2019 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_SAMPLE_RATE_CONVERTER_H | |||
#define OBOE_SAMPLE_RATE_CONVERTER_H | |||
#include <unistd.h> | |||
#include <sys/types.h> | |||
#include "FlowGraphNode.h" | |||
#include "resampler/MultiChannelResampler.h" | |||
namespace flowgraph { | |||
class SampleRateConverter : public FlowGraphFilter { | |||
public: | |||
explicit SampleRateConverter(int32_t channelCount, resampler::MultiChannelResampler &mResampler); | |||
virtual ~SampleRateConverter() = default; | |||
int32_t onProcess(int32_t numFrames) override; | |||
const char *getName() override { | |||
return "SampleRateConverter"; | |||
} | |||
private: | |||
// Return true if there is a sample available. | |||
bool isInputAvailable(); | |||
// This assumes data is available. Only call after calling isInputAvailable(). | |||
const float *getNextInputFrame(); | |||
resampler::MultiChannelResampler &mResampler; | |||
int32_t mInputCursor = 0; | |||
int32_t mNumValidInputFrames = 0; | |||
int64_t mInputFramePosition = 0; // monotonic counter of input frames used for pullData | |||
}; | |||
} /* namespace flowgraph */ | |||
#endif //OBOE_SAMPLE_RATE_CONVERTER_H |
@@ -1,50 +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 <algorithm> | |||
#include <unistd.h> | |||
#include "FlowGraphNode.h" | |||
#include "SinkFloat.h" | |||
using namespace flowgraph; | |||
SinkFloat::SinkFloat(int32_t channelCount) | |||
: FlowGraphSink(channelCount) { | |||
} | |||
int32_t SinkFloat::read(int64_t framePosition, void *data, int32_t numFrames) { | |||
// printf("SinkFloat::read(,,%d)\n", numFrames); | |||
float *floatData = (float *) data; | |||
int32_t channelCount = input.getSamplesPerFrame(); | |||
int32_t framesLeft = numFrames; | |||
while (framesLeft > 0) { | |||
// Run the graph and pull data through the input port. | |||
int32_t framesPulled = pullData(framePosition, framesLeft); | |||
// printf("SinkFloat::read: framesLeft = %d, framesPulled = %d\n", framesLeft, framesPulled); | |||
if (framesPulled <= 0) { | |||
break; | |||
} | |||
const float *signal = input.getBuffer(); | |||
int32_t numSamples = framesPulled * channelCount; | |||
memcpy(floatData, signal, numSamples * sizeof(float)); | |||
floatData += numSamples; | |||
framesLeft -= framesPulled; | |||
framePosition += framesPulled; | |||
} | |||
// printf("SinkFloat returning %d\n", numFrames - framesLeft); | |||
return numFrames - framesLeft; | |||
} |
@@ -1,44 +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. | |||
*/ | |||
#ifndef FLOWGRAPH_SINK_FLOAT_H | |||
#define FLOWGRAPH_SINK_FLOAT_H | |||
#include <unistd.h> | |||
#include <sys/types.h> | |||
#include "FlowGraphNode.h" | |||
namespace flowgraph { | |||
/** | |||
* AudioSink that lets you read data as 32-bit floats. | |||
*/ | |||
class SinkFloat : public FlowGraphSink { | |||
public: | |||
explicit SinkFloat(int32_t channelCount); | |||
int32_t read(int64_t framePosition, void *data, int32_t numFrames) override; | |||
const char *getName() override { | |||
return "SinkFloat"; | |||
} | |||
}; | |||
} /* namespace flowgraph */ | |||
#endif //FLOWGRAPH_SINK_FLOAT_H |
@@ -1,58 +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 <algorithm> | |||
#include <unistd.h> | |||
#include "SinkI16.h" | |||
#if FLOWGRAPH_ANDROID_INTERNAL | |||
#include <audio_utils/primitives.h> | |||
#endif | |||
using namespace flowgraph; | |||
SinkI16::SinkI16(int32_t channelCount) | |||
: FlowGraphSink(channelCount) {} | |||
int32_t SinkI16::read(int64_t framePosition, void *data, int32_t numFrames) { | |||
int16_t *shortData = (int16_t *) data; | |||
const int32_t channelCount = input.getSamplesPerFrame(); | |||
int32_t framesLeft = numFrames; | |||
while (framesLeft > 0) { | |||
// Run the graph and pull data through the input port. | |||
int32_t framesRead = pullData(framePosition, framesLeft); | |||
if (framesRead <= 0) { | |||
break; | |||
} | |||
const float *signal = input.getBuffer(); | |||
int32_t numSamples = framesRead * channelCount; | |||
#if FLOWGRAPH_ANDROID_INTERNAL | |||
memcpy_to_i16_from_float(shortData, signal, numSamples); | |||
shortData += numSamples; | |||
signal += numSamples; | |||
#else | |||
for (int i = 0; i < numSamples; i++) { | |||
int32_t n = (int32_t) (*signal++ * 32768.0f); | |||
*shortData++ = std::min(INT16_MAX, std::max(INT16_MIN, n)); // clip | |||
} | |||
#endif | |||
framesLeft -= framesRead; | |||
framePosition += framesRead; | |||
} | |||
return numFrames - framesLeft; | |||
} |
@@ -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. | |||
*/ | |||
#ifndef FLOWGRAPH_SINK_I16_H | |||
#define FLOWGRAPH_SINK_I16_H | |||
#include <unistd.h> | |||
#include <sys/types.h> | |||
#include "FlowGraphNode.h" | |||
namespace flowgraph { | |||
/** | |||
* AudioSink that lets you read data as 16-bit signed integers. | |||
*/ | |||
class SinkI16 : public FlowGraphSink { | |||
public: | |||
explicit SinkI16(int32_t channelCount); | |||
int32_t read(int64_t framePosition, void *data, int32_t numFrames) override; | |||
const char *getName() override { | |||
return "SinkI16"; | |||
} | |||
}; | |||
} /* namespace flowgraph */ | |||
#endif //FLOWGRAPH_SINK_I16_H |
@@ -1,67 +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 <algorithm> | |||
#include <unistd.h> | |||
#include "FlowGraphNode.h" | |||
#include "SinkI24.h" | |||
#if FLOWGRAPH_ANDROID_INTERNAL | |||
#include <audio_utils/primitives.h> | |||
#endif | |||
using namespace flowgraph; | |||
SinkI24::SinkI24(int32_t channelCount) | |||
: FlowGraphSink(channelCount) {} | |||
int32_t SinkI24::read(int64_t framePosition, void *data, int32_t numFrames) { | |||
uint8_t *byteData = (uint8_t *) data; | |||
const int32_t channelCount = input.getSamplesPerFrame(); | |||
int32_t framesLeft = numFrames; | |||
while (framesLeft > 0) { | |||
// Run the graph and pull data through the input port. | |||
int32_t framesRead = pullData(framePosition, framesLeft); | |||
if (framesRead <= 0) { | |||
break; | |||
} | |||
const float *floatData = input.getBuffer(); | |||
int32_t numSamples = framesRead * channelCount; | |||
#if FLOWGRAPH_ANDROID_INTERNAL | |||
memcpy_to_p24_from_float(byteData, floatData, numSamples); | |||
static const int kBytesPerI24Packed = 3; | |||
byteData += numSamples * kBytesPerI24Packed; | |||
floatData += numSamples; | |||
#else | |||
const int32_t kI24PackedMax = 0x007FFFFF; | |||
const int32_t kI24PackedMin = 0xFF800000; | |||
for (int i = 0; i < numSamples; i++) { | |||
int32_t n = (int32_t) (*floatData++ * 0x00800000); | |||
n = std::min(kI24PackedMax, std::max(kI24PackedMin, n)); // clip | |||
// Write as a packed 24-bit integer in Little Endian format. | |||
*byteData++ = (uint8_t) n; | |||
*byteData++ = (uint8_t) (n >> 8); | |||
*byteData++ = (uint8_t) (n >> 16); | |||
} | |||
#endif | |||
framesLeft -= framesRead; | |||
framePosition += framesRead; | |||
} | |||
return numFrames - framesLeft; | |||
} |
@@ -1,44 +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. | |||
*/ | |||
#ifndef FLOWGRAPH_SINK_I24_H | |||
#define FLOWGRAPH_SINK_I24_H | |||
#include <unistd.h> | |||
#include <sys/types.h> | |||
#include "FlowGraphNode.h" | |||
namespace flowgraph { | |||
/** | |||
* AudioSink that lets you read data as packed 24-bit signed integers. | |||
* The sample size is 3 bytes. | |||
*/ | |||
class SinkI24 : public FlowGraphSink { | |||
public: | |||
explicit SinkI24(int32_t channelCount); | |||
int32_t read(int64_t framePosition, void *data, int32_t numFrames) override; | |||
const char *getName() override { | |||
return "SinkI24"; | |||
} | |||
}; | |||
} /* namespace flowgraph */ | |||
#endif //FLOWGRAPH_SINK_I24_H |
@@ -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 "common/OboeDebug.h" | |||
#include <algorithm> | |||
#include <unistd.h> | |||
#include "FlowGraphNode.h" | |||
#include "SourceFloat.h" | |||
using namespace flowgraph; | |||
SourceFloat::SourceFloat(int32_t channelCount) | |||
: FlowGraphSourceBuffered(channelCount) { | |||
} | |||
int32_t SourceFloat::onProcess(int32_t numFrames) { | |||
float *outputBuffer = output.getBuffer(); | |||
int32_t channelCount = output.getSamplesPerFrame(); | |||
int32_t framesLeft = mSizeInFrames - mFrameIndex; | |||
int32_t framesToProcess = std::min(numFrames, framesLeft); | |||
int32_t numSamples = framesToProcess * channelCount; | |||
const float *floatBase = (float *) mData; | |||
const float *floatData = &floatBase[mFrameIndex * channelCount]; | |||
memcpy(outputBuffer, floatData, numSamples * sizeof(float)); | |||
mFrameIndex += framesToProcess; | |||
return framesToProcess; | |||
} | |||
@@ -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. | |||
*/ | |||
#ifndef FLOWGRAPH_SOURCE_FLOAT_H | |||
#define FLOWGRAPH_SOURCE_FLOAT_H | |||
#include <unistd.h> | |||
#include <sys/types.h> | |||
#include "FlowGraphNode.h" | |||
namespace flowgraph { | |||
/** | |||
* AudioSource that reads a block of pre-defined float data. | |||
*/ | |||
class SourceFloat : public FlowGraphSourceBuffered { | |||
public: | |||
explicit SourceFloat(int32_t channelCount); | |||
int32_t onProcess(int32_t numFrames) override; | |||
const char *getName() override { | |||
return "SourceFloat"; | |||
} | |||
}; | |||
} /* namespace flowgraph */ | |||
#endif //FLOWGRAPH_SOURCE_FLOAT_H |
@@ -1,54 +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 <algorithm> | |||
#include <unistd.h> | |||
#include "FlowGraphNode.h" | |||
#include "SourceI16.h" | |||
#if FLOWGRAPH_ANDROID_INTERNAL | |||
#include <audio_utils/primitives.h> | |||
#endif | |||
using namespace flowgraph; | |||
SourceI16::SourceI16(int32_t channelCount) | |||
: FlowGraphSourceBuffered(channelCount) { | |||
} | |||
int32_t SourceI16::onProcess(int32_t numFrames) { | |||
float *floatData = output.getBuffer(); | |||
int32_t channelCount = output.getSamplesPerFrame(); | |||
int32_t framesLeft = mSizeInFrames - mFrameIndex; | |||
int32_t framesToProcess = std::min(numFrames, framesLeft); | |||
int32_t numSamples = framesToProcess * channelCount; | |||
const int16_t *shortBase = static_cast<const int16_t *>(mData); | |||
const int16_t *shortData = &shortBase[mFrameIndex * channelCount]; | |||
#if FLOWGRAPH_ANDROID_INTERNAL | |||
memcpy_to_float_from_i16(floatData, shortData, numSamples); | |||
#else | |||
for (int i = 0; i < numSamples; i++) { | |||
*floatData++ = *shortData++ * (1.0f / 32768); | |||
} | |||
#endif | |||
mFrameIndex += framesToProcess; | |||
return framesToProcess; | |||
} |
@@ -1,42 +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. | |||
*/ | |||
#ifndef FLOWGRAPH_SOURCE_I16_H | |||
#define FLOWGRAPH_SOURCE_I16_H | |||
#include <unistd.h> | |||
#include <sys/types.h> | |||
#include "FlowGraphNode.h" | |||
namespace flowgraph { | |||
/** | |||
* AudioSource that reads a block of pre-defined 16-bit integer data. | |||
*/ | |||
class SourceI16 : public FlowGraphSourceBuffered { | |||
public: | |||
explicit SourceI16(int32_t channelCount); | |||
int32_t onProcess(int32_t numFrames) override; | |||
const char *getName() override { | |||
return "SourceI16"; | |||
} | |||
}; | |||
} /* namespace flowgraph */ | |||
#endif //FLOWGRAPH_SOURCE_I16_H |
@@ -1,65 +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 <algorithm> | |||
#include <unistd.h> | |||
#if FLOWGRAPH_ANDROID_INTERNAL | |||
#include <audio_utils/primitives.h> | |||
#endif | |||
#include "FlowGraphNode.h" | |||
#include "SourceI24.h" | |||
using namespace flowgraph; | |||
constexpr int kBytesPerI24Packed = 3; | |||
SourceI24::SourceI24(int32_t channelCount) | |||
: FlowGraphSourceBuffered(channelCount) { | |||
} | |||
int32_t SourceI24::onProcess(int32_t numFrames) { | |||
float *floatData = output.getBuffer(); | |||
int32_t channelCount = output.getSamplesPerFrame(); | |||
int32_t framesLeft = mSizeInFrames - mFrameIndex; | |||
int32_t framesToProcess = std::min(numFrames, framesLeft); | |||
int32_t numSamples = framesToProcess * channelCount; | |||
const uint8_t *byteBase = (uint8_t *) mData; | |||
const uint8_t *byteData = &byteBase[mFrameIndex * channelCount * kBytesPerI24Packed]; | |||
#if FLOWGRAPH_ANDROID_INTERNAL | |||
memcpy_to_float_from_p24(floatData, byteData, numSamples); | |||
#else | |||
static const float scale = 1. / (float)(1UL << 31); | |||
for (int i = 0; i < numSamples; i++) { | |||
// Assemble the data assuming Little Endian format. | |||
int32_t pad = byteData[2]; | |||
pad <<= 8; | |||
pad |= byteData[1]; | |||
pad <<= 8; | |||
pad |= byteData[0]; | |||
pad <<= 8; // Shift to 32 bit data so the sign is correct. | |||
byteData += kBytesPerI24Packed; | |||
*floatData++ = pad * scale; // scale to range -1.0 to 1.0 | |||
} | |||
#endif | |||
mFrameIndex += framesToProcess; | |||
return framesToProcess; | |||
} |
@@ -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. | |||
*/ | |||
#ifndef FLOWGRAPH_SOURCE_I24_H | |||
#define FLOWGRAPH_SOURCE_I24_H | |||
#include <unistd.h> | |||
#include <sys/types.h> | |||
#include "FlowGraphNode.h" | |||
namespace flowgraph { | |||
/** | |||
* AudioSource that reads a block of pre-defined 24-bit packed integer data. | |||
*/ | |||
class SourceI24 : public FlowGraphSourceBuffered { | |||
public: | |||
explicit SourceI24(int32_t channelCount); | |||
int32_t onProcess(int32_t numFrames) override; | |||
const char *getName() override { | |||
return "SourceI24"; | |||
} | |||
}; | |||
} /* namespace flowgraph */ | |||
#endif //FLOWGRAPH_SOURCE_I24_H |
@@ -1,68 +0,0 @@ | |||
/* | |||
* Copyright 2019 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 RESAMPLER_HYPERBOLIC_COSINE_WINDOW_H | |||
#define RESAMPLER_HYPERBOLIC_COSINE_WINDOW_H | |||
#include <math.h> | |||
namespace resampler { | |||
/** | |||
* Calculate a HyperbolicCosineWindow window centered at 0. | |||
* This can be used in place of a Kaiser window. | |||
* | |||
* The code is based on an anonymous contribution by "a concerned citizen": | |||
* https://dsp.stackexchange.com/questions/37714/kaiser-window-approximation | |||
*/ | |||
class HyperbolicCosineWindow { | |||
public: | |||
HyperbolicCosineWindow() { | |||
setStopBandAttenuation(60); | |||
} | |||
/** | |||
* @param attenuation typical values range from 30 to 90 dB | |||
* @return beta | |||
*/ | |||
double setStopBandAttenuation(double attenuation) { | |||
double alpha = ((-325.1e-6 * attenuation + 0.1677) * attenuation) - 3.149; | |||
setAlpha(alpha); | |||
return alpha; | |||
} | |||
void setAlpha(double alpha) { | |||
mAlpha = alpha; | |||
mInverseCoshAlpha = 1.0 / cosh(alpha); | |||
} | |||
/** | |||
* @param x ranges from -1.0 to +1.0 | |||
*/ | |||
double operator()(double x) { | |||
double x2 = x * x; | |||
if (x2 >= 1.0) return 0.0; | |||
double w = mAlpha * sqrt(1.0 - x2); | |||
return cosh(w) * mInverseCoshAlpha; | |||
} | |||
private: | |||
double mAlpha = 0.0; | |||
double mInverseCoshAlpha = 1.0; | |||
}; | |||
} // namespace resampler | |||
#endif //RESAMPLER_HYPERBOLIC_COSINE_WINDOW_H |
@@ -1,52 +0,0 @@ | |||
/* | |||
* Copyright 2019 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 <vector> | |||
#include "IntegerRatio.h" | |||
using namespace resampler; | |||
// Enough primes to cover the common sample rates. | |||
const std::vector<int> IntegerRatio::kPrimes{ | |||
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, | |||
43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, | |||
101, 103, 107, 109, 113, 127, 131, 137, 139, 149, | |||
151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199}; | |||
void IntegerRatio::reduce() { | |||
for (int prime : kPrimes) { | |||
if (mNumerator < prime || mDenominator < prime) { | |||
break; | |||
} | |||
// Find biggest prime factor for numerator. | |||
while (true) { | |||
int top = mNumerator / prime; | |||
int bottom = mDenominator / prime; | |||
if ((top >= 1) | |||
&& (bottom >= 1) | |||
&& (top * prime == mNumerator) // divided evenly? | |||
&& (bottom * prime == mDenominator)) { | |||
mNumerator = top; | |||
mDenominator = bottom; | |||
} else { | |||
break; | |||
} | |||
} | |||
} | |||
} |
@@ -1,54 +0,0 @@ | |||
/* | |||
* Copyright 2019 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_INTEGER_RATIO_H | |||
#define OBOE_INTEGER_RATIO_H | |||
#include <sys/types.h> | |||
#include <vector> | |||
namespace resampler { | |||
/** | |||
* Represent the ratio of two integers. | |||
*/ | |||
class IntegerRatio { | |||
public: | |||
IntegerRatio(int32_t numerator, int32_t denominator) | |||
: mNumerator(numerator), mDenominator(denominator) {} | |||
/** | |||
* Reduce by removing common prime factors. | |||
*/ | |||
void reduce(); | |||
int32_t getNumerator() { | |||
return mNumerator; | |||
} | |||
int32_t getDenominator() { | |||
return mDenominator; | |||
} | |||
private: | |||
int32_t mNumerator; | |||
int32_t mDenominator; | |||
static const std::vector<int> kPrimes; | |||
}; | |||
} | |||
#endif //OBOE_INTEGER_RATIO_H |
@@ -1,87 +0,0 @@ | |||
/* | |||
* Copyright 2019 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 RESAMPLER_KAISER_WINDOW_H | |||
#define RESAMPLER_KAISER_WINDOW_H | |||
#include <math.h> | |||
namespace resampler { | |||
/** | |||
* Calculate a Kaiser window centered at 0. | |||
*/ | |||
class KaiserWindow { | |||
public: | |||
KaiserWindow() { | |||
setStopBandAttenuation(60); | |||
} | |||
/** | |||
* @param attenuation typical values range from 30 to 90 dB | |||
* @return beta | |||
*/ | |||
double setStopBandAttenuation(double attenuation) { | |||
double beta = 0.0; | |||
if (attenuation > 50) { | |||
beta = 0.1102 * (attenuation - 8.7); | |||
} else if (attenuation >= 21) { | |||
double a21 = attenuation - 21; | |||
beta = 0.5842 * pow(a21, 0.4) + (0.07886 * a21); | |||
} | |||
setBeta(beta); | |||
return beta; | |||
} | |||
void setBeta(double beta) { | |||
mBeta = beta; | |||
mInverseBesselBeta = 1.0 / bessel(beta); | |||
} | |||
/** | |||
* @param x ranges from -1.0 to +1.0 | |||
*/ | |||
double operator()(double x) { | |||
double x2 = x * x; | |||
if (x2 >= 1.0) return 0.0; | |||
double w = mBeta * sqrt(1.0 - x2); | |||
return bessel(w) * mInverseBesselBeta; | |||
} | |||
// Approximation of a | |||
// modified zero order Bessel function of the first kind. | |||
// Based on a discussion at: | |||
// https://dsp.stackexchange.com/questions/37714/kaiser-window-approximation | |||
static double bessel(double x) { | |||
double y = cosh(0.970941817426052 * x); | |||
y += cosh(0.8854560256532099 * x); | |||
y += cosh(0.7485107481711011 * x); | |||
y += cosh(0.5680647467311558 * x); | |||
y += cosh(0.3546048870425356 * x); | |||
y += cosh(0.120536680255323 * x); | |||
y *= 2; | |||
y += cosh(x); | |||
y /= 13; | |||
return y; | |||
} | |||
private: | |||
double mBeta = 0.0; | |||
double mInverseBesselBeta = 1.0; | |||
}; | |||
} // namespace resampler | |||
#endif //RESAMPLER_KAISER_WINDOW_H |
@@ -1,42 +0,0 @@ | |||
/* | |||
* Copyright 2019 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 "LinearResampler.h" | |||
using namespace resampler; | |||
LinearResampler::LinearResampler(const MultiChannelResampler::Builder &builder) | |||
: MultiChannelResampler(builder) { | |||
mPreviousFrame = std::make_unique<float[]>(getChannelCount()); | |||
mCurrentFrame = std::make_unique<float[]>(getChannelCount()); | |||
} | |||
void LinearResampler::writeFrame(const float *frame) { | |||
memcpy(mPreviousFrame.get(), mCurrentFrame.get(), sizeof(float) * getChannelCount()); | |||
memcpy(mCurrentFrame.get(), frame, sizeof(float) * getChannelCount()); | |||
} | |||
void LinearResampler::readFrame(float *frame) { | |||
float *previous = mPreviousFrame.get(); | |||
float *current = mCurrentFrame.get(); | |||
float phase = (float) getIntegerPhase() / mDenominator; | |||
// iterate across samples in the frame | |||
for (int channel = 0; channel < getChannelCount(); channel++) { | |||
float f0 = *previous++; | |||
float f1 = *current++; | |||
*frame++ = f0 + (phase * (f1 - f0)); | |||
} | |||
} |
@@ -1,44 +0,0 @@ | |||
/* | |||
* Copyright 2019 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_LINEAR_RESAMPLER_H | |||
#define OBOE_LINEAR_RESAMPLER_H | |||
#include <memory> | |||
#include <sys/types.h> | |||
#include <unistd.h> | |||
#include "MultiChannelResampler.h" | |||
namespace resampler { | |||
/** | |||
* Simple resampler that uses bi-linear interpolation. | |||
*/ | |||
class LinearResampler : public MultiChannelResampler { | |||
public: | |||
LinearResampler(const MultiChannelResampler::Builder &builder); | |||
void writeFrame(const float *frame) override; | |||
void readFrame(float *frame) override; | |||
private: | |||
std::unique_ptr<float[]> mPreviousFrame; | |||
std::unique_ptr<float[]> mCurrentFrame; | |||
}; | |||
} // namespace resampler | |||
#endif //OBOE_LINEAR_RESAMPLER_H |
@@ -1,171 +0,0 @@ | |||
/* | |||
* Copyright 2019 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 <math.h> | |||
#include "IntegerRatio.h" | |||
#include "LinearResampler.h" | |||
#include "MultiChannelResampler.h" | |||
#include "PolyphaseResampler.h" | |||
#include "PolyphaseResamplerMono.h" | |||
#include "PolyphaseResamplerStereo.h" | |||
#include "SincResampler.h" | |||
#include "SincResamplerStereo.h" | |||
using namespace resampler; | |||
MultiChannelResampler::MultiChannelResampler(const MultiChannelResampler::Builder &builder) | |||
: mNumTaps(builder.getNumTaps()) | |||
, mX(builder.getChannelCount() * builder.getNumTaps() * 2) | |||
, mSingleFrame(builder.getChannelCount()) | |||
, mChannelCount(builder.getChannelCount()) | |||
{ | |||
// Reduce sample rates to the smallest ratio. | |||
// For example 44100/48000 would become 147/160. | |||
IntegerRatio ratio(builder.getInputRate(), builder.getOutputRate()); | |||
ratio.reduce(); | |||
mNumerator = ratio.getNumerator(); | |||
mDenominator = ratio.getDenominator(); | |||
mIntegerPhase = mDenominator; | |||
} | |||
// static factory method | |||
MultiChannelResampler *MultiChannelResampler::make(int32_t channelCount, | |||
int32_t inputRate, | |||
int32_t outputRate, | |||
Quality quality) { | |||
Builder builder; | |||
builder.setInputRate(inputRate); | |||
builder.setOutputRate(outputRate); | |||
builder.setChannelCount(channelCount); | |||
switch (quality) { | |||
case Quality::Fastest: | |||
builder.setNumTaps(2); | |||
break; | |||
case Quality::Low: | |||
builder.setNumTaps(4); | |||
break; | |||
case Quality::Medium: | |||
default: | |||
builder.setNumTaps(8); | |||
break; | |||
case Quality::High: | |||
builder.setNumTaps(16); | |||
break; | |||
case Quality::Best: | |||
builder.setNumTaps(32); | |||
break; | |||
} | |||
// Set the cutoff frequency so that we do not get aliasing when down-sampling. | |||
if (inputRate > outputRate) { | |||
builder.setNormalizedCutoff(kDefaultNormalizedCutoff); | |||
} | |||
return builder.build(); | |||
} | |||
MultiChannelResampler *MultiChannelResampler::Builder::build() { | |||
if (getNumTaps() == 2) { | |||
// Note that this does not do low pass filteringh. | |||
return new LinearResampler(*this); | |||
} | |||
IntegerRatio ratio(getInputRate(), getOutputRate()); | |||
ratio.reduce(); | |||
bool usePolyphase = (getNumTaps() * ratio.getDenominator()) <= kMaxCoefficients; | |||
if (usePolyphase) { | |||
if (getChannelCount() == 1) { | |||
return new PolyphaseResamplerMono(*this); | |||
} else if (getChannelCount() == 2) { | |||
return new PolyphaseResamplerStereo(*this); | |||
} else { | |||
return new PolyphaseResampler(*this); | |||
} | |||
} else { | |||
// Use less optimized resampler that uses a float phaseIncrement. | |||
// TODO mono resampler | |||
if (getChannelCount() == 2) { | |||
return new SincResamplerStereo(*this); | |||
} else { | |||
return new SincResampler(*this); | |||
} | |||
} | |||
} | |||
void MultiChannelResampler::writeFrame(const float *frame) { | |||
// Move cursor before write so that cursor points to last written frame in read. | |||
if (--mCursor < 0) { | |||
mCursor = getNumTaps() - 1; | |||
} | |||
float *dest = &mX[mCursor * getChannelCount()]; | |||
int offset = getNumTaps() * getChannelCount(); | |||
for (int channel = 0; channel < getChannelCount(); channel++) { | |||
// Write twice so we avoid having to wrap when reading. | |||
dest[channel] = dest[channel + offset] = frame[channel]; | |||
} | |||
} | |||
float MultiChannelResampler::sinc(float radians) { | |||
if (abs(radians) < 1.0e-9) return 1.0f; // avoid divide by zero | |||
return sinf(radians) / radians; // Sinc function | |||
} | |||
// Generate coefficients in the order they will be used by readFrame(). | |||
// This is more complicated but readFrame() is called repeatedly and should be optimized. | |||
void MultiChannelResampler::generateCoefficients(int32_t inputRate, | |||
int32_t outputRate, | |||
int32_t numRows, | |||
double phaseIncrement, | |||
float normalizedCutoff) { | |||
mCoefficients.resize(getNumTaps() * numRows); | |||
int coefficientIndex = 0; | |||
double phase = 0.0; // ranges from 0.0 to 1.0, fraction between samples | |||
// Stretch the sinc function for low pass filtering. | |||
const float cutoffScaler = normalizedCutoff * | |||
((outputRate < inputRate) | |||
? ((float)outputRate / inputRate) | |||
: ((float)inputRate / outputRate)); | |||
const int numTapsHalf = getNumTaps() / 2; // numTaps must be even. | |||
const float numTapsHalfInverse = 1.0f / numTapsHalf; | |||
for (int i = 0; i < numRows; i++) { | |||
float tapPhase = phase - numTapsHalf; | |||
float gain = 0.0; // sum of raw coefficients | |||
int gainCursor = coefficientIndex; | |||
for (int tap = 0; tap < getNumTaps(); tap++) { | |||
float radians = tapPhase * M_PI; | |||
#if MCR_USE_KAISER | |||
float window = mKaiserWindow(tapPhase * numTapsHalfInverse); | |||
#else | |||
float window = mCoshWindow(tapPhase * numTapsHalfInverse); | |||
#endif | |||
float coefficient = sinc(radians * cutoffScaler) * window; | |||
mCoefficients.at(coefficientIndex++) = coefficient; | |||
gain += coefficient; | |||
tapPhase += 1.0; | |||
} | |||
phase += phaseIncrement; | |||
while (phase >= 1.0) { | |||
phase -= 1.0; | |||
} | |||
// Correct for gain variations. | |||
float gainCorrection = 1.0 / gain; // normalize the gain | |||
for (int tap = 0; tap < getNumTaps(); tap++) { | |||
mCoefficients.at(gainCursor + tap) *= gainCorrection; | |||
} | |||
} | |||
} |
@@ -1,271 +0,0 @@ | |||
/* | |||
* Copyright 2019 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_MULTICHANNEL_RESAMPLER_H | |||
#define OBOE_MULTICHANNEL_RESAMPLER_H | |||
#include <memory> | |||
#include <vector> | |||
#include <sys/types.h> | |||
#include <unistd.h> | |||
#ifndef MCR_USE_KAISER | |||
// It appears from the spectrogram that the HyperbolicCosine window leads to fewer artifacts. | |||
// And it is faster to calculate. | |||
#define MCR_USE_KAISER 0 | |||
#endif | |||
#if MCR_USE_KAISER | |||
#include "KaiserWindow.h" | |||
#else | |||
#include "HyperbolicCosineWindow.h" | |||
#endif | |||
namespace resampler { | |||
class MultiChannelResampler { | |||
public: | |||
enum class Quality : int32_t { | |||
Fastest, | |||
Low, | |||
Medium, | |||
High, | |||
Best, | |||
}; | |||
class Builder { | |||
public: | |||
/** | |||
* Construct an optimal resampler based on the specified parameters. | |||
* @return address of a resampler | |||
*/ | |||
MultiChannelResampler *build(); | |||
/** | |||
* The number of taps in the resampling filter. | |||
* More taps gives better quality but uses more CPU time. | |||
* This typically ranges from 4 to 64. Default is 16. | |||
* | |||
* For polyphase filters, numTaps must be a multiple of four for loop unrolling. | |||
* @param numTaps number of taps for the filter | |||
* @return address of this builder for chaining calls | |||
*/ | |||
Builder *setNumTaps(int32_t numTaps) { | |||
mNumTaps = numTaps; | |||
return this; | |||
} | |||
/** | |||
* Use 1 for mono, 2 for stereo, etc. Default is 1. | |||
* | |||
* @param channelCount number of channels | |||
* @return address of this builder for chaining calls | |||
*/ | |||
Builder *setChannelCount(int32_t channelCount) { | |||
mChannelCount = channelCount; | |||
return this; | |||
} | |||
/** | |||
* Default is 48000. | |||
* | |||
* @param inputRate sample rate of the input stream | |||
* @return address of this builder for chaining calls | |||
*/ | |||
Builder *setInputRate(int32_t inputRate) { | |||
mInputRate = inputRate; | |||
return this; | |||
} | |||
/** | |||
* Default is 48000. | |||
* | |||
* @param outputRate sample rate of the output stream | |||
* @return address of this builder for chaining calls | |||
*/ | |||
Builder *setOutputRate(int32_t outputRate) { | |||
mOutputRate = outputRate; | |||
return this; | |||
} | |||
/** | |||
* Set cutoff frequency relative to the Nyquist rate of the output sample rate. | |||
* Set to 1.0 to match the Nyquist frequency. | |||
* Set lower to reduce aliasing. | |||
* Default is 0.70. | |||
* | |||
* @param normalizedCutoff anti-aliasing filter cutoff | |||
* @return address of this builder for chaining calls | |||
*/ | |||
Builder *setNormalizedCutoff(float normalizedCutoff) { | |||
mNormalizedCutoff = normalizedCutoff; | |||
return this; | |||
} | |||
int32_t getNumTaps() const { | |||
return mNumTaps; | |||
} | |||
int32_t getChannelCount() const { | |||
return mChannelCount; | |||
} | |||
int32_t getInputRate() const { | |||
return mInputRate; | |||
} | |||
int32_t getOutputRate() const { | |||
return mOutputRate; | |||
} | |||
float getNormalizedCutoff() const { | |||
return mNormalizedCutoff; | |||
} | |||
protected: | |||
int32_t mChannelCount = 1; | |||
int32_t mNumTaps = 16; | |||
int32_t mInputRate = 48000; | |||
int32_t mOutputRate = 48000; | |||
float mNormalizedCutoff = kDefaultNormalizedCutoff; | |||
}; | |||
virtual ~MultiChannelResampler() = default; | |||
/** | |||
* Factory method for making a resampler that is optimal for the given inputs. | |||
* | |||
* @param channelCount number of channels, 2 for stereo | |||
* @param inputRate sample rate of the input stream | |||
* @param outputRate sample rate of the output stream | |||
* @param quality higher quality sounds better but uses more CPU | |||
* @return an optimal resampler | |||
*/ | |||
static MultiChannelResampler *make(int32_t channelCount, | |||
int32_t inputRate, | |||
int32_t outputRate, | |||
Quality quality); | |||
bool isWriteNeeded() const { | |||
return mIntegerPhase >= mDenominator; | |||
} | |||
/** | |||
* Write a frame containing N samples. | |||
* | |||
* @param frame pointer to the first sample in a frame | |||
*/ | |||
void writeNextFrame(const float *frame) { | |||
writeFrame(frame); | |||
advanceWrite(); | |||
} | |||
/** | |||
* Read a frame containing N samples. | |||
* | |||
* @param frame pointer to the first sample in a frame | |||
*/ | |||
void readNextFrame(float *frame) { | |||
readFrame(frame); | |||
advanceRead(); | |||
} | |||
int getNumTaps() const { | |||
return mNumTaps; | |||
} | |||
int getChannelCount() const { | |||
return mChannelCount; | |||
} | |||
static float hammingWindow(float radians, float spread); | |||
static float sinc(float radians); | |||
protected: | |||
explicit MultiChannelResampler(const MultiChannelResampler::Builder &builder); | |||
/** | |||
* Write a frame containing N samples. | |||
* Call advanceWrite() after calling this. | |||
* @param frame pointer to the first sample in a frame | |||
*/ | |||
virtual void writeFrame(const float *frame); | |||
/** | |||
* Read a frame containing N samples using interpolation. | |||
* Call advanceRead() after calling this. | |||
* @param frame pointer to the first sample in a frame | |||
*/ | |||
virtual void readFrame(float *frame) = 0; | |||
void advanceWrite() { | |||
mIntegerPhase -= mDenominator; | |||
} | |||
void advanceRead() { | |||
mIntegerPhase += mNumerator; | |||
} | |||
/** | |||
* Generate the filter coefficients in optimal order. | |||
* @param inputRate sample rate of the input stream | |||
* @param outputRate sample rate of the output stream | |||
* @param numRows number of rows in the array that contain a set of tap coefficients | |||
* @param phaseIncrement how much to increment the phase between rows | |||
* @param normalizedCutoff filter cutoff frequency normalized to Nyquist rate of output | |||
*/ | |||
void generateCoefficients(int32_t inputRate, | |||
int32_t outputRate, | |||
int32_t numRows, | |||
double phaseIncrement, | |||
float normalizedCutoff); | |||
int32_t getIntegerPhase() { | |||
return mIntegerPhase; | |||
} | |||
static constexpr int kMaxCoefficients = 8 * 1024; | |||
std::vector<float> mCoefficients; | |||
const int mNumTaps; | |||
int mCursor = 0; | |||
std::vector<float> mX; // delayed input values for the FIR | |||
std::vector<float> mSingleFrame; // one frame for temporary use | |||
int32_t mIntegerPhase = 0; | |||
int32_t mNumerator = 0; | |||
int32_t mDenominator = 0; | |||
private: | |||
#if MCR_USE_KAISER | |||
KaiserWindow mKaiserWindow; | |||
#else | |||
HyperbolicCosineWindow mCoshWindow; | |||
#endif | |||
static constexpr float kDefaultNormalizedCutoff = 0.70f; | |||
const int mChannelCount; | |||
}; | |||
} | |||
#endif //OBOE_MULTICHANNEL_RESAMPLER_H |
@@ -1,61 +0,0 @@ | |||
/* | |||
* Copyright 2019 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 <math.h> | |||
#include "IntegerRatio.h" | |||
#include "PolyphaseResampler.h" | |||
using namespace resampler; | |||
PolyphaseResampler::PolyphaseResampler(const MultiChannelResampler::Builder &builder) | |||
: MultiChannelResampler(builder) | |||
{ | |||
assert((getNumTaps() % 4) == 0); // Required for loop unrolling. | |||
int32_t inputRate = builder.getInputRate(); | |||
int32_t outputRate = builder.getOutputRate(); | |||
int32_t numRows = mDenominator; | |||
double phaseIncrement = (double) inputRate / (double) outputRate; | |||
generateCoefficients(inputRate, outputRate, | |||
numRows, phaseIncrement, | |||
builder.getNormalizedCutoff()); | |||
} | |||
void PolyphaseResampler::readFrame(float *frame) { | |||
// Clear accumulator for mixing. | |||
std::fill(mSingleFrame.begin(), mSingleFrame.end(), 0.0); | |||
// printf("PolyphaseResampler: mCoefficientCursor = %4d\n", mCoefficientCursor); | |||
// Multiply input times windowed sinc function. | |||
float *coefficients = &mCoefficients[mCoefficientCursor]; | |||
float *xFrame = &mX[mCursor * getChannelCount()]; | |||
for (int i = 0; i < mNumTaps; i++) { | |||
float coefficient = *coefficients++; | |||
// printf("PolyphaseResampler: coeff = %10.6f, xFrame[0] = %10.6f\n", coefficient, xFrame[0]); | |||
for (int channel = 0; channel < getChannelCount(); channel++) { | |||
mSingleFrame[channel] += *xFrame++ * coefficient; | |||
} | |||
} | |||
// Advance and wrap through coefficients. | |||
mCoefficientCursor = (mCoefficientCursor + mNumTaps) % mCoefficients.size(); | |||
// Copy accumulator to output. | |||
for (int channel = 0; channel < getChannelCount(); channel++) { | |||
frame[channel] = mSingleFrame[channel]; | |||
} | |||
} |
@@ -1,51 +0,0 @@ | |||
/* | |||
* Copyright 2019 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_POLYPHASE_RESAMPLER_H | |||
#define OBOE_POLYPHASE_RESAMPLER_H | |||
#include <memory> | |||
#include <vector> | |||
#include <sys/types.h> | |||
#include <unistd.h> | |||
#include "MultiChannelResampler.h" | |||
namespace resampler { | |||
/** | |||
* Resample that is optimized for a reduced ratio of sample rates. | |||
* All of the coefficients for eacxh possible phase value are precalculated. | |||
*/ | |||
class PolyphaseResampler : public MultiChannelResampler { | |||
public: | |||
/** | |||
* | |||
* @param builder containing lots of parameters | |||
*/ | |||
explicit PolyphaseResampler(const MultiChannelResampler::Builder &builder); | |||
virtual ~PolyphaseResampler() = default; | |||
void readFrame(float *frame) override; | |||
protected: | |||
int32_t mCoefficientCursor = 0; | |||
}; | |||
} | |||
#endif //OBOE_POLYPHASE_RESAMPLER_H |
@@ -1,62 +0,0 @@ | |||
/* | |||
* Copyright 2019 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 "PolyphaseResamplerMono.h" | |||
using namespace resampler; | |||
#define MONO 1 | |||
PolyphaseResamplerMono::PolyphaseResamplerMono(const MultiChannelResampler::Builder &builder) | |||
: PolyphaseResampler(builder) { | |||
assert(builder.getChannelCount() == MONO); | |||
} | |||
void PolyphaseResamplerMono::writeFrame(const float *frame) { | |||
// Move cursor before write so that cursor points to last written frame in read. | |||
if (--mCursor < 0) { | |||
mCursor = getNumTaps() - 1; | |||
} | |||
float *dest = &mX[mCursor * MONO]; | |||
const int offset = mNumTaps * MONO; | |||
// Write each channel twice so we avoid having to wrap when running the FIR. | |||
const float sample = frame[0]; | |||
// Put ordered writes together. | |||
dest[0] = sample; | |||
dest[offset] = sample; | |||
} | |||
void PolyphaseResamplerMono::readFrame(float *frame) { | |||
// Clear accumulator. | |||
float sum = 0.0; | |||
// Multiply input times precomputed windowed sinc function. | |||
const float *coefficients = &mCoefficients[mCoefficientCursor]; | |||
float *xFrame = &mX[mCursor * MONO]; | |||
const int numLoops = mNumTaps >> 2; // n/4 | |||
for (int i = 0; i < numLoops; i++) { | |||
// Manual loop unrolling, might get converted to SIMD. | |||
sum += *xFrame++ * *coefficients++; | |||
sum += *xFrame++ * *coefficients++; | |||
sum += *xFrame++ * *coefficients++; | |||
sum += *xFrame++ * *coefficients++; | |||
} | |||
mCoefficientCursor = (mCoefficientCursor + mNumTaps) % mCoefficients.size(); | |||
// Copy accumulator to output. | |||
frame[0] = sum; | |||
} |
@@ -1,39 +0,0 @@ | |||
/* | |||
* Copyright 2019 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_POLYPHASE_RESAMPLER_MONO_H | |||
#define OBOE_POLYPHASE_RESAMPLER_MONO_H | |||
#include <sys/types.h> | |||
#include <unistd.h> | |||
#include "PolyphaseResampler.h" | |||
namespace resampler { | |||
class PolyphaseResamplerMono : public PolyphaseResampler { | |||
public: | |||
explicit PolyphaseResamplerMono(const MultiChannelResampler::Builder &builder); | |||
virtual ~PolyphaseResamplerMono() = default; | |||
void writeFrame(const float *frame) override; | |||
void readFrame(float *frame) override; | |||
}; | |||
} | |||
#endif //OBOE_POLYPHASE_RESAMPLER_MONO_H |