Browse Source

Updates to support the latest Oboe (c5c3cc17f7).

tags/2021-05-28
Lukasz Kozakiewicz jules 6 years ago
parent
commit
2718ce96f0
2 changed files with 152 additions and 135 deletions
  1. +1
    -1
      extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Android.h
  2. +151
    -134
      modules/juce_audio_devices/native/juce_android_Oboe.cpp

+ 1
- 1
extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Android.h View File

@@ -881,7 +881,7 @@ private:
{ {
props.add (new TextPropertyComponent (androidOboeRepositoryPath, "Oboe Repository Path", 2048, false), props.add (new TextPropertyComponent (androidOboeRepositoryPath, "Oboe Repository Path", 2048, false),
"Path to the root of Oboe repository. Make sure to point Oboe repository to " "Path to the root of Oboe repository. Make sure to point Oboe repository to "
"commit with SHA 44c6b6ea9c8fa9b5b74cbd60f355068b57b50b37 before building.");
"commit with SHA c5c3cc17f78974bf005bf33a2de1a093ac55cc07 before building.");
props.add (new ChoicePropertyComponent (androidInternetNeeded, "Internet Access"), props.add (new ChoicePropertyComponent (androidInternetNeeded, "Internet Access"),
"If enabled, this will set the android.permission.INTERNET flag in the manifest."); "If enabled, this will set the android.permission.INTERNET flag in the manifest.");


+ 151
- 134
modules/juce_audio_devices/native/juce_android_Oboe.cpp View File

@@ -124,6 +124,12 @@ struct OboeAudioIODeviceBufferHelpers<float>
} }
} }
}; };

