1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-22 01:34:21 +00:00

Android: Ensured that JUCE will always use the high-performance audio path if the device supports it and that increasing the buffer size will more effectively reduce glitchess

This commit is contained in:
hogliux 2018-02-06 15:41:39 +00:00
parent d8d2f5d0f4
commit 0fae9341c8
2 changed files with 86 additions and 26 deletions

View file

@ -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)
};