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.

556 lines
20KB

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