mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-02-02 03:20:06 +00:00
Android: Reworked default buffer size calculation logic to prefer stream's frames per burst over OUTPUT_FRAMES_PER_BUFFER property
This commit is contained in:
parent
6a48f66cd4
commit
6725162cf8
3 changed files with 61 additions and 31 deletions
|
|
@ -38,8 +38,9 @@ namespace AndroidHighPerformanceAudioHelpers
|
|||
return audioManagerGetProperty ("android.media.property.OUTPUT_SAMPLE_RATE").getDoubleValue();
|
||||
}
|
||||
|
||||
static int getNativeBufferSize()
|
||||
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)
|
||||
|
|
@ -61,17 +62,17 @@ namespace AndroidHighPerformanceAudioHelpers
|
|||
return androidHasSystemFeature ("android.hardware.audio.low_latency");
|
||||
}
|
||||
|
||||
static bool canUseHighPerformanceAudioPath (int requestedBufferSize, int requestedSampleRate)
|
||||
static bool canUseHighPerformanceAudioPath (int nativeBufferSize, int requestedBufferSize, int requestedSampleRate)
|
||||
{
|
||||
return ((requestedBufferSize % getNativeBufferSize()) == 0)
|
||||
return ((requestedBufferSize % nativeBufferSize) == 0)
|
||||
&& (requestedSampleRate == getNativeSampleRate())
|
||||
&& isProAudioDevice();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static int getMinimumBuffersToEnqueue (double requestedSampleRate)
|
||||
static int getMinimumBuffersToEnqueue (int nativeBufferSize, double requestedSampleRate)
|
||||
{
|
||||
if (canUseHighPerformanceAudioPath (getNativeBufferSize(), (int) 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
|
||||
|
|
@ -84,29 +85,27 @@ namespace AndroidHighPerformanceAudioHelpers
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int buffersToQueueForBufferDuration (int bufferDurationInMs, double sampleRate) noexcept
|
||||
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> (getNativeBufferSize())));
|
||||
/ static_cast<double> (nativeBufferSize)));
|
||||
|
||||
return jmax (getMinimumBuffersToEnqueue (sampleRate), maxNumBuffers);
|
||||
return jmax (getMinimumBuffersToEnqueue (nativeBufferSize, sampleRate), maxNumBuffers);
|
||||
}
|
||||
|
||||
static int getMaximumBuffersToEnqueue (double maximumSampleRate) noexcept
|
||||
static int getMaximumBuffersToEnqueue (int nativeBufferSize, double maximumSampleRate) noexcept
|
||||
{
|
||||
static constexpr int maxBufferSizeMs = 200;
|
||||
|
||||
return jmax (8, buffersToQueueForBufferDuration (maxBufferSizeMs, maximumSampleRate));
|
||||
return jmax (8, buffersToQueueForBufferDuration (nativeBufferSize, maxBufferSizeMs, maximumSampleRate));
|
||||
}
|
||||
|
||||
static Array<int> getAvailableBufferSizes (Array<double> availableSampleRates)
|
||||
static Array<int> getAvailableBufferSizes (int nativeBufferSize, Array<double> availableSampleRates)
|
||||
{
|
||||
auto nativeBufferSize = getNativeBufferSize();
|
||||
|
||||
auto minBuffersToQueue = getMinimumBuffersToEnqueue (getNativeSampleRate());
|
||||
auto maxBuffersToQueue = getMaximumBuffersToEnqueue (findMaximum (availableSampleRates.getRawDataPointer(),
|
||||
availableSampleRates.size()));
|
||||
auto minBuffersToQueue = getMinimumBuffersToEnqueue (nativeBufferSize, getNativeSampleRate());
|
||||
auto maxBuffersToQueue = getMaximumBuffersToEnqueue (nativeBufferSize, findMaximum (availableSampleRates.getRawDataPointer(),
|
||||
availableSampleRates.size()));
|
||||
|
||||
Array<int> bufferSizes;
|
||||
|
||||
|
|
@ -116,7 +115,7 @@ namespace AndroidHighPerformanceAudioHelpers
|
|||
return bufferSizes;
|
||||
}
|
||||
|
||||
static int getDefaultBufferSize (double currentSampleRate)
|
||||
static int getDefaultBufferSize (int nativeBufferSize, double currentSampleRate)
|
||||
{
|
||||
static constexpr int defaultBufferSizeForLowLatencyDeviceMs = 40;
|
||||
static constexpr int defaultBufferSizeForStandardLatencyDeviceMs = 100;
|
||||
|
|
@ -124,8 +123,8 @@ namespace AndroidHighPerformanceAudioHelpers
|
|||
auto defaultBufferLength = (hasLowLatencyAudioPath() ? defaultBufferSizeForLowLatencyDeviceMs
|
||||
: defaultBufferSizeForStandardLatencyDeviceMs);
|
||||
|
||||
auto defaultBuffersToEnqueue = buffersToQueueForBufferDuration (defaultBufferLength, currentSampleRate);
|
||||
return defaultBuffersToEnqueue * getNativeBufferSize();
|
||||
auto defaultBuffersToEnqueue = buffersToQueueForBufferDuration (nativeBufferSize, defaultBufferLength, currentSampleRate);
|
||||
return defaultBuffersToEnqueue * nativeBufferSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ public:
|
|||
|
||||
Array<int> getAvailableBufferSizes() override
|
||||
{
|
||||
return AndroidHighPerformanceAudioHelpers::getAvailableBufferSizes (getAvailableSampleRates());
|
||||
return AndroidHighPerformanceAudioHelpers::getAvailableBufferSizes (getNativeBufferSize(), getAvailableSampleRates());
|
||||
}
|
||||
|
||||
String open (const BigInteger& inputChannels, const BigInteger& outputChannels,
|
||||
|
|
@ -262,7 +262,7 @@ public:
|
|||
|
||||
int getDefaultBufferSize() override
|
||||
{
|
||||
return AndroidHighPerformanceAudioHelpers::getDefaultBufferSize (getCurrentSampleRate());
|
||||
return AndroidHighPerformanceAudioHelpers::getDefaultBufferSize (getNativeBufferSize(), getCurrentSampleRate());
|
||||
}
|
||||
|
||||
double getCurrentSampleRate() override
|
||||
|
|
@ -371,6 +371,30 @@ private:
|
|||
return rates;
|
||||
}
|
||||
|
||||
static int getNativeBufferSize()
|
||||
{
|
||||
auto bufferSizeHint = AndroidHighPerformanceAudioHelpers::getNativeBufferSizeHint();
|
||||
|
||||
// NB: Exclusive mode could be rejected if a device is already opened in that mode, so to get
|
||||
// reliable results, only use this function when a device is closed.
|
||||
// We initially try to open a stream with a buffer size returned from
|
||||
// android.media.property.OUTPUT_FRAMES_PER_BUFFER property, but then we verify the actual
|
||||
// size after the stream is open.
|
||||
OboeAudioIODevice::OboeStream tempStream (-1,
|
||||
oboe::Direction::Output,
|
||||
oboe::SharingMode::Exclusive,
|
||||
2,
|
||||
getAndroidSDKVersion() >= 21 ? oboe::AudioFormat::Float : oboe::AudioFormat::I16,
|
||||
(int) AndroidHighPerformanceAudioHelpers::getNativeSampleRate(),
|
||||
bufferSizeHint,
|
||||
nullptr);
|
||||
|
||||
if (auto* nativeStream = tempStream.getNativeStream())
|
||||
return nativeStream->getFramesPerBurst();
|
||||
|
||||
return bufferSizeHint;
|
||||
}
|
||||
|
||||
void setCallback (AudioIODeviceCallback* callbackToUse)
|
||||
{
|
||||
if (! running)
|
||||
|
|
@ -499,7 +523,7 @@ private:
|
|||
int32 newSampleRate, int32 newBufferSize,
|
||||
oboe::AudioStreamCallback* newCallback = nullptr)
|
||||
{
|
||||
oboe::DefaultStreamValues::FramesPerBurst = AndroidHighPerformanceAudioHelpers::getNativeBufferSize();
|
||||
oboe::DefaultStreamValues::FramesPerBurst = AndroidHighPerformanceAudioHelpers::getNativeBufferSizeHint();
|
||||
|
||||
oboe::AudioStreamBuilder builder;
|
||||
|
||||
|
|
@ -1063,7 +1087,7 @@ public:
|
|||
forInput ? 1 : 2,
|
||||
getAndroidSDKVersion() >= 21 ? oboe::AudioFormat::Float : oboe::AudioFormat::I16,
|
||||
(int) AndroidHighPerformanceAudioHelpers::getNativeSampleRate(),
|
||||
AndroidHighPerformanceAudioHelpers::getNativeBufferSize(),
|
||||
AndroidHighPerformanceAudioHelpers::getNativeBufferSizeHint(),
|
||||
nullptr);
|
||||
|
||||
if (auto* nativeStream = tempStream.getNativeStream())
|
||||
|
|
@ -1329,7 +1353,7 @@ public:
|
|||
1,
|
||||
oboe::AudioFormat::Float,
|
||||
(int) AndroidHighPerformanceAudioHelpers::getNativeSampleRate(),
|
||||
AndroidHighPerformanceAudioHelpers::getNativeBufferSize(),
|
||||
OboeAudioIODevice::getNativeBufferSize(),
|
||||
this)),
|
||||
formatUsed (oboe::AudioFormat::Float)
|
||||
{
|
||||
|
|
@ -1342,7 +1366,7 @@ public:
|
|||
1,
|
||||
oboe::AudioFormat::I16,
|
||||
(int) AndroidHighPerformanceAudioHelpers::getNativeSampleRate(),
|
||||
AndroidHighPerformanceAudioHelpers::getNativeBufferSize(),
|
||||
OboeAudioIODevice::getNativeBufferSize(),
|
||||
this));
|
||||
|
||||
formatUsed = oboe::AudioFormat::I16;
|
||||
|
|
|
|||
|
|
@ -866,7 +866,8 @@ public:
|
|||
|
||||
Array<int> getAvailableBufferSizes() override
|
||||
{
|
||||
return AndroidHighPerformanceAudioHelpers::getAvailableBufferSizes (getAvailableSampleRates());
|
||||
return AndroidHighPerformanceAudioHelpers::getAvailableBufferSizes (AndroidHighPerformanceAudioHelpers::getNativeBufferSizeHint(),
|
||||
getAvailableSampleRates());
|
||||
}
|
||||
|
||||
String open (const BigInteger& inputChannels,
|
||||
|
|
@ -883,8 +884,13 @@ public:
|
|||
|
||||
audioBuffersToEnqueue = [this, preferredBufferSize]
|
||||
{
|
||||
if (AndroidHighPerformanceAudioHelpers::canUseHighPerformanceAudioPath (preferredBufferSize, sampleRate))
|
||||
return preferredBufferSize / AndroidHighPerformanceAudioHelpers::getNativeBufferSize();
|
||||
using namespace AndroidHighPerformanceAudioHelpers;
|
||||
|
||||
auto nativeBufferSize = getNativeBufferSizeHint();
|
||||
|
||||
if (canUseHighPerformanceAudioPath (nativeBufferSize, preferredBufferSize, sampleRate))
|
||||
return preferredBufferSize / nativeBufferSize;
|
||||
|
||||
|
||||
return 1;
|
||||
}();
|
||||
|
|
@ -930,7 +936,7 @@ public:
|
|||
|
||||
DBG ("OpenSL: numInputChannels = " << numInputChannels
|
||||
<< ", numOutputChannels = " << numOutputChannels
|
||||
<< ", nativeBufferSize = " << AndroidHighPerformanceAudioHelpers::getNativeBufferSize()
|
||||
<< ", nativeBufferSize = " << AndroidHighPerformanceAudioHelpers::getNativeBufferSizeHint()
|
||||
<< ", nativeSampleRate = " << AndroidHighPerformanceAudioHelpers::getNativeSampleRate()
|
||||
<< ", actualBufferSize = " << actualBufferSize
|
||||
<< ", audioBuffersToEnqueue = " << audioBuffersToEnqueue
|
||||
|
|
@ -964,7 +970,8 @@ public:
|
|||
|
||||
int getDefaultBufferSize() override
|
||||
{
|
||||
return AndroidHighPerformanceAudioHelpers::getDefaultBufferSize (getCurrentSampleRate());
|
||||
return AndroidHighPerformanceAudioHelpers::getDefaultBufferSize (AndroidHighPerformanceAudioHelpers::getNativeBufferSizeHint(),
|
||||
getCurrentSampleRate());
|
||||
}
|
||||
|
||||
double getCurrentSampleRate() override
|
||||
|
|
@ -1272,7 +1279,7 @@ private:
|
|||
SlRef<SLPlayItf_> player;
|
||||
SlRef<SLAndroidSimpleBufferQueueItf_> queue;
|
||||
|
||||
int bufferSize = AndroidHighPerformanceAudioHelpers::getNativeBufferSize();
|
||||
int bufferSize = AndroidHighPerformanceAudioHelpers::getNativeBufferSizeHint();
|
||||
HeapBlock<int16> buffer { HeapBlock<int16> (static_cast<size_t> (1 * bufferSize * numBuffers)) };
|
||||
|
||||
void* (*threadEntryProc) (void*) = nullptr;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue