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.

juce_ios_Audio.cpp 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  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. class iOSAudioIODevice : public AudioIODevice
  18. {
  19. public:
  20. iOSAudioIODevice (const String& deviceName)
  21. : AudioIODevice (deviceName, "Audio"),
  22. actualBufferSize (0),
  23. isRunning (false),
  24. audioUnit (0),
  25. callback (nullptr),
  26. floatData (1, 2)
  27. {
  28. getSessionHolder().activeDevices.add (this);
  29. numInputChannels = 2;
  30. numOutputChannels = 2;
  31. preferredBufferSize = 0;
  32. updateDeviceInfo();
  33. }
  34. ~iOSAudioIODevice()
  35. {
  36. getSessionHolder().activeDevices.removeFirstMatchingValue (this);
  37. close();
  38. }
  39. StringArray getOutputChannelNames() override
  40. {
  41. StringArray s;
  42. s.add ("Left");
  43. s.add ("Right");
  44. return s;
  45. }
  46. StringArray getInputChannelNames() override
  47. {
  48. StringArray s;
  49. if (audioInputIsAvailable)
  50. {
  51. s.add ("Left");
  52. s.add ("Right");
  53. }
  54. return s;
  55. }
  56. Array<double> getAvailableSampleRates() override { return sampleRates; }
  57. Array<int> getAvailableBufferSizes() override
  58. {
  59. Array<int> r;
  60. for (int i = 6; i < 12; ++i)
  61. r.add (1 << i);
  62. return r;
  63. }
  64. int getDefaultBufferSize() override { return 1024; }
  65. String open (const BigInteger& inputChannelsWanted,
  66. const BigInteger& outputChannelsWanted,
  67. double targetSampleRate, int bufferSize) override
  68. {
  69. close();
  70. lastError.clear();
  71. preferredBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize;
  72. // xxx set up channel mapping
  73. activeOutputChans = outputChannelsWanted;
  74. activeOutputChans.setRange (2, activeOutputChans.getHighestBit(), false);
  75. numOutputChannels = activeOutputChans.countNumberOfSetBits();
  76. monoOutputChannelNumber = activeOutputChans.findNextSetBit (0);
  77. activeInputChans = inputChannelsWanted;
  78. activeInputChans.setRange (2, activeInputChans.getHighestBit(), false);
  79. numInputChannels = activeInputChans.countNumberOfSetBits();
  80. monoInputChannelNumber = activeInputChans.findNextSetBit (0);
  81. AudioSessionSetActive (true);
  82. if (numInputChannels > 0 && audioInputIsAvailable)
  83. {
  84. setSessionUInt32Property (kAudioSessionProperty_AudioCategory, kAudioSessionCategory_PlayAndRecord);
  85. setSessionUInt32Property (kAudioSessionProperty_OverrideCategoryEnableBluetoothInput, 1);
  86. }
  87. else
  88. {
  89. setSessionUInt32Property (kAudioSessionProperty_AudioCategory, kAudioSessionCategory_MediaPlayback);
  90. }
  91. AudioSessionAddPropertyListener (kAudioSessionProperty_AudioRouteChange, routingChangedStatic, this);
  92. fixAudioRouteIfSetToReceiver();
  93. updateDeviceInfo();
  94. setSessionFloat64Property (kAudioSessionProperty_PreferredHardwareSampleRate, targetSampleRate);
  95. updateSampleRates();
  96. setSessionFloat32Property (kAudioSessionProperty_PreferredHardwareIOBufferDuration, preferredBufferSize / sampleRate);
  97. updateCurrentBufferSize();
  98. prepareFloatBuffers (actualBufferSize);
  99. isRunning = true;
  100. routingChanged (nullptr); // creates and starts the AU
  101. lastError = audioUnit != 0 ? "" : "Couldn't open the device";
  102. return lastError;
  103. }
  104. void close() override
  105. {
  106. if (isRunning)
  107. {
  108. isRunning = false;
  109. setSessionUInt32Property (kAudioSessionProperty_AudioCategory, kAudioSessionCategory_MediaPlayback);
  110. AudioSessionRemovePropertyListenerWithUserData (kAudioSessionProperty_AudioRouteChange, routingChangedStatic, this);
  111. AudioSessionSetActive (false);
  112. if (audioUnit != 0)
  113. {
  114. AudioComponentInstanceDispose (audioUnit);
  115. audioUnit = 0;
  116. }
  117. }
  118. }
  119. bool isOpen() override { return isRunning; }
  120. int getCurrentBufferSizeSamples() override { return actualBufferSize; }
  121. double getCurrentSampleRate() override { return sampleRate; }
  122. int getCurrentBitDepth() override { return 16; }
  123. BigInteger getActiveOutputChannels() const override { return activeOutputChans; }
  124. BigInteger getActiveInputChannels() const override { return activeInputChans; }
  125. int getOutputLatencyInSamples() override { return 0; } //xxx
  126. int getInputLatencyInSamples() override { return 0; } //xxx
  127. void start (AudioIODeviceCallback* newCallback) override
  128. {
  129. if (isRunning && callback != newCallback)
  130. {
  131. if (newCallback != nullptr)
  132. newCallback->audioDeviceAboutToStart (this);
  133. const ScopedLock sl (callbackLock);
  134. callback = newCallback;
  135. }
  136. }
  137. void stop() override
  138. {
  139. if (isRunning)
  140. {
  141. AudioIODeviceCallback* lastCallback;
  142. {
  143. const ScopedLock sl (callbackLock);
  144. lastCallback = callback;
  145. callback = nullptr;
  146. }
  147. if (lastCallback != nullptr)
  148. lastCallback->audioDeviceStopped();
  149. }
  150. }
  151. bool isPlaying() override { return isRunning && callback != nullptr; }
  152. String getLastError() override { return lastError; }
  153. private:
  154. //==================================================================================================
  155. CriticalSection callbackLock;
  156. Float64 sampleRate;
  157. Array<Float64> sampleRates;
  158. int numInputChannels, numOutputChannels;
  159. int preferredBufferSize, actualBufferSize;
  160. bool isRunning;
  161. String lastError;
  162. AudioStreamBasicDescription format;
  163. AudioUnit audioUnit;
  164. UInt32 audioInputIsAvailable;
  165. AudioIODeviceCallback* callback;
  166. BigInteger activeOutputChans, activeInputChans;
  167. AudioSampleBuffer floatData;
  168. float* inputChannels[3];
  169. float* outputChannels[3];
  170. bool monoInputChannelNumber, monoOutputChannelNumber;
  171. void prepareFloatBuffers (int bufferSize)
  172. {
  173. if (numInputChannels + numOutputChannels > 0)
  174. {
  175. floatData.setSize (numInputChannels + numOutputChannels, bufferSize);
  176. zeromem (inputChannels, sizeof (inputChannels));
  177. zeromem (outputChannels, sizeof (outputChannels));
  178. for (int i = 0; i < numInputChannels; ++i)
  179. inputChannels[i] = floatData.getSampleData (i);
  180. for (int i = 0; i < numOutputChannels; ++i)
  181. outputChannels[i] = floatData.getSampleData (i + numInputChannels);
  182. }
  183. }
  184. //==================================================================================================
  185. OSStatus process (AudioUnitRenderActionFlags* flags, const AudioTimeStamp* time,
  186. const UInt32 numFrames, AudioBufferList* data)
  187. {
  188. OSStatus err = noErr;
  189. if (audioInputIsAvailable && numInputChannels > 0)
  190. err = AudioUnitRender (audioUnit, flags, time, 1, numFrames, data);
  191. const ScopedLock sl (callbackLock);
  192. if (callback != nullptr)
  193. {
  194. if ((int) numFrames > floatData.getNumSamples())
  195. prepareFloatBuffers ((int) numFrames);
  196. if (audioInputIsAvailable && numInputChannels > 0)
  197. {
  198. short* shortData = (short*) data->mBuffers[0].mData;
  199. if (numInputChannels >= 2)
  200. {
  201. for (UInt32 i = 0; i < numFrames; ++i)
  202. {
  203. inputChannels[0][i] = *shortData++ * (1.0f / 32768.0f);
  204. inputChannels[1][i] = *shortData++ * (1.0f / 32768.0f);
  205. }
  206. }
  207. else
  208. {
  209. if (monoInputChannelNumber > 0)
  210. ++shortData;
  211. for (UInt32 i = 0; i < numFrames; ++i)
  212. {
  213. inputChannels[0][i] = *shortData++ * (1.0f / 32768.0f);
  214. ++shortData;
  215. }
  216. }
  217. }
  218. else
  219. {
  220. for (int i = numInputChannels; --i >= 0;)
  221. zeromem (inputChannels[i], sizeof (float) * numFrames);
  222. }
  223. callback->audioDeviceIOCallback ((const float**) inputChannels, numInputChannels,
  224. outputChannels, numOutputChannels, (int) numFrames);
  225. short* shortData = (short*) data->mBuffers[0].mData;
  226. int n = 0;
  227. if (numOutputChannels >= 2)
  228. {
  229. for (UInt32 i = 0; i < numFrames; ++i)
  230. {
  231. shortData [n++] = (short) (outputChannels[0][i] * 32767.0f);
  232. shortData [n++] = (short) (outputChannels[1][i] * 32767.0f);
  233. }
  234. }
  235. else if (numOutputChannels == 1)
  236. {
  237. for (UInt32 i = 0; i < numFrames; ++i)
  238. {
  239. const short s = (short) (outputChannels[monoOutputChannelNumber][i] * 32767.0f);
  240. shortData [n++] = s;
  241. shortData [n++] = s;
  242. }
  243. }
  244. else
  245. {
  246. zeromem (data->mBuffers[0].mData, 2 * sizeof (short) * numFrames);
  247. }
  248. }
  249. else
  250. {
  251. zeromem (data->mBuffers[0].mData, 2 * sizeof (short) * numFrames);
  252. }
  253. return err;
  254. }
  255. void updateDeviceInfo()
  256. {
  257. getSessionProperty (kAudioSessionProperty_CurrentHardwareSampleRate, sampleRate);
  258. getSessionProperty (kAudioSessionProperty_AudioInputAvailable, audioInputIsAvailable);
  259. }
  260. void updateSampleRates()
  261. {
  262. getSessionProperty (kAudioSessionProperty_CurrentHardwareSampleRate, sampleRate);
  263. sampleRates.clear();
  264. sampleRates.add (sampleRate);
  265. const int commonSampleRates[] = { 8000, 16000, 22050, 32000, 44100, 48000 };
  266. for (int i = 0; i < numElementsInArray (commonSampleRates); ++i)
  267. {
  268. Float64 rate = (Float64) commonSampleRates[i];
  269. if (rate != sampleRate)
  270. {
  271. setSessionFloat64Property (kAudioSessionProperty_PreferredHardwareSampleRate, rate);
  272. Float64 actualSampleRate = 0.0;
  273. getSessionProperty (kAudioSessionProperty_CurrentHardwareSampleRate, actualSampleRate);
  274. if (actualSampleRate == rate)
  275. sampleRates.add (actualSampleRate);
  276. }
  277. }
  278. DefaultElementComparator<Float64> comparator;
  279. sampleRates.sort (comparator);
  280. setSessionFloat64Property (kAudioSessionProperty_PreferredHardwareSampleRate, sampleRate);
  281. getSessionProperty (kAudioSessionProperty_CurrentHardwareSampleRate, sampleRate);
  282. }
  283. void updateCurrentBufferSize()
  284. {
  285. Float32 bufferDuration = sampleRate > 0 ? (Float32) (preferredBufferSize / sampleRate) : 0.0f;
  286. getSessionProperty (kAudioSessionProperty_CurrentHardwareIOBufferDuration, bufferDuration);
  287. actualBufferSize = (int) (sampleRate * bufferDuration + 0.5);
  288. }
  289. void routingChanged (const void* propertyValue)
  290. {
  291. if (! isRunning)
  292. return;
  293. if (propertyValue != nullptr)
  294. {
  295. CFDictionaryRef routeChangeDictionary = (CFDictionaryRef) propertyValue;
  296. CFNumberRef routeChangeReasonRef = (CFNumberRef) CFDictionaryGetValue (routeChangeDictionary,
  297. CFSTR (kAudioSession_AudioRouteChangeKey_Reason));
  298. SInt32 routeChangeReason;
  299. CFNumberGetValue (routeChangeReasonRef, kCFNumberSInt32Type, &routeChangeReason);
  300. if (routeChangeReason == kAudioSessionRouteChangeReason_OldDeviceUnavailable)
  301. {
  302. const ScopedLock sl (callbackLock);
  303. if (callback != nullptr)
  304. callback->audioDeviceError ("Old device unavailable");
  305. }
  306. }
  307. updateDeviceInfo();
  308. createAudioUnit();
  309. AudioSessionSetActive (true);
  310. if (audioUnit != 0)
  311. {
  312. UInt32 formatSize = sizeof (format);
  313. AudioUnitGetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &format, &formatSize);
  314. updateCurrentBufferSize();
  315. AudioOutputUnitStart (audioUnit);
  316. }
  317. }
  318. //==================================================================================================
  319. struct AudioSessionHolder
  320. {
  321. AudioSessionHolder()
  322. {
  323. AudioSessionInitialize (0, 0, interruptionListenerCallback, this);
  324. }
  325. static void interruptionListenerCallback (void* client, UInt32 interruptionType)
  326. {
  327. const Array <iOSAudioIODevice*>& activeDevices = static_cast <AudioSessionHolder*> (client)->activeDevices;
  328. for (int i = activeDevices.size(); --i >= 0;)
  329. activeDevices.getUnchecked(i)->interruptionListener (interruptionType);
  330. }
  331. Array <iOSAudioIODevice*> activeDevices;
  332. };
  333. static AudioSessionHolder& getSessionHolder()
  334. {
  335. static AudioSessionHolder audioSessionHolder;
  336. return audioSessionHolder;
  337. }
  338. void interruptionListener (const UInt32 interruptionType)
  339. {
  340. if (interruptionType == kAudioSessionBeginInterruption)
  341. {
  342. isRunning = false;
  343. AudioOutputUnitStop (audioUnit);
  344. AudioSessionSetActive (false);
  345. const ScopedLock sl (callbackLock);
  346. if (callback != nullptr)
  347. callback->audioDeviceError ("iOS audio session interruption");
  348. }
  349. if (interruptionType == kAudioSessionEndInterruption)
  350. {
  351. isRunning = true;
  352. AudioSessionSetActive (true);
  353. AudioOutputUnitStart (audioUnit);
  354. const ScopedLock sl (callbackLock);
  355. if (callback != nullptr)
  356. callback->audioDeviceError ("iOS audio session resumed");
  357. }
  358. }
  359. //==================================================================================================
  360. static OSStatus processStatic (void* client, AudioUnitRenderActionFlags* flags, const AudioTimeStamp* time,
  361. UInt32 /*busNumber*/, UInt32 numFrames, AudioBufferList* data)
  362. {
  363. return static_cast <iOSAudioIODevice*> (client)->process (flags, time, numFrames, data);
  364. }
  365. static void routingChangedStatic (void* client, AudioSessionPropertyID, UInt32 /*inDataSize*/, const void* propertyValue)
  366. {
  367. static_cast <iOSAudioIODevice*> (client)->routingChanged (propertyValue);
  368. }
  369. //==================================================================================================
  370. void resetFormat (const int numChannels) noexcept
  371. {
  372. zerostruct (format);
  373. format.mFormatID = kAudioFormatLinearPCM;
  374. format.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked | kAudioFormatFlagsNativeEndian;
  375. format.mBitsPerChannel = 8 * sizeof (short);
  376. format.mChannelsPerFrame = (UInt32) numChannels;
  377. format.mFramesPerPacket = 1;
  378. format.mBytesPerFrame = format.mBytesPerPacket = (UInt32) numChannels * sizeof (short);
  379. }
  380. bool createAudioUnit()
  381. {
  382. if (audioUnit != 0)
  383. {
  384. AudioComponentInstanceDispose (audioUnit);
  385. audioUnit = 0;
  386. }
  387. resetFormat (2);
  388. AudioComponentDescription desc;
  389. desc.componentType = kAudioUnitType_Output;
  390. desc.componentSubType = kAudioUnitSubType_RemoteIO;
  391. desc.componentManufacturer = kAudioUnitManufacturer_Apple;
  392. desc.componentFlags = 0;
  393. desc.componentFlagsMask = 0;
  394. AudioComponent comp = AudioComponentFindNext (0, &desc);
  395. AudioComponentInstanceNew (comp, &audioUnit);
  396. if (audioUnit == 0)
  397. return false;
  398. if (numInputChannels > 0)
  399. {
  400. const UInt32 one = 1;
  401. AudioUnitSetProperty (audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &one, sizeof (one));
  402. }
  403. {
  404. AudioChannelLayout layout;
  405. layout.mChannelBitmap = 0;
  406. layout.mNumberChannelDescriptions = 0;
  407. layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
  408. AudioUnitSetProperty (audioUnit, kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Input, 0, &layout, sizeof (layout));
  409. AudioUnitSetProperty (audioUnit, kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Output, 0, &layout, sizeof (layout));
  410. }
  411. {
  412. AURenderCallbackStruct inputProc;
  413. inputProc.inputProc = processStatic;
  414. inputProc.inputProcRefCon = this;
  415. AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &inputProc, sizeof (inputProc));
  416. }
  417. AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &format, sizeof (format));
  418. AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &format, sizeof (format));
  419. AudioUnitInitialize (audioUnit);
  420. return true;
  421. }
  422. // If the routing is set to go through the receiver (i.e. the speaker, but quiet), this re-routes it
  423. // to make it loud. Needed because by default when using an input + output, the output is kept quiet.
  424. static void fixAudioRouteIfSetToReceiver()
  425. {
  426. CFStringRef audioRoute = 0;
  427. if (getSessionProperty (kAudioSessionProperty_AudioRoute, audioRoute) == noErr)
  428. {
  429. NSString* route = (NSString*) audioRoute;
  430. //DBG ("audio route: " + nsStringToJuce (route));
  431. if ([route hasPrefix: @"Receiver"])
  432. setSessionUInt32Property (kAudioSessionProperty_OverrideAudioRoute, kAudioSessionOverrideAudioRoute_Speaker);
  433. CFRelease (audioRoute);
  434. }
  435. }
  436. template <typename Type>
  437. static OSStatus getSessionProperty (AudioSessionPropertyID propID, Type& result) noexcept
  438. {
  439. UInt32 valueSize = sizeof (result);
  440. return AudioSessionGetProperty (propID, &valueSize, &result);
  441. }
  442. static void setSessionUInt32Property (AudioSessionPropertyID propID, UInt32 v) noexcept { AudioSessionSetProperty (propID, sizeof (v), &v); }
  443. static void setSessionFloat32Property (AudioSessionPropertyID propID, Float32 v) noexcept { AudioSessionSetProperty (propID, sizeof (v), &v); }
  444. static void setSessionFloat64Property (AudioSessionPropertyID propID, Float64 v) noexcept { AudioSessionSetProperty (propID, sizeof (v), &v); }
  445. JUCE_DECLARE_NON_COPYABLE (iOSAudioIODevice)
  446. };
  447. //==============================================================================
  448. class iOSAudioIODeviceType : public AudioIODeviceType
  449. {
  450. public:
  451. iOSAudioIODeviceType() : AudioIODeviceType ("iOS Audio") {}
  452. void scanForDevices() {}
  453. StringArray getDeviceNames (bool /*wantInputNames*/) const { return StringArray ("iOS Audio"); }
  454. int getDefaultDeviceIndex (bool /*forInput*/) const { return 0; }
  455. int getIndexOfDevice (AudioIODevice* d, bool /*asInput*/) const { return d != nullptr ? 0 : -1; }
  456. bool hasSeparateInputsAndOutputs() const { return false; }
  457. AudioIODevice* createDevice (const String& outputDeviceName, const String& inputDeviceName)
  458. {
  459. if (outputDeviceName.isNotEmpty() || inputDeviceName.isNotEmpty())
  460. return new iOSAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName
  461. : inputDeviceName);
  462. return nullptr;
  463. }
  464. private:
  465. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (iOSAudioIODeviceType)
  466. };
  467. //==============================================================================
  468. AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio()
  469. {
  470. return new iOSAudioIODeviceType();
  471. }