The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1490 lines
62KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2018 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. The code included in this file is provided under the terms of the ISC license
  8. http://www.isc.org/downloads/software-support-policy/isc-license. Permission
  9. To use, copy, modify, and/or distribute this software for any purpose with or
  10. without fee is hereby granted provided that the above copyright notice and
  11. this permission notice appear in all copies.
  12. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  13. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  14. DISCLAIMED.
  15. ==============================================================================
  16. */
  17. #ifndef JUCE_OBOE_LOG_ENABLED
  18. #define JUCE_OBOE_LOG_ENABLED 1
  19. #endif
  20. #if JUCE_OBOE_LOG_ENABLED
  21. #define JUCE_OBOE_LOG(x) DBG(x)
  22. #else
  23. #define JUCE_OBOE_LOG(x) {}
  24. #endif
  25. namespace juce
  26. {
  27. template <typename OboeDataFormat> struct OboeAudioIODeviceBufferHelpers {};
  28. template<>
  29. struct OboeAudioIODeviceBufferHelpers<int16>
  30. {
  31. static oboe::AudioFormat oboeAudioFormat() { return oboe::AudioFormat::I16; }
  32. static constexpr int bitDepth() { return 16; }
  33. static void referAudioBufferDirectlyToOboeIfPossible (int16*, AudioBuffer<float>&, int) {}
  34. static void convertFromOboe (const int16* srcInterleaved, AudioBuffer<float>& audioBuffer, int numSamples)
  35. {
  36. for (int i = 0; i < audioBuffer.getNumChannels(); ++i)
  37. {
  38. using DstSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst>;
  39. using SrcSampleType = AudioData::Pointer<AudioData::Int16, AudioData::NativeEndian, AudioData::Interleaved, AudioData::Const>;
  40. DstSampleType dstData (audioBuffer.getWritePointer (i));
  41. SrcSampleType srcData (srcInterleaved + i, audioBuffer.getNumChannels());
  42. dstData.convertSamples (srcData, numSamples);
  43. }
  44. }
  45. static void convertToOboe (const AudioBuffer<float>& audioBuffer, int16* dstInterleaved, int numSamples)
  46. {
  47. for (int i = 0; i < audioBuffer.getNumChannels(); ++i)
  48. {
  49. using DstSampleType = AudioData::Pointer<AudioData::Int16, AudioData::NativeEndian, AudioData::Interleaved, AudioData::NonConst>;
  50. using SrcSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const>;
  51. DstSampleType dstData (dstInterleaved + i, audioBuffer.getNumChannels());
  52. SrcSampleType srcData (audioBuffer.getReadPointer (i));
  53. dstData.convertSamples (srcData, numSamples);
  54. }
  55. }
  56. };
  57. template<>
  58. struct OboeAudioIODeviceBufferHelpers<float>
  59. {
  60. static oboe::AudioFormat oboeAudioFormat() { return oboe::AudioFormat::Float; }
  61. static constexpr int bitDepth() { return 32; }
  62. static void referAudioBufferDirectlyToOboeIfPossible (float* nativeBuffer, AudioBuffer<float>& audioBuffer, int numSamples)
  63. {
  64. if (audioBuffer.getNumChannels() == 1)
  65. audioBuffer.setDataToReferTo (&nativeBuffer, 1, numSamples);
  66. }
  67. static void convertFromOboe (const float* srcInterleaved, AudioBuffer<float>& audioBuffer, int numSamples)
  68. {
  69. // No need to convert, we instructed the buffer to point to the src data directly already
  70. if (audioBuffer.getNumChannels() == 1)
  71. {
  72. jassert (audioBuffer.getWritePointer (0) == srcInterleaved);
  73. return;
  74. }
  75. for (int i = 0; i < audioBuffer.getNumChannels(); ++i)
  76. {
  77. using DstSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst>;
  78. using SrcSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::Interleaved, AudioData::Const>;
  79. DstSampleType dstData (audioBuffer.getWritePointer (i));
  80. SrcSampleType srcData (srcInterleaved + i, audioBuffer.getNumChannels());
  81. dstData.convertSamples (srcData, numSamples);
  82. }
  83. }
  84. static void convertToOboe (const AudioBuffer<float>& audioBuffer, float* dstInterleaved, int numSamples)
  85. {
  86. // No need to convert, we instructed the buffer to point to the src data directly already
  87. if (audioBuffer.getNumChannels() == 1)
  88. {
  89. jassert (audioBuffer.getReadPointer (0) == dstInterleaved);
  90. return;
  91. }
  92. for (int i = 0; i < audioBuffer.getNumChannels(); ++i)
  93. {
  94. using DstSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::Interleaved, AudioData::NonConst>;
  95. using SrcSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const>;
  96. DstSampleType dstData (dstInterleaved + i, audioBuffer.getNumChannels());
  97. SrcSampleType srcData (audioBuffer.getReadPointer (i));
  98. dstData.convertSamples (srcData, numSamples);
  99. }
  100. }
  101. };
  102. //==============================================================================
  103. class OboeAudioIODevice : public AudioIODevice
  104. {
  105. public:
  106. //==============================================================================
  107. OboeAudioIODevice (const String& deviceName,
  108. int inputDeviceIdToUse,
  109. const Array<int>& supportedInputSampleRatesToUse,
  110. int maxNumInputChannelsToUse,
  111. int outputDeviceIdToUse,
  112. const Array<int>& supportedOutputSampleRatesToUse,
  113. int maxNumOutputChannelsToUse)
  114. : AudioIODevice (deviceName, oboeTypeName),
  115. inputDeviceId (inputDeviceIdToUse),
  116. supportedInputSampleRates (supportedInputSampleRatesToUse),
  117. maxNumInputChannels (maxNumInputChannelsToUse),
  118. outputDeviceId (outputDeviceIdToUse),
  119. supportedOutputSampleRates (supportedOutputSampleRatesToUse),
  120. maxNumOutputChannels (maxNumOutputChannelsToUse)
  121. {
  122. // At least an input or an output has to be supported by the device!
  123. jassert (inputDeviceId != -1 || outputDeviceId != -1);
  124. }
  125. ~OboeAudioIODevice()
  126. {
  127. close();
  128. }
  129. StringArray getOutputChannelNames() override { return getChannelNames (false); }
  130. StringArray getInputChannelNames() override { return getChannelNames (true); }
  131. Array<double> getAvailableSampleRates() override
  132. {
  133. Array<double> result;
  134. auto inputSampleRates = getAvailableSampleRates (true);
  135. auto outputSampleRates = getAvailableSampleRates (false);
  136. if (inputDeviceId == -1)
  137. {
  138. for (auto& sr : outputSampleRates)
  139. result.add (sr);
  140. }
  141. else if (outputDeviceId == -1)
  142. {
  143. for (auto& sr : inputSampleRates)
  144. result.add (sr);
  145. }
  146. else
  147. {
  148. // For best performance, the same sample rate should be used for input and output,
  149. for (auto& inputSampleRate : inputSampleRates)
  150. {
  151. if (outputSampleRates.contains (inputSampleRate))
  152. result.add (inputSampleRate);
  153. }
  154. }
  155. // either invalid device was requested or its input&output don't have compatible sample rate
  156. jassert (result.size() > 0);
  157. return result;
  158. }
  159. Array<int> getAvailableBufferSizes() override
  160. {
  161. // we need to offer the lowest possible buffer size which
  162. // is the native buffer size
  163. const int defaultNumMultiples = 8;
  164. const int nativeBufferSize = getNativeBufferSize();
  165. Array<int> bufferSizes;
  166. for (int i = 1; i < defaultNumMultiples; ++i)
  167. bufferSizes.add (i * nativeBufferSize);
  168. return bufferSizes;
  169. }
  170. String open (const BigInteger& inputChannels, const BigInteger& outputChannels,
  171. double requestedSampleRate, int bufferSize) override
  172. {
  173. close();
  174. lastError.clear();
  175. sampleRate = (int) requestedSampleRate;
  176. actualBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize;
  177. // The device may report no max, claiming "no limits". Pick sensible defaults.
  178. int maxOutChans = maxNumOutputChannels > 0 ? maxNumOutputChannels : 2;
  179. int maxInChans = maxNumInputChannels > 0 ? maxNumInputChannels : 1;
  180. activeOutputChans = outputChannels;
  181. activeOutputChans.setRange (maxOutChans,
  182. activeOutputChans.getHighestBit() + 1 - maxOutChans,
  183. false);
  184. activeInputChans = inputChannels;
  185. activeInputChans.setRange (maxInChans,
  186. activeInputChans.getHighestBit() + 1 - maxInChans,
  187. false);
  188. int numOutputChans = activeOutputChans.countNumberOfSetBits();
  189. int numInputChans = activeInputChans.countNumberOfSetBits();
  190. if (numInputChans > 0 && (! RuntimePermissions::isGranted (RuntimePermissions::recordAudio)))
  191. {
  192. // If you hit this assert, you probably forgot to get RuntimePermissions::recordAudio
  193. // before trying to open an audio input device. This is not going to work!
  194. jassertfalse;
  195. lastError = "Error opening Oboe input device: the app was not granted android.permission.RECORD_AUDIO";
  196. }
  197. // At least one output channel should be set!
  198. jassert (numOutputChans >= 0);
  199. session.reset (OboeSessionBase::create (*this,
  200. inputDeviceId, outputDeviceId,
  201. numInputChans, numOutputChans,
  202. sampleRate, actualBufferSize));
  203. deviceOpen = session != nullptr;
  204. if (! deviceOpen)
  205. lastError = "Failed to create audio session";
  206. return lastError;
  207. }
  208. void close() override { stop(); }
  209. int getOutputLatencyInSamples() override { return session->getOutputLatencyInSamples(); }
  210. int getInputLatencyInSamples() override { return session->getInputLatencyInSamples(); }
  211. bool isOpen() override { return deviceOpen; }
  212. int getCurrentBufferSizeSamples() override { return actualBufferSize; }
  213. int getCurrentBitDepth() override { return session->getCurrentBitDepth(); }
  214. BigInteger getActiveOutputChannels() const override { return activeOutputChans; }
  215. BigInteger getActiveInputChannels() const override { return activeInputChans; }
  216. String getLastError() override { return lastError; }
  217. bool isPlaying() override { return callback.get() != nullptr; }
  218. int getXRunCount() const noexcept override { return session->getXRunCount(); }
  219. int getDefaultBufferSize() override
  220. {
  221. // Only on a Pro-Audio device will we set the lowest possible buffer size
  222. // by default. We need to be more conservative on other devices
  223. // as they may be low-latency, but still have a crappy CPU.
  224. return (isProAudioDevice() ? 1 : 6)
  225. * defaultBufferSizeIsMultipleOfNative * getNativeBufferSize();
  226. }
  227. double getCurrentSampleRate() override
  228. {
  229. return (sampleRate == 0.0 ? getNativeSampleRate() : sampleRate);
  230. }
  231. void start (AudioIODeviceCallback* newCallback) override
  232. {
  233. if (callback.get() != newCallback)
  234. {
  235. if (newCallback != nullptr)
  236. newCallback->audioDeviceAboutToStart (this);
  237. AudioIODeviceCallback* oldCallback = callback.get();
  238. if (oldCallback != nullptr)
  239. {
  240. // already running
  241. if (newCallback == nullptr)
  242. stop();
  243. else
  244. setCallback (newCallback);
  245. oldCallback->audioDeviceStopped();
  246. }
  247. else
  248. {
  249. jassert (newCallback != nullptr);
  250. // session hasn't started yet
  251. setCallback (newCallback);
  252. running = true;
  253. session->start();
  254. }
  255. callback = newCallback;
  256. }
  257. }
  258. void stop() override
  259. {
  260. if (session != nullptr)
  261. session->stop();
  262. running = false;
  263. setCallback (nullptr);
  264. }
  265. bool setAudioPreprocessingEnabled (bool) override
  266. {
  267. // Oboe does not expose this setting, yet it may use preprocessing
  268. // for older APIs running OpenSL
  269. return false;
  270. }
  271. static const char* const oboeTypeName;
  272. private:
  273. StringArray getChannelNames (bool forInput)
  274. {
  275. auto& deviceId = forInput ? inputDeviceId : outputDeviceId;
  276. auto& numChannels = forInput ? maxNumInputChannels : maxNumOutputChannels;
  277. // If the device id is unknown (on olders APIs) or if the device claims to
  278. // support "any" channel count, use a sensible default
  279. if (deviceId == -1 || numChannels == -1)
  280. return forInput ? StringArray ("Input") : StringArray ("Left", "Right");
  281. StringArray names;
  282. for (int i = 0; i < numChannels; ++i)
  283. names.add ("Channel " + String (i + 1));
  284. return names;
  285. }
  286. Array<int> getAvailableSampleRates (bool forInput)
  287. {
  288. auto& supportedSampleRates = forInput
  289. ? supportedInputSampleRates
  290. : supportedOutputSampleRates;
  291. if (! supportedSampleRates.isEmpty())
  292. return supportedSampleRates;
  293. // device claims that it supports "any" sample rate, use
  294. // standard ones then
  295. return getDefaultSampleRates();
  296. }
  297. static Array<int> getDefaultSampleRates()
  298. {
  299. static const int standardRates[] = { 8000, 11025, 12000, 16000,
  300. 22050, 24000, 32000, 44100, 48000 };
  301. Array<int> rates (standardRates, numElementsInArray (standardRates));
  302. // make sure the native sample rate is part of the list
  303. int native = (int) getNativeSampleRate();
  304. if (native != 0 && ! rates.contains (native))
  305. rates.add (native);
  306. return rates;
  307. }
  308. void setCallback (AudioIODeviceCallback* callbackToUse)
  309. {
  310. if (! running)
  311. {
  312. callback.set (callbackToUse);
  313. return;
  314. }
  315. // Setting nullptr callback is allowed only when playback is stopped.
  316. jassert (callbackToUse != nullptr);
  317. while (true)
  318. {
  319. AudioIODeviceCallback* old = callback.get();
  320. if (old == callbackToUse)
  321. break;
  322. // If old is nullptr, then it means that it's currently being used!
  323. if (old != nullptr && callback.compareAndSetBool (callbackToUse, old))
  324. break;
  325. Thread::sleep (1);
  326. }
  327. }
  328. void process (const float** inputChannelData, int numInputChannels,
  329. float** outputChannelData, int numOutputChannels, int32_t numFrames)
  330. {
  331. if (AudioIODeviceCallback* cb = callback.exchange (nullptr))
  332. {
  333. cb->audioDeviceIOCallback (inputChannelData, numInputChannels,
  334. outputChannelData, numOutputChannels, numFrames);
  335. callback.set (cb);
  336. }
  337. else
  338. {
  339. for (int i = 0; i < numOutputChannels; ++i)
  340. zeromem (outputChannelData[i], sizeof (float) * static_cast<size_t> (numFrames));
  341. }
  342. }
  343. //==============================================================================
  344. class OboeStream
  345. {
  346. public:
  347. OboeStream (int deviceId, oboe::Direction direction,
  348. oboe::SharingMode sharingMode,
  349. int channelCount, oboe::AudioFormat format,
  350. int32 sampleRate, int32 bufferSize,
  351. oboe::AudioStreamCallback* callback = nullptr)
  352. {
  353. open (deviceId, direction, sharingMode, channelCount,
  354. format, sampleRate, bufferSize, callback);
  355. }
  356. ~OboeStream()
  357. {
  358. // AudioStreamCallback can only be deleted when stream has been closed
  359. close();
  360. }
  361. bool openedOk() const noexcept
  362. {
  363. return openResult == oboe::Result::OK;
  364. }
  365. void start()
  366. {
  367. jassert (openedOk());
  368. if (openedOk() && stream != nullptr)
  369. {
  370. auto expectedState = oboe::StreamState::Starting;
  371. auto nextState = oboe::StreamState::Started;
  372. int64 timeoutNanos = 1000 * oboe::kNanosPerMillisecond;
  373. auto startResult = stream->requestStart();
  374. JUCE_OBOE_LOG ("Requested Oboe stream start with result: " + String (oboe::convertToText (startResult)));
  375. startResult = stream->waitForStateChange (expectedState, &nextState, timeoutNanos);
  376. JUCE_OBOE_LOG ("Starting Oboe stream with result: " + String (oboe::convertToText (startResult));
  377. + "\nUses AAudio = " + (stream != nullptr ? String ((int) stream->usesAAudio()) : String ("?"))
  378. + "\nDirection = " + (stream != nullptr ? String (oboe::convertToText (stream->getDirection())) : String ("?"))
  379. + "\nSharingMode = " + (stream != nullptr ? String (oboe::convertToText (stream->getSharingMode())) : String ("?"))
  380. + "\nChannelCount = " + (stream != nullptr ? String (stream->getChannelCount()) : String ("?"))
  381. + "\nFormat = " + (stream != nullptr ? String (oboe::convertToText (stream->getFormat())) : String ("?"))
  382. + "\nSampleRate = " + (stream != nullptr ? String (stream->getSampleRate()) : String ("?"))
  383. + "\nBufferSizeInFrames = " + (stream != nullptr ? String (stream->getBufferSizeInFrames()) : String ("?"))
  384. + "\nBufferCapacityInFrames = " + (stream != nullptr ? String (stream->getBufferCapacityInFrames()) : String ("?"))
  385. + "\nFramesPerBurst = " + (stream != nullptr ? String (stream->getFramesPerBurst()) : String ("?"))
  386. + "\nFramesPerCallback = " + (stream != nullptr ? String (stream->getFramesPerCallback()) : String ("?"))
  387. + "\nBytesPerFrame = " + (stream != nullptr ? String (stream->getBytesPerFrame()) : String ("?"))
  388. + "\nBytesPerSample = " + (stream != nullptr ? String (stream->getBytesPerSample()) : String ("?"))
  389. + "\nPerformanceMode = " + String (oboe::convertToText (oboe::PerformanceMode::LowLatency))
  390. + "\ngetDeviceId = " + (stream != nullptr ? String (stream->getDeviceId()) : String ("?")));
  391. }
  392. }
  393. oboe::AudioStream* getNativeStream()
  394. {
  395. jassert (openedOk());
  396. return stream;
  397. }
  398. int getXRunCount() const
  399. {
  400. return stream != nullptr ? stream->getXRunCount() : 0;
  401. }
  402. private:
  403. void open (int deviceId, oboe::Direction direction,
  404. oboe::SharingMode sharingMode,
  405. int channelCount, oboe::AudioFormat format,
  406. int32 sampleRate, int32 bufferSize,
  407. oboe::AudioStreamCallback* callback = nullptr)
  408. {
  409. oboe::AudioStreamBuilder builder;
  410. if (deviceId != -1)
  411. builder.setDeviceId (deviceId);
  412. static int defaultFramesPerBurst = getDefaultFramesPerBurst();
  413. // Note: letting OS to choose the buffer capacity & frames per callback.
  414. builder.setDirection (direction);
  415. builder.setSharingMode (sharingMode);
  416. builder.setChannelCount (channelCount);
  417. builder.setFormat (format);
  418. builder.setSampleRate (sampleRate);
  419. builder.setDefaultFramesPerBurst ((int32) defaultFramesPerBurst);
  420. builder.setPerformanceMode (oboe::PerformanceMode::LowLatency);
  421. builder.setCallback (callback);
  422. JUCE_OBOE_LOG (String ("Preparing Oboe stream with params:")
  423. + "\nAAudio supported = " + String (int (builder.isAAudioSupported()))
  424. + "\nAPI = " + String (oboe::convertToText (builder.getAudioApi()))
  425. + "\nDeviceId = " + String (deviceId)
  426. + "\nDirection = " + String (oboe::convertToText (direction))
  427. + "\nSharingMode = " + String (oboe::convertToText (sharingMode))
  428. + "\nChannelCount = " + String (channelCount)
  429. + "\nFormat = " + String (oboe::convertToText (format))
  430. + "\nSampleRate = " + String (sampleRate)
  431. + "\nBufferSizeInFrames = " + String (bufferSize)
  432. + "\nFramesPerBurst = " + String (defaultFramesPerBurst)
  433. + "\nPerformanceMode = " + String (oboe::convertToText (oboe::PerformanceMode::LowLatency)));
  434. openResult = builder.openStream (&stream);
  435. JUCE_OBOE_LOG ("Building Oboe stream with result: " + String (oboe::convertToText (openResult))
  436. + "\nStream state = " + (stream != nullptr ? String (oboe::convertToText (stream->getState())) : String ("?")));
  437. if (stream != nullptr)
  438. stream->setBufferSizeInFrames (bufferSize);
  439. JUCE_OBOE_LOG (String ("Stream details:")
  440. + "\nUses AAudio = " + (stream != nullptr ? String ((int) stream->usesAAudio()) : String ("?"))
  441. + "\nDeviceId = " + (stream != nullptr ? String (stream->getDeviceId()) : String ("?"))
  442. + "\nDirection = " + (stream != nullptr ? String (oboe::convertToText (stream->getDirection())) : String ("?"))
  443. + "\nSharingMode = " + (stream != nullptr ? String (oboe::convertToText (stream->getSharingMode())) : String ("?"))
  444. + "\nChannelCount = " + (stream != nullptr ? String (stream->getChannelCount()) : String ("?"))
  445. + "\nFormat = " + (stream != nullptr ? String (oboe::convertToText (stream->getFormat())) : String ("?"))
  446. + "\nSampleRate = " + (stream != nullptr ? String (stream->getSampleRate()) : String ("?"))
  447. + "\nBufferSizeInFrames = " + (stream != nullptr ? String (stream->getBufferSizeInFrames()) : String ("?"))
  448. + "\nBufferCapacityInFrames = " + (stream != nullptr ? String (stream->getBufferCapacityInFrames()) : String ("?"))
  449. + "\nFramesPerBurst = " + (stream != nullptr ? String (stream->getFramesPerBurst()) : String ("?"))
  450. + "\nFramesPerCallback = " + (stream != nullptr ? String (stream->getFramesPerCallback()) : String ("?"))
  451. + "\nBytesPerFrame = " + (stream != nullptr ? String (stream->getBytesPerFrame()) : String ("?"))
  452. + "\nBytesPerSample = " + (stream != nullptr ? String (stream->getBytesPerSample()) : String ("?"))
  453. + "\nPerformanceMode = " + String (oboe::convertToText (oboe::PerformanceMode::LowLatency)));
  454. }
  455. void close()
  456. {
  457. if (stream != nullptr)
  458. {
  459. oboe::Result result = stream->close();
  460. JUCE_OBOE_LOG ("Requested Oboe stream close with result: " + String (oboe::convertToText (result)));
  461. }
  462. }
  463. int getDefaultFramesPerBurst() const
  464. {
  465. // NB: this function only works for inbuilt speakers and headphones
  466. auto* env = getEnv();
  467. auto audioManager = LocalRef<jobject> (env->CallObjectMethod (android.activity,
  468. JuceAppActivity.getSystemService,
  469. javaString ("audio").get()));
  470. auto propertyJavaString = javaString ("android.media.property.OUTPUT_FRAMES_PER_BUFFER");
  471. auto framesPerBurstString = LocalRef<jstring> ((jstring) android.activity.callObjectMethod (JuceAppActivity.audioManagerGetProperty,
  472. propertyJavaString.get()));
  473. return framesPerBurstString != 0 ? env->CallStaticIntMethod (JavaInteger, JavaInteger.parseInt, framesPerBurstString.get(), 10) : 192;
  474. }
  475. oboe::AudioStream* stream = nullptr;
  476. oboe::Result openResult;
  477. };
  478. //==============================================================================
  479. class OboeSessionBase : protected oboe::AudioStreamCallback
  480. {
  481. public:
  482. static OboeSessionBase* create (OboeAudioIODevice& owner,
  483. int inputDeviceId, int outputDeviceId,
  484. int numInputChannels, int numOutputChannels,
  485. int sampleRate, int bufferSize);
  486. virtual void start() = 0;
  487. virtual void stop() = 0;
  488. virtual int getOutputLatencyInSamples() = 0;
  489. virtual int getInputLatencyInSamples() = 0;
  490. bool openedOk() const noexcept
  491. {
  492. if (inputStream != nullptr && ! inputStream->openedOk())
  493. return false;
  494. return outputStream != nullptr && outputStream->openedOk();
  495. }
  496. int getCurrentBitDepth() const noexcept { return bitDepth; }
  497. int getXRunCount() const
  498. {
  499. int inputXRunCount = jmax (0, inputStream != nullptr ? inputStream->getXRunCount() : 0);
  500. int outputXRunCount = jmax (0, outputStream != nullptr ? outputStream->getXRunCount() : 0);
  501. return inputXRunCount + outputXRunCount;
  502. }
  503. protected:
  504. OboeSessionBase (OboeAudioIODevice& ownerToUse,
  505. int inputDeviceIdToUse, int outputDeviceIdToUse,
  506. int numInputChannelsToUse, int numOutputChannelsToUse,
  507. int sampleRateToUse, int bufferSizeToUse,
  508. oboe::AudioFormat streamFormatToUse,
  509. int bitDepthToUse)
  510. : owner (ownerToUse),
  511. inputDeviceId (inputDeviceIdToUse),
  512. outputDeviceId (outputDeviceIdToUse),
  513. numInputChannels (numInputChannelsToUse),
  514. numOutputChannels (numOutputChannelsToUse),
  515. sampleRate (sampleRateToUse),
  516. bufferSize (bufferSizeToUse),
  517. streamFormat (streamFormatToUse),
  518. bitDepth (bitDepthToUse),
  519. outputStream (new OboeStream (outputDeviceId,
  520. oboe::Direction::Output,
  521. oboe::SharingMode::Exclusive,
  522. numOutputChannels,
  523. streamFormatToUse,
  524. sampleRateToUse,
  525. bufferSizeToUse,
  526. this))
  527. {
  528. if (numInputChannels > 0)
  529. {
  530. inputStream.reset (new OboeStream (inputDeviceId,
  531. oboe::Direction::Input,
  532. oboe::SharingMode::Exclusive,
  533. numInputChannels,
  534. streamFormatToUse,
  535. sampleRateToUse,
  536. bufferSizeToUse,
  537. nullptr));
  538. if (inputStream->openedOk() && outputStream->openedOk())
  539. {
  540. // Input & output sample rates should match!
  541. jassert (inputStream->getNativeStream()->getSampleRate()
  542. == outputStream->getNativeStream()->getSampleRate());
  543. }
  544. checkStreamSetup (inputStream.get(), inputDeviceId, numInputChannels,
  545. sampleRate, bufferSize, streamFormat);
  546. }
  547. checkStreamSetup (outputStream.get(), outputDeviceId, numOutputChannels,
  548. sampleRate, bufferSize, streamFormat);
  549. }
  550. // Not strictly required as these should not change, but recommended by Google anyway
  551. void checkStreamSetup (OboeStream* stream, int deviceId, int numChannels, int sampleRate,
  552. int bufferSize, oboe::AudioFormat format)
  553. {
  554. auto* nativeStream = stream != nullptr ? stream->getNativeStream() : nullptr;
  555. if (nativeStream != nullptr)
  556. {
  557. ignoreUnused (deviceId, numChannels, sampleRate, bufferSize);
  558. ignoreUnused (streamFormat, bitDepth);
  559. jassert (numChannels = nativeStream->getChannelCount());
  560. jassert (sampleRate == nativeStream->getSampleRate());
  561. jassert (format == nativeStream->getFormat());
  562. if (nativeStream->usesAAudio())
  563. jassert (bufferSize == nativeStream->getBufferSizeInFrames());
  564. }
  565. }
  566. int getBufferCapacityInFrames (bool forInput) const
  567. {
  568. auto& ptr = forInput ? inputStream : outputStream;
  569. if (ptr == nullptr || ! ptr->openedOk())
  570. return 0;
  571. return ptr->getNativeStream()->getBufferCapacityInFrames();
  572. }
  573. OboeAudioIODevice& owner;
  574. int inputDeviceId, outputDeviceId;
  575. int numInputChannels, numOutputChannels;
  576. int sampleRate;
  577. int bufferSize;
  578. oboe::AudioFormat streamFormat;
  579. int bitDepth;
  580. std::unique_ptr<OboeStream> inputStream, outputStream;
  581. };
  582. //==============================================================================
  583. template <typename SampleType>
  584. class OboeSessionImpl : public OboeSessionBase
  585. {
  586. public:
  587. OboeSessionImpl (OboeAudioIODevice& ownerToUse,
  588. int inputDeviceId, int outputDeviceId,
  589. int numInputChannelsToUse, int numOutputChannelsToUse,
  590. int sampleRateToUse, int bufferSizeToUse)
  591. : OboeSessionBase (ownerToUse,
  592. inputDeviceId, outputDeviceId,
  593. numInputChannelsToUse, numOutputChannelsToUse,
  594. sampleRateToUse, bufferSizeToUse,
  595. OboeAudioIODeviceBufferHelpers<SampleType>::oboeAudioFormat(),
  596. OboeAudioIODeviceBufferHelpers<SampleType>::bitDepth()),
  597. inputStreamNativeBuffer (static_cast<size_t> (numInputChannelsToUse * getBufferCapacityInFrames (true))),
  598. inputStreamSampleBuffer (numInputChannels, getBufferCapacityInFrames (true)),
  599. outputStreamSampleBuffer (numOutputChannels, getBufferCapacityInFrames (false))
  600. {
  601. }
  602. void start() override
  603. {
  604. audioCallbackGuard.set (0);
  605. if (inputStream != nullptr)
  606. inputStream->start();
  607. outputStream->start();
  608. checkIsOutputLatencyDetectionSupported();
  609. }
  610. void stop() override
  611. {
  612. while (! audioCallbackGuard.compareAndSetBool (1, 0))
  613. Thread::sleep (1);
  614. inputStream = nullptr;
  615. outputStream = nullptr;
  616. audioCallbackGuard.set (0);
  617. }
  618. int getOutputLatencyInSamples() override { return outputLatency; }
  619. int getInputLatencyInSamples() override { return -1; }
  620. private:
  621. void checkIsOutputLatencyDetectionSupported()
  622. {
  623. if (! openedOk())
  624. {
  625. isOutputLatencyDetectionSupported = false;
  626. return;
  627. }
  628. auto result = outputStream->getNativeStream()->getTimestamp (CLOCK_MONOTONIC, 0, 0);
  629. isOutputLatencyDetectionSupported = result != oboe::Result::ErrorUnimplemented;
  630. }
  631. oboe::DataCallbackResult onAudioReady (oboe::AudioStream* stream, void* audioData, int32_t numFrames) override
  632. {
  633. attachAndroidJNI();
  634. if (audioCallbackGuard.compareAndSetBool (1, 0))
  635. {
  636. if (stream == nullptr)
  637. return oboe::DataCallbackResult::Stop;
  638. // only output stream should be the master stream receiving callbacks
  639. jassert (stream->getDirection() == oboe::Direction::Output && stream == outputStream->getNativeStream());
  640. //-----------------
  641. // Read input from Oboe
  642. inputStreamSampleBuffer.clear();
  643. inputStreamNativeBuffer.calloc (static_cast<size_t> (numInputChannels * bufferSize));
  644. if (inputStream != nullptr)
  645. {
  646. auto* nativeInputStream = inputStream->getNativeStream();
  647. if (nativeInputStream->getFormat() != oboe::AudioFormat::I16 && nativeInputStream->getFormat() != oboe::AudioFormat::Float)
  648. {
  649. JUCE_OBOE_LOG ("Unsupported input stream audio format: " + String (oboe::convertToText (nativeInputStream->getFormat())));
  650. jassertfalse;
  651. return oboe::DataCallbackResult::Continue;
  652. }
  653. auto result = inputStream->getNativeStream()->read (inputStreamNativeBuffer.getData(), numFrames, 0);
  654. if (result)
  655. {
  656. OboeAudioIODeviceBufferHelpers<SampleType>::referAudioBufferDirectlyToOboeIfPossible (inputStreamNativeBuffer.get(),
  657. inputStreamSampleBuffer,
  658. result.value());
  659. OboeAudioIODeviceBufferHelpers<SampleType>::convertFromOboe (inputStreamNativeBuffer.get(), inputStreamSampleBuffer, result.value());
  660. }
  661. else
  662. {
  663. // Failed to read from input stream.
  664. jassertfalse;
  665. }
  666. }
  667. //-----------------
  668. // Setup output buffer
  669. outputStreamSampleBuffer.clear();
  670. OboeAudioIODeviceBufferHelpers<SampleType>::referAudioBufferDirectlyToOboeIfPossible (static_cast<SampleType*> (audioData),
  671. outputStreamSampleBuffer,
  672. numFrames);
  673. //-----------------
  674. // Process
  675. // NB: the number of samples read from the input can potentially differ from numFrames.
  676. owner.process (inputStreamSampleBuffer.getArrayOfReadPointers(), numInputChannels,
  677. outputStreamSampleBuffer.getArrayOfWritePointers(), numOutputChannels,
  678. numFrames);
  679. //-----------------
  680. // Write output to Oboe
  681. OboeAudioIODeviceBufferHelpers<SampleType>::convertToOboe (outputStreamSampleBuffer, static_cast<SampleType*> (audioData), numFrames);
  682. if (isOutputLatencyDetectionSupported)
  683. calculateOutputLatency();
  684. audioCallbackGuard.set (0);
  685. }
  686. return oboe::DataCallbackResult::Continue;
  687. }
  688. void printStreamDebugInfo (oboe::AudioStream* stream)
  689. {
  690. ignoreUnused (stream);
  691. JUCE_OBOE_LOG ("\nUses AAudio = " + (stream != nullptr ? String ((int) stream->usesAAudio()) : String ("?"))
  692. + "\nDirection = " + (stream != nullptr ? String (oboe::convertToText (stream->getDirection())) : String ("?"))
  693. + "\nSharingMode = " + (stream != nullptr ? String (oboe::convertToText (stream->getSharingMode())) : String ("?"))
  694. + "\nChannelCount = " + (stream != nullptr ? String (stream->getChannelCount()) : String ("?"))
  695. + "\nFormat = " + (stream != nullptr ? String (oboe::convertToText (stream->getFormat())) : String ("?"))
  696. + "\nSampleRate = " + (stream != nullptr ? String (stream->getSampleRate()) : String ("?"))
  697. + "\nBufferSizeInFrames = " + (stream != nullptr ? String (stream->getBufferSizeInFrames()) : String ("?"))
  698. + "\nBufferCapacityInFrames = " + (stream != nullptr ? String (stream->getBufferCapacityInFrames()) : String ("?"))
  699. + "\nFramesPerBurst = " + (stream != nullptr ? String (stream->getFramesPerBurst()) : String ("?"))
  700. + "\nFramesPerCallback = " + (stream != nullptr ? String (stream->getFramesPerCallback()) : String ("?"))
  701. + "\nBytesPerFrame = " + (stream != nullptr ? String (stream->getBytesPerFrame()) : String ("?"))
  702. + "\nBytesPerSample = " + (stream != nullptr ? String (stream->getBytesPerSample()) : String ("?"))
  703. + "\nPerformanceMode = " + String (oboe::convertToText (oboe::PerformanceMode::LowLatency))
  704. + "\ngetDeviceId = " + (stream != nullptr ? String (stream->getDeviceId()) : String ("?")));
  705. }
  706. void calculateOutputLatency()
  707. {
  708. // Sadly, Oboe uses non-portable int64_t (a.k.a. long on LP64 and long long on LLP64)
  709. int64_t lastWrittenAndPresentedFrameIndex = 0;
  710. int64_t lastFramePresentationTimeNanos = 0;
  711. auto result = outputStream->getNativeStream()->getTimestamp (CLOCK_MONOTONIC,
  712. &lastWrittenAndPresentedFrameIndex,
  713. &lastFramePresentationTimeNanos);
  714. if (result != oboe::Result::OK)
  715. return;
  716. int64_t currentNumFramesWritten = outputStream->getNativeStream()->getFramesWritten();
  717. int64_t framesDelta = currentNumFramesWritten - lastWrittenAndPresentedFrameIndex;
  718. int64_t timeDeltaNanos = framesDelta * oboe::kNanosPerSecond / sampleRate;
  719. int64_t nextPresentationTimeNanos = lastFramePresentationTimeNanos + timeDeltaNanos;
  720. int64_t nextFrameWriteTimeNanos = getCurrentTimeNanos();
  721. if (nextFrameWriteTimeNanos < 0)
  722. return;
  723. outputLatency = (int) ((nextPresentationTimeNanos - nextFrameWriteTimeNanos) * sampleRate / oboe::kNanosPerSecond);
  724. }
  725. int64_t getCurrentTimeNanos()
  726. {
  727. timespec time;
  728. if (clock_gettime (CLOCK_MONOTONIC, &time) < 0)
  729. return -1;
  730. return time.tv_sec * oboe::kNanosPerSecond + time.tv_nsec;
  731. }
  732. void onErrorBeforeClose (oboe::AudioStream* stream, oboe::Result error) override
  733. {
  734. // only output stream should be the master stream receiving callbacks
  735. jassert (stream->getDirection() == oboe::Direction::Output);
  736. JUCE_OBOE_LOG ("Oboe stream onErrorBeforeClose(): " + String (oboe::convertToText (error)));
  737. printStreamDebugInfo (stream);
  738. }
  739. void onErrorAfterClose (oboe::AudioStream* stream, oboe::Result error) override
  740. {
  741. // only output stream should be the master stream receiving callbacks
  742. jassert (stream->getDirection() == oboe::Direction::Output);
  743. JUCE_OBOE_LOG ("Oboe stream onErrorAfterClose(): " + String (oboe::convertToText (error)));
  744. if (error == oboe::Result::ErrorDisconnected)
  745. {
  746. if (streamRestartGuard.compareAndSetBool (1, 0))
  747. {
  748. // Close, recreate, and start the stream, not much use in current one.
  749. // Use default device id, to let the OS pick the best ID (since our was disconnected).
  750. while (! audioCallbackGuard.compareAndSetBool (1, 0))
  751. Thread::sleep (1);
  752. outputStream = nullptr;
  753. outputStream.reset (new OboeStream (-1,
  754. oboe::Direction::Output,
  755. oboe::SharingMode::Exclusive,
  756. numOutputChannels,
  757. streamFormat,
  758. sampleRate,
  759. bufferSize,
  760. this));
  761. outputStream->start();
  762. audioCallbackGuard.set (0);
  763. streamRestartGuard.set (0);
  764. }
  765. }
  766. }
  767. HeapBlock<SampleType> inputStreamNativeBuffer;
  768. AudioBuffer<float> inputStreamSampleBuffer,
  769. outputStreamSampleBuffer;
  770. Atomic<int> audioCallbackGuard { 0 },
  771. streamRestartGuard { 0 };
  772. bool isOutputLatencyDetectionSupported = true;
  773. int outputLatency = -1;
  774. };
  775. //==============================================================================
  776. friend class OboeAudioIODeviceType;
  777. friend class OboeRealtimeThread;
  778. //==============================================================================
  779. int actualBufferSize = 0, sampleRate = 0;
  780. bool deviceOpen = false;
  781. String lastError;
  782. BigInteger activeOutputChans, activeInputChans;
  783. Atomic<AudioIODeviceCallback*> callback { nullptr };
  784. int inputDeviceId;
  785. Array<int> supportedInputSampleRates;
  786. int maxNumInputChannels;
  787. int outputDeviceId;
  788. Array<int> supportedOutputSampleRates;
  789. int maxNumOutputChannels;
  790. std::unique_ptr<OboeSessionBase> session;
  791. bool running = false;
  792. enum
  793. {
  794. // These at the moment correspond to OpenSL settings.
  795. bufferSizeMultForLowLatency = 4,
  796. bufferSizeMultForSlowAudio = 8,
  797. defaultBufferSizeIsMultipleOfNative = 1
  798. };
  799. //==============================================================================
  800. static String audioManagerGetProperty (const String& property)
  801. {
  802. const LocalRef<jstring> jProperty (javaString (property));
  803. const LocalRef<jstring> text ((jstring) android.activity.callObjectMethod (JuceAppActivity.audioManagerGetProperty,
  804. jProperty.get()));
  805. if (text.get() != 0)
  806. return juceString (text);
  807. return {};
  808. }
  809. static bool androidHasSystemFeature (const String& property)
  810. {
  811. const LocalRef<jstring> jProperty (javaString (property));
  812. return android.activity.callBooleanMethod (JuceAppActivity.hasSystemFeature, jProperty.get());
  813. }
  814. static double getNativeSampleRate()
  815. {
  816. return audioManagerGetProperty ("android.media.property.OUTPUT_SAMPLE_RATE").getDoubleValue();
  817. }
  818. static int getNativeBufferSize()
  819. {
  820. auto val = audioManagerGetProperty ("android.media.property.OUTPUT_FRAMES_PER_BUFFER").getIntValue();
  821. return val > 0 ? val : 512;
  822. }
  823. static bool isProAudioDevice()
  824. {
  825. return androidHasSystemFeature ("android.hardware.audio.pro");
  826. }
  827. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OboeAudioIODevice)
  828. };
  829. //==============================================================================
  830. OboeAudioIODevice::OboeSessionBase* OboeAudioIODevice::OboeSessionBase::create (OboeAudioIODevice& owner,
  831. int inputDeviceId,
  832. int outputDeviceId,
  833. int numInputChannels,
  834. int numOutputChannels,
  835. int sampleRate,
  836. int bufferSize)
  837. {
  838. std::unique_ptr<OboeSessionBase> session;
  839. auto sdkVersion = getEnv()->GetStaticIntField (AndroidBuildVersion, AndroidBuildVersion.SDK_INT);
  840. // SDK versions 21 and higher should natively support floating point...
  841. if (sdkVersion >= 21)
  842. {
  843. session.reset (new OboeSessionImpl<float> (owner, inputDeviceId, outputDeviceId,
  844. numInputChannels, numOutputChannels, sampleRate, bufferSize));
  845. // ...however, some devices lie so re-try without floating point
  846. if (session != nullptr && (! session->openedOk()))
  847. session.reset();
  848. }
  849. if (session == nullptr)
  850. {
  851. session.reset (new OboeSessionImpl<int16> (owner, inputDeviceId, outputDeviceId,
  852. numInputChannels, numOutputChannels, sampleRate, bufferSize));
  853. if (session != nullptr && (! session->openedOk()))
  854. session.reset();
  855. }
  856. return session.release();
  857. }
  858. //==============================================================================
  859. class OboeAudioIODeviceType : public AudioIODeviceType
  860. {
  861. public:
  862. OboeAudioIODeviceType()
  863. : AudioIODeviceType (OboeAudioIODevice::oboeTypeName)
  864. {
  865. // Not using scanForDevices() to maintain behaviour backwards compatible with older APIs
  866. checkAvailableDevices();
  867. }
  868. //==============================================================================
  869. void scanForDevices() override {}
  870. StringArray getDeviceNames (bool wantInputNames) const override
  871. {
  872. if (inputDevices.isEmpty() && outputDevices.isEmpty())
  873. return StringArray (OboeAudioIODevice::oboeTypeName);
  874. StringArray names;
  875. for (auto& device : wantInputNames ? inputDevices : outputDevices)
  876. names.add (device.name);
  877. return names;
  878. }
  879. int getDefaultDeviceIndex (bool forInput) const override
  880. {
  881. // No need to create a stream when only one default device is created.
  882. if (! supportsDevicesInfo())
  883. return 0;
  884. if (forInput && (! RuntimePermissions::isGranted (RuntimePermissions::recordAudio)))
  885. return 0;
  886. // Create stream with a default device ID and query the stream for its device ID
  887. using OboeStream = OboeAudioIODevice::OboeStream;
  888. OboeStream tempStream (-1,
  889. forInput ? oboe::Direction::Input : oboe::Direction::Output,
  890. oboe::SharingMode::Shared,
  891. forInput ? 1 : 2,
  892. getSdkVersion() >= 21 ? oboe::AudioFormat::Float : oboe::AudioFormat::I16,
  893. (int) OboeAudioIODevice::getNativeSampleRate(),
  894. OboeAudioIODevice::getNativeBufferSize(),
  895. nullptr);
  896. if (auto* nativeStream = tempStream.getNativeStream())
  897. {
  898. auto& devices = forInput ? inputDevices : outputDevices;
  899. for (int i = 0; i < devices.size(); ++i)
  900. if (devices.getReference (i).id == nativeStream->getDeviceId())
  901. return i;
  902. }
  903. return 0;
  904. }
  905. int getIndexOfDevice (AudioIODevice* device, bool asInput) const override
  906. {
  907. if (device == nullptr)
  908. return -1;
  909. auto* oboeDevice = static_cast<OboeAudioIODevice*> (device);
  910. auto oboeDeviceId = asInput ? oboeDevice->inputDeviceId
  911. : oboeDevice->outputDeviceId;
  912. auto& devices = asInput ? inputDevices : outputDevices;
  913. for (int i = 0; i < devices.size(); ++i)
  914. if (devices.getReference (i).id == oboeDeviceId)
  915. return i;
  916. return -1;
  917. }
  918. bool hasSeparateInputsAndOutputs() const override { return true; }
  919. AudioIODevice* createDevice (const String& outputDeviceName,
  920. const String& inputDeviceName) override
  921. {
  922. auto outputDeviceInfo = getDeviceInfoForName (outputDeviceName, false);
  923. auto inputDeviceInfo = getDeviceInfoForName (inputDeviceName, true);
  924. if (outputDeviceInfo.name.isEmpty() && inputDeviceInfo.name.isEmpty())
  925. {
  926. // Invalid device name passed. It must be one of the names returned by getDeviceNames().
  927. jassertfalse;
  928. return nullptr;
  929. }
  930. auto& name = outputDeviceInfo.name.isNotEmpty() ? outputDeviceInfo.name
  931. : inputDeviceInfo.name;
  932. return new OboeAudioIODevice (name,
  933. inputDeviceInfo.id, inputDeviceInfo.sampleRates,
  934. inputDeviceInfo.numChannels,
  935. outputDeviceInfo.id, outputDeviceInfo.sampleRates,
  936. outputDeviceInfo.numChannels);
  937. }
  938. static bool isOboeAvailable()
  939. {
  940. #if JUCE_USE_ANDROID_OBOE
  941. return true;
  942. #else
  943. return false;
  944. #endif
  945. }
  946. private:
  947. void checkAvailableDevices()
  948. {
  949. if (! supportsDevicesInfo())
  950. {
  951. auto sampleRates = OboeAudioIODevice::getDefaultSampleRates();
  952. inputDevices .add ({ OboeAudioIODevice::oboeTypeName, -1, sampleRates, 1 });
  953. outputDevices.add ({ OboeAudioIODevice::oboeTypeName, -1, sampleRates, 2 });
  954. return;
  955. }
  956. auto* env = getEnv();
  957. jclass audioManagerClass = env->FindClass ("android/media/AudioManager");
  958. // We should be really entering here only if API supports it.
  959. jassert (audioManagerClass != 0);
  960. if (audioManagerClass == 0)
  961. return;
  962. auto audioManager = LocalRef<jobject> (env->CallObjectMethod (android.activity,
  963. JuceAppActivity.getSystemService,
  964. javaString ("audio").get()));
  965. static jmethodID getDevicesMethod = env->GetMethodID (audioManagerClass, "getDevices",
  966. "(I)[Landroid/media/AudioDeviceInfo;");
  967. static constexpr int allDevices = 3;
  968. auto devices = LocalRef<jobjectArray> ((jobjectArray) env->CallObjectMethod (audioManager,
  969. getDevicesMethod,
  970. allDevices));
  971. const int numDevices = env->GetArrayLength (devices.get());
  972. for (int i = 0; i < numDevices; ++i)
  973. {
  974. auto device = LocalRef<jobject> ((jobject) env->GetObjectArrayElement (devices.get(), i));
  975. addDevice (device, env);
  976. }
  977. JUCE_OBOE_LOG ("-----InputDevices:");
  978. for (auto& device : inputDevices)
  979. {
  980. JUCE_OBOE_LOG ("name = " << device.name);
  981. JUCE_OBOE_LOG ("id = " << String (device.id));
  982. JUCE_OBOE_LOG ("sample rates size = " << String (device.sampleRates.size()));
  983. JUCE_OBOE_LOG ("num channels = " + String (device.numChannels));
  984. }
  985. JUCE_OBOE_LOG ("-----OutputDevices:");
  986. for (auto& device : outputDevices)
  987. {
  988. JUCE_OBOE_LOG ("name = " << device.name);
  989. JUCE_OBOE_LOG ("id = " << String (device.id));
  990. JUCE_OBOE_LOG ("sample rates size = " << String (device.sampleRates.size()));
  991. JUCE_OBOE_LOG ("num channels = " + String (device.numChannels));
  992. }
  993. }
  994. bool supportsDevicesInfo() const
  995. {
  996. static auto result = getSdkVersion() >= 23;
  997. return result;
  998. }
  999. int getSdkVersion() const
  1000. {
  1001. static auto sdkVersion = getEnv()->GetStaticIntField (AndroidBuildVersion, AndroidBuildVersion.SDK_INT);
  1002. return sdkVersion;
  1003. }
  1004. void addDevice (const LocalRef<jobject>& device, JNIEnv* env)
  1005. {
  1006. auto deviceClass = LocalRef<jclass> ((jclass) env->FindClass ("android/media/AudioDeviceInfo"));
  1007. jmethodID getProductNameMethod = env->GetMethodID (deviceClass, "getProductName",
  1008. "()Ljava/lang/CharSequence;");
  1009. jmethodID getTypeMethod = env->GetMethodID (deviceClass, "getType", "()I");
  1010. jmethodID getIdMethod = env->GetMethodID (deviceClass, "getId", "()I");
  1011. jmethodID getSampleRatesMethod = env->GetMethodID (deviceClass, "getSampleRates", "()[I");
  1012. jmethodID getChannelCountsMethod = env->GetMethodID (deviceClass, "getChannelCounts", "()[I");
  1013. jmethodID isSourceMethod = env->GetMethodID (deviceClass, "isSource", "()Z");
  1014. auto name = juceString ((jstring) env->CallObjectMethod (device, getProductNameMethod));
  1015. name << deviceTypeToString (env->CallIntMethod (device, getTypeMethod));
  1016. int id = env->CallIntMethod (device, getIdMethod);
  1017. auto jSampleRates = LocalRef<jintArray> ((jintArray) env->CallObjectMethod (device, getSampleRatesMethod));
  1018. auto sampleRates = jintArrayToJuceArray (jSampleRates);
  1019. auto jChannelCounts = LocalRef<jintArray> ((jintArray) env->CallObjectMethod (device, getChannelCountsMethod));
  1020. auto channelCounts = jintArrayToJuceArray (jChannelCounts);
  1021. int numChannels = channelCounts.isEmpty() ? -1 : channelCounts.getLast();
  1022. bool isInput = env->CallBooleanMethod (device, isSourceMethod);
  1023. auto& devices = isInput ? inputDevices : outputDevices;
  1024. devices.add ({ name, id, sampleRates, numChannels });
  1025. }
  1026. static const char* deviceTypeToString (int type)
  1027. {
  1028. switch (type)
  1029. {
  1030. case 0: return "";
  1031. case 1: return " built-in earphone speaker";
  1032. case 2: return " built-in speaker";
  1033. case 3: return " wired headset";
  1034. case 4: return " wired headphones";
  1035. case 5: return " line analog";
  1036. case 6: return " line digital";
  1037. case 7: return " Bluetooth device typically used for telephony";
  1038. case 8: return " Bluetooth device supporting the A2DP profile";
  1039. case 9: return " HDMI";
  1040. case 10: return " HDMI audio return channel";
  1041. case 11: return " USB device";
  1042. case 12: return " USB accessory";
  1043. case 13: return " DOCK";
  1044. case 14: return " FM";
  1045. case 15: return " built-in microphone";
  1046. case 16: return " FM tuner";
  1047. case 17: return " TV tuner";
  1048. case 18: return " telephony";
  1049. case 19: return " auxiliary line-level connectors";
  1050. case 20: return " IP";
  1051. case 21: return " BUS";
  1052. case 22: return " USB headset";
  1053. default: jassertfalse; return ""; // type not supported yet, needs to be added!
  1054. }
  1055. }
  1056. static Array<int> jintArrayToJuceArray (const LocalRef<jintArray>& jArray)
  1057. {
  1058. auto* env = getEnv();
  1059. jint* jArrayElems = env->GetIntArrayElements (jArray, 0);
  1060. int numElems = env->GetArrayLength (jArray);
  1061. Array<int> juceArray;
  1062. for (int s = 0; s < numElems; ++s)
  1063. juceArray.add (jArrayElems[s]);
  1064. env->ReleaseIntArrayElements (jArray, jArrayElems, 0);
  1065. return juceArray;
  1066. }
  1067. struct DeviceInfo
  1068. {
  1069. String name;
  1070. int id;
  1071. Array<int> sampleRates;
  1072. int numChannels;
  1073. };
  1074. DeviceInfo getDeviceInfoForName (const String& name, bool isInput)
  1075. {
  1076. if (name.isEmpty())
  1077. return {};
  1078. for (auto& device : isInput ? inputDevices : outputDevices)
  1079. if (device.name == name)
  1080. return device;
  1081. return {};
  1082. }
  1083. Array<DeviceInfo> inputDevices, outputDevices;
  1084. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OboeAudioIODeviceType)
  1085. };
  1086. const char* const OboeAudioIODevice::oboeTypeName = "Android Oboe";
  1087. //==============================================================================
  1088. bool isOboeAvailable() { return OboeAudioIODeviceType::isOboeAvailable(); }
  1089. AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Oboe()
  1090. {
  1091. return isOboeAvailable() ? new OboeAudioIODeviceType() : nullptr;
  1092. }
  1093. //==============================================================================
  1094. class OboeRealtimeThread : private oboe::AudioStreamCallback
  1095. {
  1096. using OboeStream = OboeAudioIODevice::OboeStream;
  1097. public:
  1098. OboeRealtimeThread()
  1099. : testStream (new OboeStream (-1,
  1100. oboe::Direction::Output,
  1101. oboe::SharingMode::Exclusive,
  1102. 1,
  1103. oboe::AudioFormat::Float,
  1104. (int) OboeAudioIODevice::getNativeSampleRate(),
  1105. OboeAudioIODevice::getNativeBufferSize(),
  1106. this)),
  1107. formatUsed (oboe::AudioFormat::Float)
  1108. {
  1109. // Fallback to I16 stream format if Float has not worked
  1110. if (! testStream->openedOk())
  1111. {
  1112. testStream.reset (new OboeStream (-1,
  1113. oboe::Direction::Output,
  1114. oboe::SharingMode::Exclusive,
  1115. 1,
  1116. oboe::AudioFormat::I16,
  1117. (int) OboeAudioIODevice::getNativeSampleRate(),
  1118. OboeAudioIODevice::getNativeBufferSize(),
  1119. this));
  1120. formatUsed = oboe::AudioFormat::I16;
  1121. }
  1122. parentThreadID = pthread_self();
  1123. pthread_cond_init (&threadReady, nullptr);
  1124. pthread_mutex_init (&threadReadyMutex, nullptr);
  1125. }
  1126. bool isOk() const
  1127. {
  1128. return testStream != nullptr && testStream->openedOk();
  1129. }
  1130. pthread_t startThread (void* (*entry) (void*), void* userPtr)
  1131. {
  1132. pthread_mutex_lock (&threadReadyMutex);
  1133. threadEntryProc = entry;
  1134. threadUserPtr = userPtr;
  1135. testStream->start();
  1136. pthread_cond_wait (&threadReady, &threadReadyMutex);
  1137. pthread_mutex_unlock (&threadReadyMutex);
  1138. return realtimeThreadID;
  1139. }
  1140. oboe::DataCallbackResult onAudioReady (oboe::AudioStream*, void*, int32_t) override
  1141. {
  1142. // When running with OpenSL, the first callback will come on the parent thread.
  1143. if (threadEntryProc != nullptr && ! pthread_equal (parentThreadID, pthread_self()))
  1144. {
  1145. pthread_mutex_lock (&threadReadyMutex);
  1146. realtimeThreadID = pthread_self();
  1147. pthread_cond_signal (&threadReady);
  1148. pthread_mutex_unlock (&threadReadyMutex);
  1149. threadEntryProc (threadUserPtr);
  1150. threadEntryProc = nullptr;
  1151. MessageManager::callAsync ([this] () { delete this; });
  1152. return oboe::DataCallbackResult::Stop;
  1153. }
  1154. return oboe::DataCallbackResult::Continue;
  1155. }
  1156. void onErrorBeforeClose (oboe::AudioStream*, oboe::Result error) override
  1157. {
  1158. JUCE_OBOE_LOG ("OboeRealtimeThread: Oboe stream onErrorBeforeClose(): " + String (oboe::convertToText (error)));
  1159. }
  1160. void onErrorAfterClose (oboe::AudioStream* stream, oboe::Result error) override
  1161. {
  1162. JUCE_OBOE_LOG ("OboeRealtimeThread: Oboe stream onErrorAfterClose(): " + String (oboe::convertToText (error)));
  1163. if (error == oboe::Result::ErrorDisconnected)
  1164. {
  1165. testStream.reset();
  1166. testStream.reset (new OboeStream (-1,
  1167. oboe::Direction::Output,
  1168. oboe::SharingMode::Exclusive,
  1169. 1,
  1170. formatUsed,
  1171. (int) OboeAudioIODevice::getNativeSampleRate(),
  1172. OboeAudioIODevice::getNativeBufferSize(),
  1173. this));
  1174. testStream->start();
  1175. }
  1176. }
  1177. private:
  1178. //=============================================================================
  1179. void* (*threadEntryProc) (void*) = nullptr;
  1180. void* threadUserPtr = nullptr;
  1181. pthread_cond_t threadReady;
  1182. pthread_mutex_t threadReadyMutex;
  1183. pthread_t parentThreadID, realtimeThreadID;
  1184. std::unique_ptr<OboeStream> testStream;
  1185. oboe::AudioFormat formatUsed;
  1186. };
  1187. pthread_t juce_createRealtimeAudioThread (void* (*entry) (void*), void* userPtr)
  1188. {
  1189. std::unique_ptr<OboeRealtimeThread> thread (new OboeRealtimeThread());
  1190. if (! thread->isOk())
  1191. return {};
  1192. auto threadID = thread->startThread (entry, userPtr);
  1193. // the thread will de-allocate itself
  1194. thread.release();
  1195. return threadID;
  1196. }
  1197. } // namespace juce