Audio plugin host https://kx.studio/carla
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.

623 lines
24KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. const char* const openSLTypeName = "Android OpenSL";
  18. bool isOpenSLAvailable()
  19. {
  20. DynamicLibrary library;
  21. return library.open ("libOpenSLES.so");
  22. }
  23. const unsigned short openSLRates[] = { 8000, 16000, 32000, 44100, 48000 };
  24. const unsigned short openSLBufferSizes[] = { 256, 512, 768, 1024, 1280, 1600 }; // must all be multiples of the block size
  25. //==============================================================================
  26. class OpenSLAudioIODevice : public AudioIODevice,
  27. public Thread
  28. {
  29. public:
  30. OpenSLAudioIODevice (const String& deviceName)
  31. : AudioIODevice (deviceName, openSLTypeName),
  32. Thread ("OpenSL"),
  33. callback (nullptr), sampleRate (0), deviceOpen (false),
  34. inputBuffer (2, 2), outputBuffer (2, 2)
  35. {
  36. // OpenSL has piss-poor support for determining latency, so the only way I can find to
  37. // get a number for this is by asking the AudioTrack/AudioRecord classes..
  38. AndroidAudioIODevice javaDevice (String::empty);
  39. // this is a total guess about how to calculate the latency, but seems to vaguely agree
  40. // with the devices I've tested.. YMMV
  41. inputLatency = ((javaDevice.minBufferSizeIn * 2) / 3);
  42. outputLatency = ((javaDevice.minBufferSizeOut * 2) / 3);
  43. const int longestLatency = jmax (inputLatency, outputLatency);
  44. const int totalLatency = inputLatency + outputLatency;
  45. inputLatency = ((longestLatency * inputLatency) / totalLatency) & ~15;
  46. outputLatency = ((longestLatency * outputLatency) / totalLatency) & ~15;
  47. }
  48. ~OpenSLAudioIODevice()
  49. {
  50. close();
  51. }
  52. bool openedOk() const { return engine.outputMixObject != nullptr; }
  53. StringArray getOutputChannelNames()
  54. {
  55. StringArray s;
  56. s.add ("Left");
  57. s.add ("Right");
  58. return s;
  59. }
  60. StringArray getInputChannelNames()
  61. {
  62. StringArray s;
  63. s.add ("Audio Input");
  64. return s;
  65. }
  66. int getNumSampleRates() { return numElementsInArray (openSLRates); }
  67. double getSampleRate (int index)
  68. {
  69. jassert (index >= 0 && index < getNumSampleRates());
  70. return (int) openSLRates [index];
  71. }
  72. int getDefaultBufferSize() { return 1024; }
  73. int getNumBufferSizesAvailable() { return numElementsInArray (openSLBufferSizes); }
  74. int getBufferSizeSamples (int index)
  75. {
  76. jassert (index >= 0 && index < getNumBufferSizesAvailable());
  77. return (int) openSLBufferSizes [index];
  78. }
  79. String open (const BigInteger& inputChannels,
  80. const BigInteger& outputChannels,
  81. double requestedSampleRate,
  82. int bufferSize)
  83. {
  84. close();
  85. lastError = String::empty;
  86. sampleRate = (int) requestedSampleRate;
  87. int preferredBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize;
  88. activeOutputChans = outputChannels;
  89. activeOutputChans.setRange (2, activeOutputChans.getHighestBit(), false);
  90. numOutputChannels = activeOutputChans.countNumberOfSetBits();
  91. activeInputChans = inputChannels;
  92. activeInputChans.setRange (1, activeInputChans.getHighestBit(), false);
  93. numInputChannels = activeInputChans.countNumberOfSetBits();
  94. actualBufferSize = preferredBufferSize;
  95. inputBuffer.setSize (jmax (1, numInputChannels), actualBufferSize);
  96. outputBuffer.setSize (jmax (1, numOutputChannels), actualBufferSize);
  97. outputBuffer.clear();
  98. recorder = engine.createRecorder (numInputChannels, sampleRate);
  99. player = engine.createPlayer (numOutputChannels, sampleRate);
  100. startThread (8);
  101. deviceOpen = true;
  102. return lastError;
  103. }
  104. void close()
  105. {
  106. stop();
  107. stopThread (6000);
  108. deviceOpen = false;
  109. recorder = nullptr;
  110. player = nullptr;
  111. }
  112. int getOutputLatencyInSamples() { return outputLatency; }
  113. int getInputLatencyInSamples() { return inputLatency; }
  114. bool isOpen() { return deviceOpen; }
  115. int getCurrentBufferSizeSamples() { return actualBufferSize; }
  116. int getCurrentBitDepth() { return 16; }
  117. double getCurrentSampleRate() { return sampleRate; }
  118. BigInteger getActiveOutputChannels() const { return activeOutputChans; }
  119. BigInteger getActiveInputChannels() const { return activeInputChans; }
  120. String getLastError() { return lastError; }
  121. bool isPlaying() { return callback != nullptr; }
  122. void start (AudioIODeviceCallback* newCallback)
  123. {
  124. stop();
  125. if (deviceOpen && callback != newCallback)
  126. {
  127. if (newCallback != nullptr)
  128. newCallback->audioDeviceAboutToStart (this);
  129. setCallback (newCallback);
  130. }
  131. }
  132. void stop()
  133. {
  134. if (AudioIODeviceCallback* const oldCallback = setCallback (nullptr))
  135. oldCallback->audioDeviceStopped();
  136. }
  137. void run() override
  138. {
  139. if (recorder != nullptr) recorder->start();
  140. if (player != nullptr) player->start();
  141. while (! threadShouldExit())
  142. {
  143. if (player != nullptr) player->writeBuffer (outputBuffer, *this);
  144. if (recorder != nullptr) recorder->readNextBlock (inputBuffer, *this);
  145. const ScopedLock sl (callbackLock);
  146. if (callback != nullptr)
  147. {
  148. callback->audioDeviceIOCallback (numInputChannels > 0 ? (const float**) inputBuffer.getArrayOfChannels() : nullptr,
  149. numInputChannels,
  150. numOutputChannels > 0 ? outputBuffer.getArrayOfChannels() : nullptr,
  151. numOutputChannels,
  152. actualBufferSize);
  153. }
  154. else
  155. {
  156. outputBuffer.clear();
  157. }
  158. }
  159. }
  160. private:
  161. //==================================================================================================
  162. CriticalSection callbackLock;
  163. AudioIODeviceCallback* callback;
  164. int actualBufferSize, sampleRate;
  165. int inputLatency, outputLatency;
  166. bool deviceOpen;
  167. String lastError;
  168. BigInteger activeOutputChans, activeInputChans;
  169. int numInputChannels, numOutputChannels;
  170. AudioSampleBuffer inputBuffer, outputBuffer;
  171. struct Player;
  172. struct Recorder;
  173. AudioIODeviceCallback* setCallback (AudioIODeviceCallback* const newCallback)
  174. {
  175. const ScopedLock sl (callbackLock);
  176. AudioIODeviceCallback* const oldCallback = callback;
  177. callback = newCallback;
  178. return oldCallback;
  179. }
  180. //==================================================================================================
  181. struct Engine
  182. {
  183. Engine()
  184. : engineObject (nullptr), engineInterface (nullptr), outputMixObject (nullptr)
  185. {
  186. if (library.open ("libOpenSLES.so"))
  187. {
  188. typedef SLresult (*CreateEngineFunc) (SLObjectItf*, SLuint32, const SLEngineOption*, SLuint32, const SLInterfaceID*, const SLboolean*);
  189. if (CreateEngineFunc createEngine = (CreateEngineFunc) library.getFunction ("slCreateEngine"))
  190. {
  191. check (createEngine (&engineObject, 0, nullptr, 0, nullptr, nullptr));
  192. SLInterfaceID* SL_IID_ENGINE = (SLInterfaceID*) library.getFunction ("SL_IID_ENGINE");
  193. SL_IID_ANDROIDSIMPLEBUFFERQUEUE = (SLInterfaceID*) library.getFunction ("SL_IID_ANDROIDSIMPLEBUFFERQUEUE");
  194. SL_IID_PLAY = (SLInterfaceID*) library.getFunction ("SL_IID_PLAY");
  195. SL_IID_RECORD = (SLInterfaceID*) library.getFunction ("SL_IID_RECORD");
  196. check ((*engineObject)->Realize (engineObject, SL_BOOLEAN_FALSE));
  197. check ((*engineObject)->GetInterface (engineObject, *SL_IID_ENGINE, &engineInterface));
  198. check ((*engineInterface)->CreateOutputMix (engineInterface, &outputMixObject, 0, nullptr, nullptr));
  199. check ((*outputMixObject)->Realize (outputMixObject, SL_BOOLEAN_FALSE));
  200. }
  201. }
  202. }
  203. ~Engine()
  204. {
  205. if (outputMixObject != nullptr) (*outputMixObject)->Destroy (outputMixObject);
  206. if (engineObject != nullptr) (*engineObject)->Destroy (engineObject);
  207. }
  208. Player* createPlayer (const int numChannels, const int sampleRate)
  209. {
  210. if (numChannels <= 0)
  211. return nullptr;
  212. ScopedPointer<Player> player (new Player (numChannels, sampleRate, *this));
  213. return player->openedOk() ? player.release() : nullptr;
  214. }
  215. Recorder* createRecorder (const int numChannels, const int sampleRate)
  216. {
  217. if (numChannels <= 0)
  218. return nullptr;
  219. ScopedPointer<Recorder> recorder (new Recorder (numChannels, sampleRate, *this));
  220. return recorder->openedOk() ? recorder.release() : nullptr;
  221. }
  222. SLObjectItf engineObject;
  223. SLEngineItf engineInterface;
  224. SLObjectItf outputMixObject;
  225. SLInterfaceID* SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
  226. SLInterfaceID* SL_IID_PLAY;
  227. SLInterfaceID* SL_IID_RECORD;
  228. private:
  229. DynamicLibrary library;
  230. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Engine)
  231. };
  232. //==================================================================================================
  233. struct BufferList
  234. {
  235. BufferList (const int numChannels_)
  236. : numChannels (numChannels_), bufferSpace (numChannels_ * numSamples * numBuffers), nextBlock (0)
  237. {
  238. }
  239. int16* waitForFreeBuffer (Thread& threadToCheck)
  240. {
  241. while (numBlocksOut.get() == numBuffers)
  242. {
  243. dataArrived.wait (1);
  244. if (threadToCheck.threadShouldExit())
  245. return nullptr;
  246. }
  247. return getNextBuffer();
  248. }
  249. int16* getNextBuffer()
  250. {
  251. if (++nextBlock == numBuffers)
  252. nextBlock = 0;
  253. return bufferSpace + nextBlock * numChannels * numSamples;
  254. }
  255. void bufferReturned() { --numBlocksOut; dataArrived.signal(); }
  256. void bufferSent() { ++numBlocksOut; dataArrived.signal(); }
  257. int getBufferSizeBytes() const { return numChannels * numSamples * sizeof (int16); }
  258. const int numChannels;
  259. enum { numSamples = 256, numBuffers = 16 };
  260. private:
  261. HeapBlock<int16> bufferSpace;
  262. int nextBlock;
  263. Atomic<int> numBlocksOut;
  264. WaitableEvent dataArrived;
  265. };
  266. //==================================================================================================
  267. struct Player
  268. {
  269. Player (int numChannels, int sampleRate, Engine& engine)
  270. : playerObject (nullptr), playerPlay (nullptr), playerBufferQueue (nullptr),
  271. bufferList (numChannels)
  272. {
  273. jassert (numChannels == 2);
  274. SLDataFormat_PCM pcmFormat =
  275. {
  276. SL_DATAFORMAT_PCM,
  277. numChannels,
  278. sampleRate * 1000, // (sample rate units are millihertz)
  279. SL_PCMSAMPLEFORMAT_FIXED_16,
  280. SL_PCMSAMPLEFORMAT_FIXED_16,
  281. SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
  282. SL_BYTEORDER_LITTLEENDIAN
  283. };
  284. SLDataLocator_AndroidSimpleBufferQueue bufferQueue = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, bufferList.numBuffers };
  285. SLDataSource audioSrc = { &bufferQueue, &pcmFormat };
  286. SLDataLocator_OutputMix outputMix = { SL_DATALOCATOR_OUTPUTMIX, engine.outputMixObject };
  287. SLDataSink audioSink = { &outputMix, nullptr };
  288. // (SL_IID_BUFFERQUEUE is not guaranteed to remain future-proof, so use SL_IID_ANDROIDSIMPLEBUFFERQUEUE)
  289. const SLInterfaceID interfaceIDs[] = { *engine.SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
  290. const SLboolean flags[] = { SL_BOOLEAN_TRUE };
  291. check ((*engine.engineInterface)->CreateAudioPlayer (engine.engineInterface, &playerObject, &audioSrc, &audioSink,
  292. 1, interfaceIDs, flags));
  293. check ((*playerObject)->Realize (playerObject, SL_BOOLEAN_FALSE));
  294. check ((*playerObject)->GetInterface (playerObject, *engine.SL_IID_PLAY, &playerPlay));
  295. check ((*playerObject)->GetInterface (playerObject, *engine.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &playerBufferQueue));
  296. check ((*playerBufferQueue)->RegisterCallback (playerBufferQueue, staticCallback, this));
  297. }
  298. ~Player()
  299. {
  300. if (playerPlay != nullptr)
  301. check ((*playerPlay)->SetPlayState (playerPlay, SL_PLAYSTATE_STOPPED));
  302. if (playerBufferQueue != nullptr)
  303. check ((*playerBufferQueue)->Clear (playerBufferQueue));
  304. if (playerObject != nullptr)
  305. (*playerObject)->Destroy (playerObject);
  306. }
  307. bool openedOk() const noexcept { return playerBufferQueue != nullptr; }
  308. void start()
  309. {
  310. jassert (openedOk());
  311. check ((*playerPlay)->SetPlayState (playerPlay, SL_PLAYSTATE_PLAYING));
  312. }
  313. void writeBuffer (const AudioSampleBuffer& buffer, Thread& thread)
  314. {
  315. jassert (buffer.getNumChannels() == bufferList.numChannels);
  316. jassert (buffer.getNumSamples() < bufferList.numSamples * bufferList.numBuffers);
  317. int offset = 0;
  318. int numSamples = buffer.getNumSamples();
  319. while (numSamples > 0)
  320. {
  321. int16* const destBuffer = bufferList.waitForFreeBuffer (thread);
  322. if (destBuffer == nullptr)
  323. break;
  324. for (int i = 0; i < bufferList.numChannels; ++i)
  325. {
  326. typedef AudioData::Pointer <AudioData::Int16, AudioData::LittleEndian, AudioData::Interleaved, AudioData::NonConst> DstSampleType;
  327. typedef AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const> SrcSampleType;
  328. DstSampleType dstData (destBuffer + i, bufferList.numChannels);
  329. SrcSampleType srcData (buffer.getSampleData (i, offset));
  330. dstData.convertSamples (srcData, bufferList.numSamples);
  331. }
  332. check ((*playerBufferQueue)->Enqueue (playerBufferQueue, destBuffer, bufferList.getBufferSizeBytes()));
  333. bufferList.bufferSent();
  334. numSamples -= bufferList.numSamples;
  335. offset += bufferList.numSamples;
  336. }
  337. }
  338. private:
  339. SLObjectItf playerObject;
  340. SLPlayItf playerPlay;
  341. SLAndroidSimpleBufferQueueItf playerBufferQueue;
  342. BufferList bufferList;
  343. static void staticCallback (SLAndroidSimpleBufferQueueItf queue, void* context)
  344. {
  345. jassert (queue == static_cast <Player*> (context)->playerBufferQueue); (void) queue;
  346. static_cast <Player*> (context)->bufferList.bufferReturned();
  347. }
  348. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Player)
  349. };
  350. //==================================================================================================
  351. struct Recorder
  352. {
  353. Recorder (int numChannels, int sampleRate, Engine& engine)
  354. : recorderObject (nullptr), recorderRecord (nullptr), recorderBufferQueue (nullptr),
  355. bufferList (numChannels)
  356. {
  357. jassert (numChannels == 1); // STEREO doesn't always work!!
  358. SLDataFormat_PCM pcmFormat =
  359. {
  360. SL_DATAFORMAT_PCM,
  361. numChannels,
  362. sampleRate * 1000, // (sample rate units are millihertz)
  363. SL_PCMSAMPLEFORMAT_FIXED_16,
  364. SL_PCMSAMPLEFORMAT_FIXED_16,
  365. (numChannels == 1) ? SL_SPEAKER_FRONT_CENTER : (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT),
  366. SL_BYTEORDER_LITTLEENDIAN
  367. };
  368. SLDataLocator_IODevice ioDevice = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, SL_DEFAULTDEVICEID_AUDIOINPUT, nullptr };
  369. SLDataSource audioSrc = { &ioDevice, nullptr };
  370. SLDataLocator_AndroidSimpleBufferQueue bufferQueue = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, bufferList.numBuffers };
  371. SLDataSink audioSink = { &bufferQueue, &pcmFormat };
  372. const SLInterfaceID interfaceIDs[] = { *engine.SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
  373. const SLboolean flags[] = { SL_BOOLEAN_TRUE };
  374. if (check ((*engine.engineInterface)->CreateAudioRecorder (engine.engineInterface, &recorderObject, &audioSrc,
  375. &audioSink, 1, interfaceIDs, flags)))
  376. {
  377. if (check ((*recorderObject)->Realize (recorderObject, SL_BOOLEAN_FALSE)))
  378. {
  379. check ((*recorderObject)->GetInterface (recorderObject, *engine.SL_IID_RECORD, &recorderRecord));
  380. check ((*recorderObject)->GetInterface (recorderObject, *engine.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &recorderBufferQueue));
  381. check ((*recorderBufferQueue)->RegisterCallback (recorderBufferQueue, staticCallback, this));
  382. check ((*recorderRecord)->SetRecordState (recorderRecord, SL_RECORDSTATE_STOPPED));
  383. for (int i = bufferList.numBuffers; --i >= 0;)
  384. {
  385. int16* const buffer = bufferList.getNextBuffer();
  386. jassert (buffer != nullptr);
  387. enqueueBuffer (buffer);
  388. }
  389. }
  390. }
  391. }
  392. ~Recorder()
  393. {
  394. if (recorderRecord != nullptr)
  395. check ((*recorderRecord)->SetRecordState (recorderRecord, SL_RECORDSTATE_STOPPED));
  396. if (recorderBufferQueue != nullptr)
  397. check ((*recorderBufferQueue)->Clear (recorderBufferQueue));
  398. if (recorderObject != nullptr)
  399. (*recorderObject)->Destroy (recorderObject);
  400. }
  401. bool openedOk() const noexcept { return recorderBufferQueue != nullptr; }
  402. void start()
  403. {
  404. jassert (openedOk());
  405. check ((*recorderRecord)->SetRecordState (recorderRecord, SL_RECORDSTATE_RECORDING));
  406. }
  407. void readNextBlock (AudioSampleBuffer& buffer, Thread& thread)
  408. {
  409. jassert (buffer.getNumChannels() == bufferList.numChannels);
  410. jassert (buffer.getNumSamples() < bufferList.numSamples * bufferList.numBuffers);
  411. jassert ((buffer.getNumSamples() % bufferList.numSamples) == 0);
  412. int offset = 0;
  413. int numSamples = buffer.getNumSamples();
  414. while (numSamples > 0)
  415. {
  416. int16* const srcBuffer = bufferList.waitForFreeBuffer (thread);
  417. if (srcBuffer == nullptr)
  418. break;
  419. for (int i = 0; i < bufferList.numChannels; ++i)
  420. {
  421. typedef AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst> DstSampleType;
  422. typedef AudioData::Pointer <AudioData::Int16, AudioData::LittleEndian, AudioData::Interleaved, AudioData::Const> SrcSampleType;
  423. DstSampleType dstData (buffer.getSampleData (i, offset));
  424. SrcSampleType srcData (srcBuffer + i, bufferList.numChannels);
  425. dstData.convertSamples (srcData, bufferList.numSamples);
  426. }
  427. enqueueBuffer (srcBuffer);
  428. numSamples -= bufferList.numSamples;
  429. offset += bufferList.numSamples;
  430. }
  431. }
  432. private:
  433. SLObjectItf recorderObject;
  434. SLRecordItf recorderRecord;
  435. SLAndroidSimpleBufferQueueItf recorderBufferQueue;
  436. BufferList bufferList;
  437. void enqueueBuffer (int16* buffer)
  438. {
  439. check ((*recorderBufferQueue)->Enqueue (recorderBufferQueue, buffer, bufferList.getBufferSizeBytes()));
  440. bufferList.bufferSent();
  441. }
  442. static void staticCallback (SLAndroidSimpleBufferQueueItf queue, void* context)
  443. {
  444. jassert (queue == static_cast <Recorder*> (context)->recorderBufferQueue); (void) queue;
  445. static_cast <Recorder*> (context)->bufferList.bufferReturned();
  446. }
  447. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Recorder)
  448. };
  449. //==============================================================================
  450. Engine engine;
  451. ScopedPointer<Player> player;
  452. ScopedPointer<Recorder> recorder;
  453. //==============================================================================
  454. static bool check (const SLresult result)
  455. {
  456. jassert (result == SL_RESULT_SUCCESS);
  457. return result == SL_RESULT_SUCCESS;
  458. }
  459. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenSLAudioIODevice)
  460. };
  461. //==============================================================================
  462. class OpenSLAudioDeviceType : public AudioIODeviceType
  463. {
  464. public:
  465. OpenSLAudioDeviceType() : AudioIODeviceType (openSLTypeName) {}
  466. //==============================================================================
  467. void scanForDevices() {}
  468. StringArray getDeviceNames (bool wantInputNames) const { return StringArray (openSLTypeName); }
  469. int getDefaultDeviceIndex (bool forInput) const { return 0; }
  470. int getIndexOfDevice (AudioIODevice* device, bool asInput) const { return device != nullptr ? 0 : -1; }
  471. bool hasSeparateInputsAndOutputs() const { return false; }
  472. AudioIODevice* createDevice (const String& outputDeviceName,
  473. const String& inputDeviceName)
  474. {
  475. ScopedPointer<OpenSLAudioIODevice> dev;
  476. if (outputDeviceName.isNotEmpty() || inputDeviceName.isNotEmpty())
  477. {
  478. dev = new OpenSLAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName
  479. : inputDeviceName);
  480. if (! dev->openedOk())
  481. dev = nullptr;
  482. }
  483. return dev.release();
  484. }
  485. private:
  486. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenSLAudioDeviceType)
  487. };
  488. //==============================================================================
  489. AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_OpenSLES()
  490. {
  491. return isOpenSLAvailable() ? new OpenSLAudioDeviceType() : nullptr;
  492. }