|
|
@@ -764,7 +764,7 @@ public: |
|
|
|
//==============================================================================
|
|
|
|
OpenSLAudioIODevice (const String& deviceName)
|
|
|
|
: AudioIODevice (deviceName, openSLTypeName),
|
|
|
|
actualBufferSize (0), sampleRate (0),
|
|
|
|
actualBufferSize (0), sampleRate (0), audioBuffersToEnqueue (0),
|
|
|
|
audioProcessingEnabled (true),
|
|
|
|
callback (nullptr)
|
|
|
|
{
|
|
|
@@ -831,11 +831,12 @@ public: |
|
|
|
{
|
|
|
|
// we need to offer the lowest possible buffer size which
|
|
|
|
// is the native buffer size
|
|
|
|
const int defaultNumMultiples = 8;
|
|
|
|
const int nativeBufferSize = getNativeBufferSize();
|
|
|
|
auto nativeBufferSize = getNativeBufferSize();
|
|
|
|
auto minBuffersToQueue = getMinimumBuffersToEnqueue();
|
|
|
|
auto maxBuffersToQueue = getMaximumBuffersToEnqueue();
|
|
|
|
|
|
|
|
Array<int> retval;
|
|
|
|
for (int i = 1; i < defaultNumMultiples; ++i)
|
|
|
|
for (int i = minBuffersToQueue; i <= maxBuffersToQueue; ++i)
|
|
|
|
retval.add (i * nativeBufferSize);
|
|
|
|
|
|
|
|
return retval;
|
|
|
@@ -851,7 +852,13 @@ public: |
|
|
|
lastError.clear();
|
|
|
|
sampleRate = (int) requestedSampleRate;
|
|
|
|
|
|
|
|
int preferredBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize;
|
|
|
|
auto totalPreferredBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize;
|
|
|
|
auto nativeBufferSize = getNativeBufferSize();
|
|
|
|
bool useHighPerformanceAudioPath = canUseHighPerformanceAudioPath (totalPreferredBufferSize, sampleRate);
|
|
|
|
|
|
|
|
audioBuffersToEnqueue = useHighPerformanceAudioPath ? (totalPreferredBufferSize / nativeBufferSize) : 1;
|
|
|
|
actualBufferSize = totalPreferredBufferSize / audioBuffersToEnqueue;
|
|
|
|
jassert ((actualBufferSize * audioBuffersToEnqueue) == totalPreferredBufferSize);
|
|
|
|
|
|
|
|
activeOutputChans = outputChannels;
|
|
|
|
activeOutputChans.setRange (2, activeOutputChans.getHighestBit(), false);
|
|
|
@@ -861,11 +868,6 @@ public: |
|
|
|
activeInputChans.setRange (1, activeInputChans.getHighestBit(), false);
|
|
|
|
int numInputChannels = activeInputChans.countNumberOfSetBits();
|
|
|
|
|
|
|
|
actualBufferSize = preferredBufferSize;
|
|
|
|
|
|
|
|
const int audioBuffersToEnqueue = hasLowLatencyAudioPath() ? buffersToEnqueueForLowLatency
|
|
|
|
: buffersToEnqueueSlowAudio;
|
|
|
|
|
|
|
|
if (numInputChannels > 0 && (! RuntimePermissions::isGranted (RuntimePermissions::recordAudio)))
|
|
|
|
{
|
|
|
|
// If you hit this assert, you probably forgot to get RuntimePermissions::recordAudio
|
|
|
@@ -917,7 +919,7 @@ public: |
|
|
|
int getOutputLatencyInSamples() override { return outputLatency; }
|
|
|
|
int getInputLatencyInSamples() override { return inputLatency; }
|
|
|
|
bool isOpen() override { return deviceOpen; }
|
|
|
|
int getCurrentBufferSizeSamples() override { return actualBufferSize; }
|
|
|
|
int getCurrentBufferSizeSamples() override { return actualBufferSize * audioBuffersToEnqueue; }
|
|
|
|
int getCurrentBitDepth() override { return (session != nullptr && session->supportsFloatingPoint() ? 32 : 16); }
|
|
|
|
BigInteger getActiveOutputChannels() const override { return activeOutputChans; }
|
|
|
|
BigInteger getActiveInputChannels() const override { return activeInputChans; }
|
|
|
@@ -927,11 +929,11 @@ public: |
|
|
|
|
|
|
|
int getDefaultBufferSize() override
|
|
|
|
{
|
|
|
|
// Only on a Pro-Audio device will we set the lowest possible buffer size
|
|
|
|
// by default. We need to be more conservative on other devices
|
|
|
|
// as they may be low-latency, but still have a crappy CPU.
|
|
|
|
return (isProAudioDevice() ? 1 : 6)
|
|
|
|
* defaultBufferSizeIsMultipleOfNative * getNativeBufferSize();
|
|
|
|
auto defaultBufferLength = (hasLowLatencyAudioPath() ? defaultBufferSizeForLowLatencyDeviceMs
|
|
|
|
: defaultBufferSizeForStandardLatencyDeviceMs);
|
|
|
|
|
|
|
|
auto defaultBuffersToEnqueue = buffersToQueueForBufferDuration (defaultBufferLength, getCurrentSampleRate());
|
|
|
|
return defaultBuffersToEnqueue * getNativeBufferSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
double getCurrentSampleRate() override
|
|
|
@@ -999,7 +1001,7 @@ private: |
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
DynamicLibrary slLibrary;
|
|
|
|
int actualBufferSize, sampleRate;
|
|
|
|
int actualBufferSize, sampleRate, audioBuffersToEnqueue;
|
|
|
|
int inputLatency, outputLatency;
|
|
|
|
bool deviceOpen, audioProcessingEnabled;
|
|
|
|
String lastError;
|
|
|
@@ -1010,13 +1012,48 @@ private: |
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
// The number of buffers to enqueue needs to be at least two for the audio to use the low-latency
|
|
|
|
// audio path (see "Performance" section in ndk/docs/Additional_library_docs/opensles/index.html)
|
|
|
|
buffersToEnqueueForLowLatency = 4,
|
|
|
|
buffersToEnqueueSlowAudio = 8,
|
|
|
|
defaultBufferSizeIsMultipleOfNative = 1
|
|
|
|
defaultBufferSizeForLowLatencyDeviceMs = 40,
|
|
|
|
defaultBufferSizeForStandardLatencyDeviceMs = 100
|
|
|
|
};
|
|
|
|
|
|
|
|
static int getMinimumBuffersToEnqueue (double sampleRateToCheck = getNativeSampleRate())
|
|
|
|
{
|
|
|
|
if (canUseHighPerformanceAudioPath (getNativeBufferSize(), (int) sampleRateToCheck))
|
|
|
|
{
|
|
|
|
// 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."
|
|
|
|
|
|
|
|
auto sdkVersion = getEnv()->GetStaticIntField (AndroidBuildVersion, AndroidBuildVersion.SDK_INT);
|
|
|
|
return (sdkVersion >= 18 ? 1 : 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
// we will not use the low-latency path so we can use the absolute minimum number of buffers
|
|
|
|
// to queue
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int getMaximumBuffersToEnqueue() noexcept
|
|
|
|
{
|
|
|
|
constexpr auto maxBufferSizeMs = 200;
|
|
|
|
|
|
|
|
auto availableSampleRates = getAvailableSampleRates();
|
|
|
|
auto maximumSampleRate = findMaximum(availableSampleRates.getRawDataPointer(), availableSampleRates.size());
|
|
|
|
|
|
|
|
// ensure we don't return something crazy small
|
|
|
|
return jmax (8, buffersToQueueForBufferDuration (maxBufferSizeMs, maximumSampleRate));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int buffersToQueueForBufferDuration (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> (getNativeBufferSize())));
|
|
|
|
|
|
|
|
return jmax (getMinimumBuffersToEnqueue (sampleRate), maxNumBuffers);
|
|
|
|
}
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
static String audioManagerGetProperty (const String& property)
|
|
|
|
{
|
|
|
@@ -1048,7 +1085,7 @@ private: |
|
|
|
|
|
|
|
static bool isProAudioDevice()
|
|
|
|
{
|
|
|
|
return androidHasSystemFeature ("android.hardware.audio.pro");
|
|
|
|
return androidHasSystemFeature ("android.hardware.audio.pro") || isSapaSupported();
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool hasLowLatencyAudioPath()
|
|
|
@@ -1056,6 +1093,27 @@ private: |
|
|
|
return androidHasSystemFeature ("android.hardware.audio.low_latency");
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool canUseHighPerformanceAudioPath (int requestedBufferSize, int requestedSampleRate)
|
|
|
|
{
|
|
|
|
return ((requestedBufferSize % getNativeBufferSize()) == 0)
|
|
|
|
&& (requestedSampleRate == getNativeSampleRate())
|
|
|
|
&& isProAudioDevice();
|
|
|
|
}
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
// Some minimum Sapa support to check if this device supports pro audio
|
|
|
|
static bool isSamsungDevice()
|
|
|
|
{
|
|
|
|
return SystemStats::getDeviceManufacturer().containsIgnoreCase ("SAMSUNG");
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool isSapaSupported()
|
|
|
|
{
|
|
|
|
static bool supported = isSamsungDevice() && DynamicLibrary().open ("libapa_jni.so");
|
|
|
|
|
|
|
|
return supported;
|
|
|
|
}
|
|
|
|
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenSLAudioIODevice)
|
|
|
|
};
|
|
|
|
|
|
|
|