diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Android.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Android.h index fded6d2871..b81bd63b8d 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Android.h +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Android.h @@ -881,7 +881,7 @@ private: { props.add (new TextPropertyComponent (androidOboeRepositoryPath, "Oboe Repository Path", 2048, false), "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"), "If enabled, this will set the android.permission.INTERNET flag in the manifest."); diff --git a/modules/juce_audio_devices/native/juce_android_Oboe.cpp b/modules/juce_audio_devices/native/juce_android_Oboe.cpp index 1b6c2bb4c1..79cedceff5 100644 --- a/modules/juce_audio_devices/native/juce_android_Oboe.cpp +++ b/modules/juce_audio_devices/native/juce_android_Oboe.cpp @@ -124,6 +124,12 @@ struct OboeAudioIODeviceBufferHelpers } } }; + +template +static String getOboeString (const Type& value) +{ + return String (oboe::convertToText (value)); +} //============================================================================== class OboeAudioIODevice : public AudioIODevice @@ -438,8 +444,8 @@ private: ~OboeStream() { - // AudioStreamCallback can only be deleted when stream has been closed close(); + delete stream; } bool openedOk() const noexcept @@ -458,40 +464,47 @@ private: int64 timeoutNanos = 1000 * oboe::kNanosPerMillisecond; 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()); - return stream; } 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: @@ -501,39 +514,38 @@ private: int32 sampleRate, int32 bufferSize, oboe::AudioStreamCallback* callback = nullptr) { + oboe::DefaultStreamValues::FramesPerBurst = getDefaultFramesPerBurst(); + oboe::AudioStreamBuilder builder; if (deviceId != -1) 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.setSharingMode (sharingMode); builder.setChannelCount (channelCount); builder.setFormat (format); builder.setSampleRate (sampleRate); - builder.setFramesPerCallback ((int32) defaultFramesPerBurst); builder.setPerformanceMode (oboe::PerformanceMode::LowLatency); builder.setCallback (callback); JUCE_OBOE_LOG (String ("Preparing Oboe stream with params:") + "\nAAudio supported = " + String (int (builder.isAAudioSupported())) - + "\nAPI = " + String (oboe::convertToText (builder.getAudioApi())) + + "\nAPI = " + getOboeString (builder.getAudioApi()) + "\nDeviceId = " + String (deviceId) - + "\nDirection = " + String (oboe::convertToText (direction)) - + "\nSharingMode = " + String (oboe::convertToText (sharingMode)) + + "\nDirection = " + getOboeString (direction) + + "\nSharingMode = " + getOboeString (sharingMode) + "\nChannelCount = " + String (channelCount) - + "\nFormat = " + String (oboe::convertToText (format)) + + "\nFormat = " + getOboeString (format) + "\nSampleRate = " + String (sampleRate) + "\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); - 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) stream->setBufferSizeInFrames (bufferSize); @@ -541,10 +553,10 @@ private: JUCE_OBOE_LOG (String ("Stream details:") + "\nUses AAudio = " + (stream != nullptr ? String ((int) stream->usesAAudio()) : 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 ("?")) - + "\nFormat = " + (stream != nullptr ? String (oboe::convertToText (stream->getFormat())) : String ("?")) + + "\nFormat = " + (stream != nullptr ? getOboeString (stream->getFormat()) : String ("?")) + "\nSampleRate = " + (stream != nullptr ? String (stream->getSampleRate()) : String ("?")) + "\nBufferSizeInFrames = " + (stream != nullptr ? String (stream->getBufferSizeInFrames()) : String ("?")) + "\nBufferCapacityInFrames = " + (stream != nullptr ? String (stream->getBufferCapacityInFrames()) : String ("?")) @@ -552,7 +564,7 @@ private: + "\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))); + + "\nPerformanceMode = " + getOboeString (oboe::PerformanceMode::LowLatency)); } void close() @@ -560,27 +572,10 @@ private: if (stream != nullptr) { 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 (env->CallObjectMethod (android.activity, - JuceAppActivity.getSystemService, - javaString ("audio").get())); - - auto propertyJavaString = javaString ("android.media.property.OUTPUT_FRAMES_PER_BUFFER"); - - auto framesPerBurstString = LocalRef ((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::Result openResult; }; @@ -672,9 +667,7 @@ private: void checkStreamSetup (OboeStream* stream, int deviceId, int numChannels, int sampleRate, 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 (streamFormat, bitDepth); @@ -739,7 +732,8 @@ private: outputStream->start(); - checkIsOutputLatencyDetectionSupported(); + isInputLatencyDetectionSupported = isLatencyDetectionSupported (inputStream.get()); + isOutputLatencyDetectionSupported = isLatencyDetectionSupported (outputStream.get()); } void stop() override @@ -754,19 +748,16 @@ private: } int getOutputLatencyInSamples() override { return outputLatency; } - int getInputLatencyInSamples() override { return -1; } + int getInputLatencyInSamples() override { return inputLatency; } 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 @@ -792,7 +783,7 @@ private: 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; return oboe::DataCallbackResult::Continue; } @@ -809,9 +800,11 @@ private: } 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::convertToOboe (outputStreamSampleBuffer, static_cast (audioData), numFrames); if (isOutputLatencyDetectionSupported) - calculateOutputLatency(); + outputLatency = getLatencyFor (*outputStream); audioCallbackGuard.set (0); } @@ -847,10 +840,10 @@ private: ignoreUnused (stream); 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 ("?")) - + "\nFormat = " + (stream != nullptr ? String (oboe::convertToText (stream->getFormat())) : String ("?")) + + "\nFormat = " + (stream != nullptr ? getOboeString (stream->getFormat()) : String ("?")) + "\nSampleRate = " + (stream != nullptr ? String (stream->getSampleRate()) : String ("?")) + "\nBufferSizeInFrames = " + (stream != nullptr ? String (stream->getBufferSizeInFrames()) : String ("?")) + "\nBufferCapacityInFrames = " + (stream != nullptr ? String (stream->getBufferCapacityInFrames()) : String ("?")) @@ -858,34 +851,48 @@ private: + "\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)) + + "\nPerformanceMode = " + getOboeString (oboe::PerformanceMode::LowLatency) + "\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 ((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) - 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 (steady_clock::now().time_since_epoch()).count(); + int64_t appFrameAppTime2 = duration_cast (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 ((latencyNanos * sampleRate) / oboe::kNanosPerSecond); } int64_t getCurrentTimeNanos() @@ -900,19 +907,23 @@ private: void onErrorBeforeClose (oboe::AudioStream* stream, oboe::Result error) override { + attachAndroidJNI(); + // only output stream should be the master stream receiving callbacks 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); } void onErrorAfterClose (oboe::AudioStream* stream, oboe::Result error) override { + attachAndroidJNI(); + // only output stream should be the master stream receiving callbacks 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) { @@ -948,7 +959,10 @@ private: Atomic audioCallbackGuard { 0 }, streamRestartGuard { 0 }; - bool isOutputLatencyDetectionSupported = true; + bool isInputLatencyDetectionSupported = false; + int inputLatency = -1; + + bool isOutputLatencyDetectionSupported = false; int outputLatency = -1; }; @@ -1016,6 +1030,23 @@ private: 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 (env->CallObjectMethod (android.activity, + JuceAppActivity.getSystemService, + javaString ("audio").get())); + + auto propertyJavaString = javaString ("android.media.property.OUTPUT_FRAMES_PER_BUFFER"); + + auto framesPerBurstString = LocalRef ((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) }; @@ -1098,7 +1129,7 @@ public: forInput ? oboe::Direction::Input : oboe::Direction::Output, oboe::SharingMode::Shared, forInput ? 1 : 2, - getSdkVersion() >= 21 ? oboe::AudioFormat::Float : oboe::AudioFormat::I16, + getSDKVersion() >= 21 ? oboe::AudioFormat::Float : oboe::AudioFormat::I16, (int) OboeAudioIODevice::getNativeSampleRate(), OboeAudioIODevice::getNativeBufferSize(), nullptr); @@ -1117,18 +1148,17 @@ public: int getIndexOfDevice (AudioIODevice* device, bool asInput) const override { - if (device == nullptr) - return -1; - - auto* oboeDevice = static_cast (device); - auto oboeDeviceId = asInput ? oboeDevice->inputDeviceId - : oboeDevice->outputDeviceId; + if (auto oboeDevice = static_cast (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; } @@ -1233,11 +1263,11 @@ public: bool supportsDevicesInfo() const { - static auto result = getSdkVersion() >= 23; + static auto result = getSDKVersion() >= 23; return result; } - int getSdkVersion() const + int getSDKVersion() const { static auto sdkVersion = getEnv()->GetStaticIntField (AndroidBuildVersion, AndroidBuildVersion.SDK_INT); return sdkVersion; @@ -1399,7 +1429,7 @@ public: 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); @@ -1439,26 +1469,16 @@ public: 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: @@ -1482,10 +1502,7 @@ pthread_t juce_createRealtimeAudioThread (void* (*entry) (void*), void* userPtr) return {}; auto threadID = thread->startThread (entry, userPtr); - - // the thread will de-allocate itself - thread.release(); - + thread.release(); // the thread will de-allocate itself return threadID; }