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.

571 lines
21KB

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