template <typename Type>
static String getOboeString (const Type& value)
{
return String (oboe::convertToText (value));
}
//============================================================================== //==============================================================================
class OboeAudioIODevice : public AudioIODevice class OboeAudioIODevice : public AudioIODevice
@@ -438,8 +444,8 @@ private:
~OboeStream() ~OboeStream()
{ {
// AudioStreamCallback can only be deleted when stream has been closed
close(); close();
delete stream;
} }
bool openedOk() const noexcept bool openedOk() const noexcept
@@ -458,40 +464,47 @@ private:
int64 timeoutNanos = 1000 * oboe::kNanosPerMillisecond; int64 timeoutNanos = 1000 * oboe::kNanosPerMillisecond;
auto startResult = stream->requestStart(); auto startResult = stream->requestStart();
JUCE_OBOE_LOG ("Requested Oboe stream start with result: " + String (oboe::convertToText (startResult)));
startResult = stream->waitForStateChange (expectedState, &nextState, timeoutNanos);
JUCE_OBOE_LOG ("Starting Oboe stream with result: " + String (oboe::convertToText (startResult));
+ "\nUses AAudio = " + (stream != nullptr ? String ((int) stream->usesAAudio()) : String ("?"))
+ "\nDirection = " + (stream != nullptr ? String (oboe::convertToText (stream->getDirection())) : String ("?"))
+ "\nSharingMode = " + (stream != nullptr ? String (oboe::convertToText (stream->getSharingMode())) : String ("?"))
+ "\nChannelCount = " + (stream != nullptr ? String (stream->getChannelCount()) : String ("?"))
+ "\nFormat = " + (stream != nullptr ? String (oboe::convertToText (stream->getFormat())) : String ("?"))
+ "\nSampleRate = " + (stream != nullptr ? String (stream->getSampleRate()) : String ("?"))
+ "\nBufferSizeInFrames = " + (stream != nullptr ? String (stream->getBufferSizeInFrames()) : String ("?"))
+ "\nBufferCapacityInFrames = " + (stream != nullptr ? String (stream->getBufferCapacityInFrames()) : String ("?"))
+ "\nFramesPerBurst = " + (stream != nullptr ? String (stream->getFramesPerBurst()) : String ("?"))
+ "\nFramesPerCallback = " + (stream != nullptr ? String (stream->getFramesPerCallback()) : String ("?"))
+ "\nBytesPerFrame = " + (stream != nullptr ? String (stream->getBytesPerFrame()) : String ("?"))
+ "\nBytesPerSample = " + (stream != nullptr ? String (stream->getBytesPerSample()) : String ("?"))
+ "\nPerformanceMode = " + String (oboe::convertToText (oboe::PerformanceMode::LowLatency))
+ "\ngetDeviceId = " + (stream != nullptr ? String (stream->getDeviceId()) : String ("?")));
JUCE_OBOE_LOG ("Requested Oboe stream start with result: " + getOboeString (startResult));
startResult = stream->waitForStateChange (expectedState, &nextState, timeoutNanos);
JUCE_OBOE_LOG ("Starting Oboe stream with result: " + getOboeString (startResult);
+ "\nUses AAudio = " + String ((int) stream->usesAAudio())
+ "\nDirection = " + getOboeString (stream->getDirection())
+ "\nSharingMode = " + getOboeString (stream->getSharingMode())
+ "\nChannelCount = " + String (stream->getChannelCount())
+ "\nFormat = " + getOboeString (stream->getFormat())
+ "\nSampleRate = " + String (stream->getSampleRate())
+ "\nBufferSizeInFrames = " + String (stream->getBufferSizeInFrames())
+ "\nBufferCapacityInFrames = " + String (stream->getBufferCapacityInFrames())
+ "\nFramesPerBurst = " + String (stream->getFramesPerBurst())
+ "\nFramesPerCallback = " + String (stream->getFramesPerCallback())
+ "\nBytesPerFrame = " + String (stream->getBytesPerFrame())
+ "\nBytesPerSample = " + String (stream->getBytesPerSample())
+ "\nPerformanceMode = " + getOboeString (oboe::PerformanceMode::LowLatency)
+ "\ngetDeviceId = " + String (stream->getDeviceId()));
} }
} }
oboe::AudioStream* getNativeStream()
oboe::AudioStream* getNativeStream() const
{ {
jassert (openedOk()); jassert (openedOk());
return stream; return stream;
} }
int getXRunCount() const int getXRunCount() const
{ {
if (auto xruns = stream->getXRunCount())
return xruns.value();
return 0;
if (stream != nullptr)
{
auto count = stream->getXRunCount();

if (count)
return count.value();

JUCE_OBOE_LOG ("Failed to get Xrun count: " + getOboeString (count.error()));
}
return 0;
} }
private: private:
@@ -501,39 +514,38 @@ private:
int32 sampleRate, int32 bufferSize, int32 sampleRate, int32 bufferSize,
oboe::AudioStreamCallback* callback = nullptr) oboe::AudioStreamCallback* callback = nullptr)
{ {
oboe::DefaultStreamValues::FramesPerBurst = getDefaultFramesPerBurst();
oboe::AudioStreamBuilder builder; oboe::AudioStreamBuilder builder;
if (deviceId != -1) if (deviceId != -1)
builder.setDeviceId (deviceId); builder.setDeviceId (deviceId);
static int defaultFramesPerBurst = getDefaultFramesPerBurst();
// Note: Letting OS to choose the buffer capacity & frames per callback.
// Note: letting OS to choose the buffer capacity & frames per callback.
builder.setDirection (direction); builder.setDirection (direction);
builder.setSharingMode (sharingMode); builder.setSharingMode (sharingMode);
builder.setChannelCount (channelCount); builder.setChannelCount (channelCount);
builder.setFormat (format); builder.setFormat (format);
builder.setSampleRate (sampleRate); builder.setSampleRate (sampleRate);
builder.setFramesPerCallback ((int32) defaultFramesPerBurst);
builder.setPerformanceMode (oboe::PerformanceMode::LowLatency); builder.setPerformanceMode (oboe::PerformanceMode::LowLatency);
builder.setCallback (callback); builder.setCallback (callback);
JUCE_OBOE_LOG (String ("Preparing Oboe stream with params:") JUCE_OBOE_LOG (String ("Preparing Oboe stream with params:")
+ "\nAAudio supported = " + String (int (builder.isAAudioSupported())) + "\nAAudio supported = " + String (int (builder.isAAudioSupported()))
+ "\nAPI = " + String (oboe::convertToText (builder.getAudioApi()))
+ "\nAPI = " + getOboeString (builder.getAudioApi())
+ "\nDeviceId = " + String (deviceId) + "\nDeviceId = " + String (deviceId)
+ "\nDirection = " + String (oboe::convertToText (direction))
+ "\nSharingMode = " + String (oboe::convertToText (sharingMode))
+ "\nDirection = " + getOboeString (direction)
+ "\nSharingMode = " + getOboeString (sharingMode)
+ "\nChannelCount = " + String (channelCount) + "\nChannelCount = " + String (channelCount)
+ "\nFormat = " + String (oboe::convertToText (format))
+ "\nFormat = " + getOboeString (format)
+ "\nSampleRate = " + String (sampleRate) + "\nSampleRate = " + String (sampleRate)
+ "\nBufferSizeInFrames = " + String (bufferSize) + "\nBufferSizeInFrames = " + String (bufferSize)
+ "\nFramesPerBurst = " + String (defaultFramesPerBurst)
+ "\nPerformanceMode = " + String (oboe::convertToText (oboe::PerformanceMode::LowLatency)));
+ "\nFramesPerBurst = " + String (oboe::DefaultStreamValues::FramesPerBurst)
+ "\nPerformanceMode = " + getOboeString (oboe::PerformanceMode::LowLatency));
openResult = builder.openStream (&stream); openResult = builder.openStream (&stream);
JUCE_OBOE_LOG ("Building Oboe stream with result: " + String (oboe::convertToText (openResult))
+ "\nStream state = " + (stream != nullptr ? String (oboe::convertToText (stream->getState())) : String ("?")));
JUCE_OBOE_LOG ("Building Oboe stream with result: " + getOboeString (openResult)
+ "\nStream state = " + (stream != nullptr ? getOboeString (stream->getState()) : String ("?")));
if (stream != nullptr) if (stream != nullptr)
stream->setBufferSizeInFrames (bufferSize); stream->setBufferSizeInFrames (bufferSize);
@@ -541,10 +553,10 @@ private:
JUCE_OBOE_LOG (String ("Stream details:") JUCE_OBOE_LOG (String ("Stream details:")
+ "\nUses AAudio = " + (stream != nullptr ? String ((int) stream->usesAAudio()) : String ("?")) + "\nUses AAudio = " + (stream != nullptr ? String ((int) stream->usesAAudio()) : String ("?"))
+ "\nDeviceId = " + (stream != nullptr ? String (stream->getDeviceId()) : String ("?")) + "\nDeviceId = " + (stream != nullptr ? String (stream->getDeviceId()) : String ("?"))
+ "\nDirection = " + (stream != nullptr ? String (oboe::convertToText (stream->getDirection())) : String ("?"))
+ "\nSharingMode = " + (stream != nullptr ? String (oboe::convertToText (stream->getSharingMode())) : String ("?"))
+ "\nDirection = " + (stream != nullptr ? getOboeString (stream->getDirection()) : String ("?"))
+ "\nSharingMode = " + (stream != nullptr ? getOboeString (stream->getSharingMode()) : String ("?"))
+ "\nChannelCount = " + (stream != nullptr ? String (stream->getChannelCount()) : String ("?")) + "\nChannelCount = " + (stream != nullptr ? String (stream->getChannelCount()) : String ("?"))
+ "\nFormat = " + (stream != nullptr ? String (oboe::convertToText (stream->getFormat())) : String ("?"))
+ "\nFormat = " + (stream != nullptr ? getOboeString (stream->getFormat()) : String ("?"))
+ "\nSampleRate = " + (stream != nullptr ? String (stream->getSampleRate()) : String ("?")) + "\nSampleRate = " + (stream != nullptr ? String (stream->getSampleRate()) : String ("?"))
+ "\nBufferSizeInFrames = " + (stream != nullptr ? String (stream->getBufferSizeInFrames()) : String ("?")) + "\nBufferSizeInFrames = " + (stream != nullptr ? String (stream->getBufferSizeInFrames()) : String ("?"))
+ "\nBufferCapacityInFrames = " + (stream != nullptr ? String (stream->getBufferCapacityInFrames()) : String ("?")) + "\nBufferCapacityInFrames = " + (stream != nullptr ? String (stream->getBufferCapacityInFrames()) : String ("?"))
@@ -552,7 +564,7 @@ private:
+ "\nFramesPerCallback = " + (stream != nullptr ? String (stream->getFramesPerCallback()) : String ("?")) + "\nFramesPerCallback = " + (stream != nullptr ? String (stream->getFramesPerCallback()) : String ("?"))
+ "\nBytesPerFrame = " + (stream != nullptr ? String (stream->getBytesPerFrame()) : String ("?")) + "\nBytesPerFrame = " + (stream != nullptr ? String (stream->getBytesPerFrame()) : String ("?"))
+ "\nBytesPerSample = " + (stream != nullptr ? String (stream->getBytesPerSample()) : String ("?")) + "\nBytesPerSample = " + (stream != nullptr ? String (stream->getBytesPerSample()) : String ("?"))
+ "\nPerformanceMode = " + String (oboe::convertToText (oboe::PerformanceMode::LowLatency)));
+ "\nPerformanceMode = " + getOboeString (oboe::PerformanceMode::LowLatency));
} }
void close() void close()
@@ -560,27 +572,10 @@ private:
if (stream != nullptr) if (stream != nullptr)
{ {
oboe::Result result = stream->close(); oboe::Result result = stream->close();
JUCE_OBOE_LOG ("Requested Oboe stream close with result: " + String (oboe::convertToText (result)));
JUCE_OBOE_LOG ("Requested Oboe stream close with result: " + getOboeString (result));
} }
} }
int getDefaultFramesPerBurst() const
{
// NB: this function only works for inbuilt speakers and headphones
auto* env = getEnv();
auto audioManager = LocalRef<jobject> (env->CallObjectMethod (android.activity,
JuceAppActivity.getSystemService,
javaString ("audio").get()));
auto propertyJavaString = javaString ("android.media.property.OUTPUT_FRAMES_PER_BUFFER");
auto framesPerBurstString = LocalRef<jstring> ((jstring) android.activity.callObjectMethod (JuceAppActivity.audioManagerGetProperty,
propertyJavaString.get()));
return framesPerBurstString != 0 ? env->CallStaticIntMethod (JavaInteger, JavaInteger.parseInt, framesPerBurstString.get(), 10) : 192;
}
oboe::AudioStream* stream = nullptr; oboe::AudioStream* stream = nullptr;
oboe::Result openResult; oboe::Result openResult;
}; };
@@ -672,9 +667,7 @@ private:
void checkStreamSetup (OboeStream* stream, int deviceId, int numChannels, int sampleRate, void checkStreamSetup (OboeStream* stream, int deviceId, int numChannels, int sampleRate,
int bufferSize, oboe::AudioFormat format) int bufferSize, oboe::AudioFormat format)
{ {
auto* nativeStream = stream != nullptr ? stream->getNativeStream() : nullptr;
if (nativeStream != nullptr)
if (auto* nativeStream = stream != nullptr ? stream->getNativeStream() : nullptr)
{ {
ignoreUnused (deviceId, numChannels, sampleRate, bufferSize); ignoreUnused (deviceId, numChannels, sampleRate, bufferSize);
ignoreUnused (streamFormat, bitDepth); ignoreUnused (streamFormat, bitDepth);
@@ -739,7 +732,8 @@ private:
outputStream->start(); outputStream->start();
checkIsOutputLatencyDetectionSupported();
isInputLatencyDetectionSupported = isLatencyDetectionSupported (inputStream.get());
isOutputLatencyDetectionSupported = isLatencyDetectionSupported (outputStream.get());
} }
void stop() override void stop() override
@@ -754,19 +748,16 @@ private:
} }
int getOutputLatencyInSamples() override { return outputLatency; } int getOutputLatencyInSamples() override { return outputLatency; }
int getInputLatencyInSamples() override { return -1; }
int getInputLatencyInSamples() override { return inputLatency; }
private: private:
void checkIsOutputLatencyDetectionSupported()
bool isLatencyDetectionSupported (OboeStream* stream)
{ {
if (! openedOk())
{
isOutputLatencyDetectionSupported = false;
return;
}
if (stream == nullptr || ! openedOk())
return false;
auto result = outputStream->getNativeStream()->getTimestamp (CLOCK_MONOTONIC, 0, 0);
isOutputLatencyDetectionSupported = result != oboe::Result::ErrorUnimplemented;
auto result = stream->getNativeStream()->getTimestamp (CLOCK_MONOTONIC, 0, 0);
return result != oboe::Result::ErrorUnimplemented;
} }
oboe::DataCallbackResult onAudioReady (oboe::AudioStream* stream, void* audioData, int32_t numFrames) override oboe::DataCallbackResult onAudioReady (oboe::AudioStream* stream, void* audioData, int32_t numFrames) override
@@ -792,7 +783,7 @@ private:
if (nativeInputStream->getFormat() != oboe::AudioFormat::I16 && nativeInputStream->getFormat() != oboe::AudioFormat::Float) if (nativeInputStream->getFormat() != oboe::AudioFormat::I16 && nativeInputStream->getFormat() != oboe::AudioFormat::Float)
{ {
JUCE_OBOE_LOG ("Unsupported input stream audio format: " + String (oboe::convertToText (nativeInputStream->getFormat())));
JUCE_OBOE_LOG ("Unsupported input stream audio format: " + getOboeString (nativeInputStream->getFormat()));
jassertfalse; jassertfalse;
return oboe::DataCallbackResult::Continue; return oboe::DataCallbackResult::Continue;
} }
@@ -809,9 +800,11 @@ private:
} }
else else
{ {
// Failed to read from input stream.
jassertfalse;
JUCE_OBOE_LOG ("Failed to read from input stream: " + getOboeString (result.error()));
} }
if (isInputLatencyDetectionSupported)
inputLatency = getLatencyFor (*inputStream);
} }
//----------------- //-----------------
@@ -834,7 +827,7 @@ private:
OboeAudioIODeviceBufferHelpers<SampleType>::convertToOboe (outputStreamSampleBuffer, static_cast<SampleType*> (audioData), numFrames); OboeAudioIODeviceBufferHelpers<SampleType>::convertToOboe (outputStreamSampleBuffer, static_cast<SampleType*> (audioData), numFrames);
if (isOutputLatencyDetectionSupported) if (isOutputLatencyDetectionSupported)
calculateOutputLatency();
outputLatency = getLatencyFor (*outputStream);
audioCallbackGuard.set (0); audioCallbackGuard.set (0);
} }
@@ -847,10 +840,10 @@ private:
ignoreUnused (stream); ignoreUnused (stream);
JUCE_OBOE_LOG ("\nUses AAudio = " + (stream != nullptr ? String ((int) stream->usesAAudio()) : String ("?")) JUCE_OBOE_LOG ("\nUses AAudio = " + (stream != nullptr ? String ((int) stream->usesAAudio()) : String ("?"))
+ "\nDirection = " + (stream != nullptr ? String (oboe::convertToText (stream->getDirection())) : String ("?"))
+ "\nSharingMode = " + (stream != nullptr ? String (oboe::convertToText (stream->getSharingMode())) : String ("?"))
+ "\nDirection = " + (stream != nullptr ? getOboeString (stream->getDirection()) : String ("?"))
+ "\nSharingMode = " + (stream != nullptr ? getOboeString (stream->getSharingMode()) : String ("?"))
+ "\nChannelCount = " + (stream != nullptr ? String (stream->getChannelCount()) : String ("?")) + "\nChannelCount = " + (stream != nullptr ? String (stream->getChannelCount()) : String ("?"))
+ "\nFormat = " + (stream != nullptr ? String (oboe::convertToText (stream->getFormat())) : String ("?"))
+ "\nFormat = " + (stream != nullptr ? getOboeString (stream->getFormat()) : String ("?"))
+ "\nSampleRate = " + (stream != nullptr ? String (stream->getSampleRate()) : String ("?")) + "\nSampleRate = " + (stream != nullptr ? String (stream->getSampleRate()) : String ("?"))
+ "\nBufferSizeInFrames = " + (stream != nullptr ? String (stream->getBufferSizeInFrames()) : String ("?")) + "\nBufferSizeInFrames = " + (stream != nullptr ? String (stream->getBufferSizeInFrames()) : String ("?"))
+ "\nBufferCapacityInFrames = " + (stream != nullptr ? String (stream->getBufferCapacityInFrames()) : String ("?")) + "\nBufferCapacityInFrames = " + (stream != nullptr ? String (stream->getBufferCapacityInFrames()) : String ("?"))
@@ -858,34 +851,48 @@ private:
+ "\nFramesPerCallback = " + (stream != nullptr ? String (stream->getFramesPerCallback()) : String ("?")) + "\nFramesPerCallback = " + (stream != nullptr ? String (stream->getFramesPerCallback()) : String ("?"))
+ "\nBytesPerFrame = " + (stream != nullptr ? String (stream->getBytesPerFrame()) : String ("?")) + "\nBytesPerFrame = " + (stream != nullptr ? String (stream->getBytesPerFrame()) : String ("?"))
+ "\nBytesPerSample = " + (stream != nullptr ? String (stream->getBytesPerSample()) : String ("?")) + "\nBytesPerSample = " + (stream != nullptr ? String (stream->getBytesPerSample()) : String ("?"))
+ "\nPerformanceMode = " + String (oboe::convertToText (oboe::PerformanceMode::LowLatency))
+ "\nPerformanceMode = " + getOboeString (oboe::PerformanceMode::LowLatency)
+ "\ngetDeviceId = " + (stream != nullptr ? String (stream->getDeviceId()) : String ("?"))); + "\ngetDeviceId = " + (stream != nullptr ? String (stream->getDeviceId()) : String ("?")));
} }
void calculateOutputLatency()
int getLatencyFor (OboeStream& stream)
{ {
// Sadly, Oboe uses non-portable int64_t (a.k.a. long on LP64 and long long on LLP64)
int64_t lastWrittenAndPresentedFrameIndex = 0;
int64_t lastFramePresentationTimeNanos = 0;
auto& nativeStream = *stream.getNativeStream();
if (auto latency = nativeStream.calculateLatencyMillis())
return static_cast<int> ((latency.value() * sampleRate) / 1000);
// Get the time that a known audio frame was presented.
int64_t hardwareFrameIndex = 0;
int64_t hardwareFrameHardwareTime = 0;
auto result = outputStream->getNativeStream()->getTimestamp (CLOCK_MONOTONIC,
&lastWrittenAndPresentedFrameIndex,
&lastFramePresentationTimeNanos);
auto result = nativeStream.getTimestamp (CLOCK_MONOTONIC,
&hardwareFrameIndex,
&hardwareFrameHardwareTime);
if (result != oboe::Result::OK) if (result != oboe::Result::OK)
return;
return 0;
// Get counter closest to the app.
const bool isOutput = nativeStream.getDirection() == oboe::Direction::Output;
const int64_t appFrameIndex = isOutput ? nativeStream.getFramesWritten() : nativeStream.getFramesRead();
int64_t currentNumFramesWritten = outputStream->getNativeStream()->getFramesWritten();
int64_t framesDelta = currentNumFramesWritten - lastWrittenAndPresentedFrameIndex;
int64_t timeDeltaNanos = framesDelta * oboe::kNanosPerSecond / sampleRate;
// Assume that the next frame will be processed at the current time
using namespace std::chrono;
int64_t appFrameAppTime = getCurrentTimeNanos();//duration_cast<nanoseconds> (steady_clock::now().time_since_epoch()).count();
int64_t appFrameAppTime2 = duration_cast<nanoseconds> (steady_clock::now().time_since_epoch()).count();
int64_t nextPresentationTimeNanos = lastFramePresentationTimeNanos + timeDeltaNanos;
int64_t nextFrameWriteTimeNanos = getCurrentTimeNanos();
// Calculate the number of frames between app and hardware
int64_t frameIndexDelta = appFrameIndex - hardwareFrameIndex;
if (nextFrameWriteTimeNanos < 0)
return;
// Calculate the time which the next frame will be or was presented
int64_t frameTimeDelta = (frameIndexDelta * oboe::kNanosPerSecond) / sampleRate;
int64_t appFrameHardwareTime = hardwareFrameHardwareTime + frameTimeDelta;
outputLatency = (int) ((nextPresentationTimeNanos - nextFrameWriteTimeNanos) * sampleRate / oboe::kNanosPerSecond);
// Calculate latency as a difference in time between when the current frame is at the app
// and when it is at the hardware.
auto latencyNanos = isOutput ? (appFrameHardwareTime - appFrameAppTime) : (appFrameAppTime - appFrameHardwareTime);
return static_cast<int> ((latencyNanos * sampleRate) / oboe::kNanosPerSecond);
} }
int64_t getCurrentTimeNanos() int64_t getCurrentTimeNanos()
@@ -900,19 +907,23 @@ private:
void onErrorBeforeClose (oboe::AudioStream* stream, oboe::Result error) override void onErrorBeforeClose (oboe::AudioStream* stream, oboe::Result error) override
{ {
attachAndroidJNI();
// only output stream should be the master stream receiving callbacks // only output stream should be the master stream receiving callbacks
jassert (stream->getDirection() == oboe::Direction::Output); jassert (stream->getDirection() == oboe::Direction::Output);
JUCE_OBOE_LOG ("Oboe stream onErrorBeforeClose(): " + String (oboe::convertToText (error)));
JUCE_OBOE_LOG ("Oboe stream onErrorBeforeClose(): " + getOboeString (error));
printStreamDebugInfo (stream); printStreamDebugInfo (stream);
} }
void onErrorAfterClose (oboe::AudioStream* stream, oboe::Result error) override void onErrorAfterClose (oboe::AudioStream* stream, oboe::Result error) override
{ {
attachAndroidJNI();
// only output stream should be the master stream receiving callbacks // only output stream should be the master stream receiving callbacks
jassert (stream->getDirection() == oboe::Direction::Output); jassert (stream->getDirection() == oboe::Direction::Output);
JUCE_OBOE_LOG ("Oboe stream onErrorAfterClose(): " + String (oboe::convertToText (error)));
JUCE_OBOE_LOG ("Oboe stream onErrorAfterClose(): " + getOboeString (error));
if (error == oboe::Result::ErrorDisconnected) if (error == oboe::Result::ErrorDisconnected)
{ {
@@ -948,7 +959,10 @@ private:
Atomic<int> audioCallbackGuard { 0 }, Atomic<int> audioCallbackGuard { 0 },
streamRestartGuard { 0 }; streamRestartGuard { 0 };
bool isOutputLatencyDetectionSupported = true;
bool isInputLatencyDetectionSupported = false;
int inputLatency = -1;
bool isOutputLatencyDetectionSupported = false;
int outputLatency = -1; int outputLatency = -1;
}; };
@@ -1016,6 +1030,23 @@ private:
return androidHasSystemFeature ("android.hardware.audio.pro"); return androidHasSystemFeature ("android.hardware.audio.pro");
} }
static int getDefaultFramesPerBurst()
{
// NB: this function only works for inbuilt speakers and headphones
auto* env = getEnv();
auto audioManager = LocalRef<jobject> (env->CallObjectMethod (android.activity,
JuceAppActivity.getSystemService,
javaString ("audio").get()));
auto propertyJavaString = javaString ("android.media.property.OUTPUT_FRAMES_PER_BUFFER");
auto framesPerBurstString = LocalRef<jstring> ((jstring) android.activity.callObjectMethod (JuceAppActivity.audioManagerGetProperty,
propertyJavaString.get()));
return framesPerBurstString != 0 ? env->CallStaticIntMethod (JavaInteger, JavaInteger.parseInt, framesPerBurstString.get(), 10) : 192;
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OboeAudioIODevice) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OboeAudioIODevice)
}; };
@@ -1098,7 +1129,7 @@ public:
forInput ? oboe::Direction::Input : oboe::Direction::Output, forInput ? oboe::Direction::Input : oboe::Direction::Output,
oboe::SharingMode::Shared, oboe::SharingMode::Shared,
forInput ? 1 : 2, forInput ? 1 : 2,
getSdkVersion() >= 21 ? oboe::AudioFormat::Float : oboe::AudioFormat::I16,
getSDKVersion() >= 21 ? oboe::AudioFormat::Float : oboe::AudioFormat::I16,
(int) OboeAudioIODevice::getNativeSampleRate(), (int) OboeAudioIODevice::getNativeSampleRate(),
OboeAudioIODevice::getNativeBufferSize(), OboeAudioIODevice::getNativeBufferSize(),
nullptr); nullptr);
@@ -1117,18 +1148,17 @@ public:
int getIndexOfDevice (AudioIODevice* device, bool asInput) const override int getIndexOfDevice (AudioIODevice* device, bool asInput) const override
{ {
if (device == nullptr)
return -1;
auto* oboeDevice = static_cast<OboeAudioIODevice*> (device);
auto oboeDeviceId = asInput ? oboeDevice->inputDeviceId
: oboeDevice->outputDeviceId;
if (auto oboeDevice = static_cast<OboeAudioIODevice*> (device))
{
auto oboeDeviceId = asInput ? oboeDevice->inputDeviceId
: oboeDevice->outputDeviceId;
auto& devices = asInput ? inputDevices : outputDevices;
auto& devices = asInput ? inputDevices : outputDevices;
for (int i = 0; i < devices.size(); ++i)
if (devices.getReference (i).id == oboeDeviceId)
return i;
for (int i = 0; i < devices.size(); ++i)
if (devices.getReference (i).id == oboeDeviceId)
return i;
}
return -1; return -1;
} }
@@ -1233,11 +1263,11 @@ public:
bool supportsDevicesInfo() const bool supportsDevicesInfo() const
{ {
static auto result = getSdkVersion() >= 23;
static auto result = getSDKVersion() >= 23;
return result; return result;
} }
int getSdkVersion() const
int getSDKVersion() const
{ {
static auto sdkVersion = getEnv()->GetStaticIntField (AndroidBuildVersion, AndroidBuildVersion.SDK_INT); static auto sdkVersion = getEnv()->GetStaticIntField (AndroidBuildVersion, AndroidBuildVersion.SDK_INT);
return sdkVersion; return sdkVersion;
@@ -1399,7 +1429,7 @@ public:
return testStream != nullptr && testStream->openedOk(); return testStream != nullptr && testStream->openedOk();
} }
pthread_t startThread (void* (*entry) (void*), void* userPtr)
pthread_t startThread (void*(*entry)(void*), void* userPtr)
{ {
pthread_mutex_lock (&threadReadyMutex); pthread_mutex_lock (&threadReadyMutex);
@@ -1439,26 +1469,16 @@ public:
void onErrorBeforeClose (oboe::AudioStream*, oboe::Result error) override void onErrorBeforeClose (oboe::AudioStream*, oboe::Result error) override
{ {
JUCE_OBOE_LOG ("OboeRealtimeThread: Oboe stream onErrorBeforeClose(): " + String (oboe::convertToText (error)));
JUCE_OBOE_LOG ("OboeRealtimeThread: Oboe stream onErrorBeforeClose(): " + getOboeString (error));
ignoreUnused (error);
jassertfalse; // Should never get here!
} }
void onErrorAfterClose (oboe::AudioStream* stream, oboe::Result error) override
void onErrorAfterClose (oboe::AudioStream*, oboe::Result error) override
{ {
JUCE_OBOE_LOG ("OboeRealtimeThread: Oboe stream onErrorAfterClose(): " + String (oboe::convertToText (error)));
if (error == oboe::Result::ErrorDisconnected)
{
testStream.reset();
testStream.reset (new OboeStream (-1,
oboe::Direction::Output,
oboe::SharingMode::Exclusive,
1,
formatUsed,
(int) OboeAudioIODevice::getNativeSampleRate(),
OboeAudioIODevice::getNativeBufferSize(),
this));
testStream->start();
}
JUCE_OBOE_LOG ("OboeRealtimeThread: Oboe stream onErrorAfterClose(): " + getOboeString (error));
ignoreUnused (error);
jassertfalse; // Should never get here!
} }
private: private:
@@ -1482,10 +1502,7 @@ pthread_t juce_createRealtimeAudioThread (void* (*entry) (void*), void* userPtr)
return {}; return {};
auto threadID = thread->startThread (entry, userPtr); auto threadID = thread->startThread (entry, userPtr);
// the thread will de-allocate itself
thread.release();
thread.release(); // the thread will de-allocate itself
return threadID; return threadID;
} }


Loading…
Cancel
Save