upgrade to JUCE 5.4.3. Remove (probably) unused JUCE modules. Remove VST2 target (it's been end-of-life'd by Steinberg and by JUCE)
This commit is contained in:
@ -125,6 +125,12 @@ struct OboeAudioIODeviceBufferHelpers<float>
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
static String getOboeString (const Type& value)
|
||||
{
|
||||
return String (oboe::convertToText (value));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class OboeAudioIODevice : public AudioIODevice
|
||||
{
|
||||
@ -272,7 +278,7 @@ public:
|
||||
// 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();
|
||||
* getNativeBufferSize();
|
||||
}
|
||||
|
||||
double getCurrentSampleRate() override
|
||||
@ -391,9 +397,9 @@ private:
|
||||
// Setting nullptr callback is allowed only when playback is stopped.
|
||||
jassert (callbackToUse != nullptr);
|
||||
|
||||
while (true)
|
||||
for (;;)
|
||||
{
|
||||
AudioIODeviceCallback* old = callback.get();
|
||||
auto old = callback.get();
|
||||
|
||||
if (old == callbackToUse)
|
||||
break;
|
||||
@ -409,7 +415,7 @@ private:
|
||||
void process (const float** inputChannelData, int numInputChannels,
|
||||
float** outputChannelData, int numOutputChannels, int32_t numFrames)
|
||||
{
|
||||
if (AudioIODeviceCallback* cb = callback.exchange (nullptr))
|
||||
if (auto* cb = callback.exchange (nullptr))
|
||||
{
|
||||
cb->audioDeviceIOCallback (inputChannelData, numInputChannels,
|
||||
outputChannelData, numOutputChannels, numFrames);
|
||||
@ -438,8 +444,8 @@ private:
|
||||
|
||||
~OboeStream()
|
||||
{
|
||||
// AudioStreamCallback can only be deleted when stream has been closed
|
||||
close();
|
||||
delete stream;
|
||||
}
|
||||
|
||||
bool openedOk() const noexcept
|
||||
@ -458,37 +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)));
|
||||
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: " + 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 ("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
|
||||
{
|
||||
return stream != nullptr ? stream->getXRunCount() : 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:
|
||||
@ -498,50 +514,50 @@ 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.
|
||||
builder.setDirection (direction);
|
||||
builder.setSharingMode (sharingMode);
|
||||
builder.setChannelCount (channelCount);
|
||||
builder.setFormat (format);
|
||||
builder.setSampleRate (sampleRate);
|
||||
builder.setDefaultFramesPerBurst ((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)));
|
||||
+ "\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)
|
||||
if (stream != nullptr && bufferSize != 0)
|
||||
{
|
||||
JUCE_OBOE_LOG ("Setting the bufferSizeInFrames to " + String (bufferSize));
|
||||
stream->setBufferSizeInFrames (bufferSize);
|
||||
}
|
||||
|
||||
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 ("?"))
|
||||
@ -549,7 +565,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()
|
||||
@ -557,27 +573,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<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::Result openResult;
|
||||
};
|
||||
@ -669,20 +668,14 @@ 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);
|
||||
|
||||
jassert (deviceId = nativeStream->getDeviceId());
|
||||
jassert (numChannels = nativeStream->getChannelCount());
|
||||
jassert (sampleRate == nativeStream->getSampleRate());
|
||||
jassert (numChannels == nativeStream->getChannelCount());
|
||||
jassert (sampleRate == 0 || sampleRate == nativeStream->getSampleRate());
|
||||
jassert (format == nativeStream->getFormat());
|
||||
|
||||
if (nativeStream->usesAAudio())
|
||||
jassert (bufferSize == nativeStream->getBufferSizeInFrames());
|
||||
}
|
||||
}
|
||||
|
||||
@ -737,7 +730,8 @@ private:
|
||||
|
||||
outputStream->start();
|
||||
|
||||
checkIsOutputLatencyDetectionSupported();
|
||||
isInputLatencyDetectionSupported = isLatencyDetectionSupported (inputStream.get());
|
||||
isOutputLatencyDetectionSupported = isLatencyDetectionSupported (outputStream.get());
|
||||
}
|
||||
|
||||
void stop() override
|
||||
@ -752,25 +746,20 @@ 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
|
||||
{
|
||||
attachAndroidJNI();
|
||||
|
||||
if (audioCallbackGuard.compareAndSetBool (1, 0))
|
||||
{
|
||||
if (stream == nullptr)
|
||||
@ -790,26 +779,28 @@ 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;
|
||||
}
|
||||
|
||||
auto result = inputStream->getNativeStream()->read (inputStreamNativeBuffer.getData(), numFrames, 0);
|
||||
|
||||
if (result >= 0)
|
||||
if (result)
|
||||
{
|
||||
OboeAudioIODeviceBufferHelpers<SampleType>::referAudioBufferDirectlyToOboeIfPossible (inputStreamNativeBuffer.get(),
|
||||
inputStreamSampleBuffer,
|
||||
result);
|
||||
result.value());
|
||||
|
||||
OboeAudioIODeviceBufferHelpers<SampleType>::convertFromOboe (inputStreamNativeBuffer.get(), inputStreamSampleBuffer, result);
|
||||
OboeAudioIODeviceBufferHelpers<SampleType>::convertFromOboe (inputStreamNativeBuffer.get(), inputStreamSampleBuffer, result.value());
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
//-----------------
|
||||
@ -832,7 +823,7 @@ private:
|
||||
OboeAudioIODeviceBufferHelpers<SampleType>::convertToOboe (outputStreamSampleBuffer, static_cast<SampleType*> (audioData), numFrames);
|
||||
|
||||
if (isOutputLatencyDetectionSupported)
|
||||
calculateOutputLatency();
|
||||
outputLatency = getLatencyFor (*outputStream);
|
||||
|
||||
audioCallbackGuard.set (0);
|
||||
}
|
||||
@ -845,10 +836,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 ("?"))
|
||||
@ -856,34 +847,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();
|
||||
|
||||
auto result = outputStream->getNativeStream()->getTimestamp (CLOCK_MONOTONIC,
|
||||
&lastWrittenAndPresentedFrameIndex,
|
||||
&lastFramePresentationTimeNanos);
|
||||
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 = nativeStream.getTimestamp (CLOCK_MONOTONIC,
|
||||
&hardwareFrameIndex,
|
||||
&hardwareFrameHardwareTime);
|
||||
|
||||
if (result != oboe::Result::OK)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
int64_t currentNumFramesWritten = outputStream->getNativeStream()->getFramesWritten();
|
||||
int64_t framesDelta = currentNumFramesWritten - lastWrittenAndPresentedFrameIndex;
|
||||
int64_t timeDeltaNanos = framesDelta * oboe::kNanosPerSecond / sampleRate;
|
||||
// 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 nextPresentationTimeNanos = lastFramePresentationTimeNanos + timeDeltaNanos;
|
||||
int64_t nextFrameWriteTimeNanos = getCurrentTimeNanos();
|
||||
// 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();
|
||||
|
||||
if (nextFrameWriteTimeNanos < 0)
|
||||
return;
|
||||
// Calculate the number of frames between app and hardware
|
||||
int64_t frameIndexDelta = appFrameIndex - hardwareFrameIndex;
|
||||
|
||||
outputLatency = (int) ((nextPresentationTimeNanos - nextFrameWriteTimeNanos) * sampleRate / oboe::kNanosPerSecond);
|
||||
// Calculate the time which the next frame will be or was presented
|
||||
int64_t frameTimeDelta = (frameIndexDelta * oboe::kNanosPerSecond) / sampleRate;
|
||||
int64_t appFrameHardwareTime = hardwareFrameHardwareTime + frameTimeDelta;
|
||||
|
||||
// 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()
|
||||
@ -901,7 +906,7 @@ private:
|
||||
// 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);
|
||||
}
|
||||
|
||||
@ -910,7 +915,7 @@ private:
|
||||
// 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)
|
||||
{
|
||||
@ -946,7 +951,10 @@ private:
|
||||
Atomic<int> audioCallbackGuard { 0 },
|
||||
streamRestartGuard { 0 };
|
||||
|
||||
bool isOutputLatencyDetectionSupported = true;
|
||||
bool isInputLatencyDetectionSupported = false;
|
||||
int inputLatency = -1;
|
||||
|
||||
bool isOutputLatencyDetectionSupported = false;
|
||||
int outputLatency = -1;
|
||||
};
|
||||
|
||||
@ -972,32 +980,7 @@ private:
|
||||
|
||||
bool running = false;
|
||||
|
||||
enum
|
||||
{
|
||||
// These at the moment correspond to OpenSL settings.
|
||||
bufferSizeMultForLowLatency = 4,
|
||||
bufferSizeMultForSlowAudio = 8,
|
||||
defaultBufferSizeIsMultipleOfNative = 1
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
static String audioManagerGetProperty (const String& property)
|
||||
{
|
||||
const LocalRef<jstring> jProperty (javaString (property));
|
||||
const LocalRef<jstring> text ((jstring) android.activity.callObjectMethod (JuceAppActivity.audioManagerGetProperty,
|
||||
jProperty.get()));
|
||||
if (text.get() != 0)
|
||||
return juceString (text);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static bool androidHasSystemFeature (const String& property)
|
||||
{
|
||||
const LocalRef<jstring> jProperty (javaString (property));
|
||||
return android.activity.callBooleanMethod (JuceAppActivity.hasSystemFeature, jProperty.get());
|
||||
}
|
||||
|
||||
static double getNativeSampleRate()
|
||||
{
|
||||
return audioManagerGetProperty ("android.media.property.OUTPUT_SAMPLE_RATE").getDoubleValue();
|
||||
@ -1014,6 +997,14 @@ private:
|
||||
return androidHasSystemFeature ("android.hardware.audio.pro");
|
||||
}
|
||||
|
||||
static int getDefaultFramesPerBurst()
|
||||
{
|
||||
// NB: this function only works for inbuilt speakers and headphones
|
||||
auto framesPerBurstString = javaString (audioManagerGetProperty ("android.media.property.OUTPUT_FRAMES_PER_BUFFER"));
|
||||
|
||||
return framesPerBurstString != 0 ? getEnv()->CallStaticIntMethod (JavaInteger, JavaInteger.parseInt, framesPerBurstString.get(), 10) : 192;
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OboeAudioIODevice)
|
||||
};
|
||||
|
||||
@ -1028,7 +1019,7 @@ OboeAudioIODevice::OboeSessionBase* OboeAudioIODevice::OboeSessionBase::create (
|
||||
{
|
||||
|
||||
std::unique_ptr<OboeSessionBase> session;
|
||||
auto sdkVersion = getEnv()->GetStaticIntField (AndroidBuildVersion, AndroidBuildVersion.SDK_INT);
|
||||
auto sdkVersion = getAndroidSDKVersion();
|
||||
|
||||
// SDK versions 21 and higher should natively support floating point...
|
||||
if (sdkVersion >= 21)
|
||||
@ -1096,7 +1087,7 @@ public:
|
||||
forInput ? oboe::Direction::Input : oboe::Direction::Output,
|
||||
oboe::SharingMode::Shared,
|
||||
forInput ? 1 : 2,
|
||||
getSdkVersion() >= 21 ? oboe::AudioFormat::Float : oboe::AudioFormat::I16,
|
||||
getAndroidSDKVersion() >= 21 ? oboe::AudioFormat::Float : oboe::AudioFormat::I16,
|
||||
(int) OboeAudioIODevice::getNativeSampleRate(),
|
||||
OboeAudioIODevice::getNativeBufferSize(),
|
||||
nullptr);
|
||||
@ -1115,18 +1106,17 @@ public:
|
||||
|
||||
int getIndexOfDevice (AudioIODevice* device, bool asInput) const override
|
||||
{
|
||||
if (device == nullptr)
|
||||
return -1;
|
||||
if (auto oboeDevice = static_cast<OboeAudioIODevice*> (device))
|
||||
{
|
||||
auto oboeDeviceId = asInput ? oboeDevice->inputDeviceId
|
||||
: oboeDevice->outputDeviceId;
|
||||
|
||||
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;
|
||||
}
|
||||
@ -1188,8 +1178,8 @@ public:
|
||||
if (audioManagerClass == 0)
|
||||
return;
|
||||
|
||||
auto audioManager = LocalRef<jobject> (env->CallObjectMethod (android.activity,
|
||||
JuceAppActivity.getSystemService,
|
||||
auto audioManager = LocalRef<jobject> (env->CallObjectMethod (getAppContext().get(),
|
||||
AndroidContext.getSystemService,
|
||||
javaString ("audio").get()));
|
||||
|
||||
static jmethodID getDevicesMethod = env->GetMethodID (audioManagerClass, "getDevices",
|
||||
@ -1231,16 +1221,10 @@ public:
|
||||
|
||||
bool supportsDevicesInfo() const
|
||||
{
|
||||
static auto result = getSdkVersion() >= 23;
|
||||
static auto result = getAndroidSDKVersion() >= 23;
|
||||
return result;
|
||||
}
|
||||
|
||||
int getSdkVersion() const
|
||||
{
|
||||
static auto sdkVersion = getEnv()->GetStaticIntField (AndroidBuildVersion, AndroidBuildVersion.SDK_INT);
|
||||
return sdkVersion;
|
||||
}
|
||||
|
||||
void addDevice (const LocalRef<jobject>& device, JNIEnv* env)
|
||||
{
|
||||
auto deviceClass = LocalRef<jclass> ((jclass) env->FindClass ("android/media/AudioDeviceInfo"));
|
||||
@ -1397,7 +1381,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);
|
||||
|
||||
@ -1437,26 +1421,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:
|
||||
@ -1480,10 +1454,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;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user