Browse Source

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

tags/2021-05-28
hogliux 7 years ago
parent
commit
0fae9341c8
2 changed files with 86 additions and 26 deletions
  1. +5
    -3
      modules/juce_audio_devices/audio_io/juce_AudioIODevice.h
  2. +81
    -23
      modules/juce_audio_devices/native/juce_android_OpenSL.cpp

+ 5
- 3
modules/juce_audio_devices/audio_io/juce_AudioIODevice.h View File

@@ -75,9 +75,11 @@ public:
@param numSamples the number of samples in each channel of the input and
output arrays. The number of samples will depend on the
audio device's buffer size and will usually remain constant,
although this isn't guaranteed, so make sure your code can
cope with reasonable changes in the buffer size from one
callback to the next.
although this isn't guaranteed. For example, on Android,
on devices which support it, Android will chop up your audio
processing into several smaller callbacks to ensure higher audio
performance. So make sure your code can cope with reasonable
changes in the buffer size from one callback to the next.
*/
virtual void audioDeviceIOCallback (const float** inputChannelData,
int numInputChannels,


+ 81
- 23
modules/juce_audio_devices/native/juce_android_OpenSL.cpp 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)
};


Loading…
Cancel
Save