| @@ -155,6 +155,8 @@ template <typename T> struct BufferHelpers {}; | |||||
| template <> | template <> | ||||
| struct BufferHelpers<int16> | struct BufferHelpers<int16> | ||||
| { | { | ||||
| enum { isFloatingPoint = 0 }; | |||||
| static void initPCMDataFormat (PCMDataFormatEx& dataFormat, int numChannels, double sampleRate) | static void initPCMDataFormat (PCMDataFormatEx& dataFormat, int numChannels, double sampleRate) | ||||
| { | { | ||||
| dataFormat.formatType = SL_DATAFORMAT_PCM; | dataFormat.formatType = SL_DATAFORMAT_PCM; | ||||
| @@ -202,6 +204,8 @@ struct BufferHelpers<int16> | |||||
| template <> | template <> | ||||
| struct BufferHelpers<float> | struct BufferHelpers<float> | ||||
| { | { | ||||
| enum { isFloatingPoint = 1 }; | |||||
| static void initPCMDataFormat (PCMDataFormatEx& dataFormat, int numChannels, double sampleRate) | static void initPCMDataFormat (PCMDataFormatEx& dataFormat, int numChannels, double sampleRate) | ||||
| { | { | ||||
| dataFormat.formatType = SL_ANDROID_DATAFORMAT_PCM_EX; | dataFormat.formatType = SL_ANDROID_DATAFORMAT_PCM_EX; | ||||
| @@ -514,6 +518,7 @@ public: | |||||
| virtual void start() { stop(); jassert (callback.get() != nullptr); running = true; } | virtual void start() { stop(); jassert (callback.get() != nullptr); running = true; } | ||||
| virtual void stop() { running = false; } | virtual void stop() { running = false; } | ||||
| virtual bool setAudioPreprocessingEnabled (bool shouldEnable) = 0; | virtual bool setAudioPreprocessingEnabled (bool shouldEnable) = 0; | ||||
| virtual bool supportsFloatingPoint() const noexcept = 0; | |||||
| void setCallback (AudioIODeviceCallback* callbackToUse) | void setCallback (AudioIODeviceCallback* callbackToUse) | ||||
| { | { | ||||
| @@ -557,8 +562,7 @@ public: | |||||
| static OpenSLSession* create (DynamicLibrary& slLibrary, | static OpenSLSession* create (DynamicLibrary& slLibrary, | ||||
| int numInputChannels, int numOutputChannels, | int numInputChannels, int numOutputChannels, | ||||
| double samleRateToUse, int bufferSizeToUse, | double samleRateToUse, int bufferSizeToUse, | ||||
| int numBuffersToUse, | |||||
| bool floatingPointSupport); | |||||
| int numBuffersToUse); | |||||
| //============================================================================== | //============================================================================== | ||||
| typedef SLresult (*CreateEngineFunc)(SLObjectItf*,SLuint32,const SLEngineOption*,SLuint32,const SLInterfaceID*,const SLboolean*); | typedef SLresult (*CreateEngineFunc)(SLObjectItf*,SLuint32,const SLEngineOption*,SLuint32,const SLInterfaceID*,const SLboolean*); | ||||
| @@ -667,6 +671,8 @@ public: | |||||
| return true; | return true; | ||||
| } | } | ||||
| bool supportsFloatingPoint() const noexcept override { return (BufferHelpers<T>::isFloatingPoint != 0); } | |||||
| void doSomeWorkOnAudioThread() | void doSomeWorkOnAudioThread() | ||||
| { | { | ||||
| // only the player or the recorder should enter this section at any time | // only the player or the recorder should enter this section at any time | ||||
| @@ -738,8 +744,6 @@ public: | |||||
| inputLatency = (int) ((longestLatency * inputLatency) / totalLatency) & ~15; | inputLatency = (int) ((longestLatency * inputLatency) / totalLatency) & ~15; | ||||
| outputLatency = (int) ((longestLatency * outputLatency) / totalLatency) & ~15; | outputLatency = (int) ((longestLatency * outputLatency) / totalLatency) & ~15; | ||||
| supportsFloatingPoint = getSupportsFloatingPoint(); | |||||
| bool success = slLibrary.open ("libOpenSLES.so"); | bool success = slLibrary.open ("libOpenSLES.so"); | ||||
| // You can only create this class if you are sure that your hardware supports OpenSL | // You can only create this class if you are sure that your hardware supports OpenSL | ||||
| @@ -824,15 +828,6 @@ public: | |||||
| const int audioBuffersToEnqueue = hasLowLatencyAudioPath() ? buffersToEnqueueForLowLatency | const int audioBuffersToEnqueue = hasLowLatencyAudioPath() ? buffersToEnqueueForLowLatency | ||||
| : buffersToEnqueueSlowAudio; | : buffersToEnqueueSlowAudio; | ||||
| DBG ("OpenSL: numInputChannels = " << numInputChannels | |||||
| << ", numOutputChannels = " << numOutputChannels | |||||
| << ", nativeBufferSize = " << getNativeBufferSize() | |||||
| << ", nativeSampleRate = " << getNativeSampleRate() | |||||
| << ", actualBufferSize = " << actualBufferSize | |||||
| << ", audioBuffersToEnqueue = " << audioBuffersToEnqueue | |||||
| << ", sampleRate = " << sampleRate | |||||
| << ", supportsFloatingPoint = " << (supportsFloatingPoint ? "true" : "false")); | |||||
| if (numInputChannels > 0 && (! RuntimePermissions::isGranted (RuntimePermissions::recordAudio))) | if (numInputChannels > 0 && (! RuntimePermissions::isGranted (RuntimePermissions::recordAudio))) | ||||
| { | { | ||||
| // If you hit this assert, you probably forgot to get RuntimePermissions::recordAudio | // If you hit this assert, you probably forgot to get RuntimePermissions::recordAudio | ||||
| @@ -842,8 +837,7 @@ public: | |||||
| } | } | ||||
| session = OpenSLSession::create (slLibrary, numInputChannels, numOutputChannels, | session = OpenSLSession::create (slLibrary, numInputChannels, numOutputChannels, | ||||
| sampleRate, actualBufferSize, audioBuffersToEnqueue, | |||||
| supportsFloatingPoint); | |||||
| sampleRate, actualBufferSize, audioBuffersToEnqueue); | |||||
| if (session != nullptr) | if (session != nullptr) | ||||
| session->setAudioPreprocessingEnabled (audioProcessingEnabled); | session->setAudioPreprocessingEnabled (audioProcessingEnabled); | ||||
| else | else | ||||
| @@ -855,11 +849,19 @@ public: | |||||
| numInputChannels = 0; | numInputChannels = 0; | ||||
| session = OpenSLSession::create(slLibrary, numInputChannels, numOutputChannels, | session = OpenSLSession::create(slLibrary, numInputChannels, numOutputChannels, | ||||
| sampleRate, actualBufferSize, audioBuffersToEnqueue, | |||||
| supportsFloatingPoint); | |||||
| sampleRate, actualBufferSize, audioBuffersToEnqueue); | |||||
| } | } | ||||
| } | } | ||||
| DBG ("OpenSL: numInputChannels = " << numInputChannels | |||||
| << ", numOutputChannels = " << numOutputChannels | |||||
| << ", nativeBufferSize = " << getNativeBufferSize() | |||||
| << ", nativeSampleRate = " << getNativeSampleRate() | |||||
| << ", actualBufferSize = " << actualBufferSize | |||||
| << ", audioBuffersToEnqueue = " << audioBuffersToEnqueue | |||||
| << ", sampleRate = " << sampleRate | |||||
| << ", supportsFloatingPoint = " << (session != nullptr && session->supportsFloatingPoint() ? "true" : "false")); | |||||
| if (session == nullptr) | if (session == nullptr) | ||||
| lastError = "Unknown error initializing opensl session"; | lastError = "Unknown error initializing opensl session"; | ||||
| @@ -878,7 +880,7 @@ public: | |||||
| int getInputLatencyInSamples() override { return inputLatency; } | int getInputLatencyInSamples() override { return inputLatency; } | ||||
| bool isOpen() override { return deviceOpen; } | bool isOpen() override { return deviceOpen; } | ||||
| int getCurrentBufferSizeSamples() override { return actualBufferSize; } | int getCurrentBufferSizeSamples() override { return actualBufferSize; } | ||||
| int getCurrentBitDepth() override { return supportsFloatingPoint ? 32 : 16; } | |||||
| int getCurrentBitDepth() override { return (session != nullptr && session->supportsFloatingPoint() ? 32 : 16); } | |||||
| BigInteger getActiveOutputChannels() const override { return activeOutputChans; } | BigInteger getActiveOutputChannels() const override { return activeOutputChans; } | ||||
| BigInteger getActiveInputChannels() const override { return activeInputChans; } | BigInteger getActiveInputChannels() const override { return activeInputChans; } | ||||
| String getLastError() override { return lastError; } | String getLastError() override { return lastError; } | ||||
| @@ -960,7 +962,7 @@ private: | |||||
| DynamicLibrary slLibrary; | DynamicLibrary slLibrary; | ||||
| int actualBufferSize, sampleRate; | int actualBufferSize, sampleRate; | ||||
| int inputLatency, outputLatency; | int inputLatency, outputLatency; | ||||
| bool deviceOpen, supportsFloatingPoint, audioProcessingEnabled; | |||||
| bool deviceOpen, audioProcessingEnabled; | |||||
| String lastError; | String lastError; | ||||
| BigInteger activeOutputChans, activeInputChans; | BigInteger activeOutputChans, activeInputChans; | ||||
| AudioIODeviceCallback* callback; | AudioIODeviceCallback* callback; | ||||
| @@ -1015,31 +1017,36 @@ private: | |||||
| return androidHasSystemFeature ("android.hardware.audio.low_latency"); | return androidHasSystemFeature ("android.hardware.audio.low_latency"); | ||||
| } | } | ||||
| static bool getSupportsFloatingPoint() | |||||
| { | |||||
| return (getEnv()->GetStaticIntField (AndroidBuildVersion, AndroidBuildVersion.SDK_INT) >= 21); | |||||
| } | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenSLAudioIODevice) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenSLAudioIODevice) | ||||
| }; | }; | ||||
| OpenSLAudioIODevice::OpenSLSession* OpenSLAudioIODevice::OpenSLSession::create (DynamicLibrary& slLibrary, | OpenSLAudioIODevice::OpenSLSession* OpenSLAudioIODevice::OpenSLSession::create (DynamicLibrary& slLibrary, | ||||
| int numInputChannels, int numOutputChannels, | int numInputChannels, int numOutputChannels, | ||||
| double samleRateToUse, int bufferSizeToUse, | double samleRateToUse, int bufferSizeToUse, | ||||
| int numBuffersToUse, | |||||
| bool floatingPointSupport) | |||||
| int numBuffersToUse) | |||||
| { | { | ||||
| ScopedPointer<OpenSLSession> retval; | ScopedPointer<OpenSLSession> retval; | ||||
| auto sdkVersion = getEnv()->GetStaticIntField (AndroidBuildVersion, AndroidBuildVersion.SDK_INT); | |||||
| if (floatingPointSupport) | |||||
| // SDK versions 21 and higher should natively support floating point... | |||||
| if (sdkVersion >= 21) | |||||
| { | |||||
| retval = new OpenSLSessionT<float> (slLibrary, numInputChannels, numOutputChannels, samleRateToUse, | retval = new OpenSLSessionT<float> (slLibrary, numInputChannels, numOutputChannels, samleRateToUse, | ||||
| bufferSizeToUse, numBuffersToUse); | bufferSizeToUse, numBuffersToUse); | ||||
| else | |||||
| // ...however, some devices lie so re-try without floating point | |||||
| if (retval != nullptr && (! retval->openedOK())) | |||||
| retval = nullptr; | |||||
| } | |||||
| if (retval == nullptr) | |||||
| { | |||||
| retval = new OpenSLSessionT<int16> (slLibrary, numInputChannels, numOutputChannels, samleRateToUse, | retval = new OpenSLSessionT<int16> (slLibrary, numInputChannels, numOutputChannels, samleRateToUse, | ||||
| bufferSizeToUse, numBuffersToUse); | bufferSizeToUse, numBuffersToUse); | ||||
| if (retval != nullptr && (! retval->openedOK())) | |||||
| retval = nullptr; | |||||
| if (retval != nullptr && (! retval->openedOK())) | |||||
| retval = nullptr; | |||||
| } | |||||
| return retval.release(); | return retval.release(); | ||||
| } | } | ||||