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.

514 lines
19KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. class IPhoneAudioIODevice : public AudioIODevice
  19. {
  20. public:
  21. IPhoneAudioIODevice (const String& deviceName)
  22. : AudioIODevice (deviceName, "Audio"),
  23. actualBufferSize (0),
  24. isRunning (false),
  25. audioUnit (0),
  26. callback (nullptr),
  27. floatData (1, 2)
  28. {
  29. numInputChannels = 2;
  30. numOutputChannels = 2;
  31. preferredBufferSize = 0;
  32. AudioSessionInitialize (0, 0, interruptionListenerStatic, this);
  33. updateDeviceInfo();
  34. }
  35. ~IPhoneAudioIODevice()
  36. {
  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 1; }
  59. int getBufferSizeSamples (int index) { return getDefaultBufferSize(); }
  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 = audioInputIsAvailable ? kAudioSessionCategory_PlayAndRecord
  80. : kAudioSessionCategory_MediaPlayback;
  81. AudioSessionSetProperty (kAudioSessionProperty_AudioCategory, sizeof (audioCategory), &audioCategory);
  82. AudioSessionAddPropertyListener (kAudioSessionProperty_AudioRouteChange, propertyChangedStatic, this);
  83. fixAudioRouteIfSetToReceiver();
  84. updateDeviceInfo();
  85. Float32 bufferDuration = preferredBufferSize / sampleRate;
  86. AudioSessionSetProperty (kAudioSessionProperty_PreferredHardwareIOBufferDuration, sizeof (bufferDuration), &bufferDuration);
  87. actualBufferSize = preferredBufferSize;
  88. prepareFloatBuffers();
  89. isRunning = true;
  90. propertyChanged (0, 0, 0); // creates and starts the AU
  91. lastError = audioUnit != 0 ? "" : "Couldn't open the device";
  92. return lastError;
  93. }
  94. void close()
  95. {
  96. if (isRunning)
  97. {
  98. isRunning = false;
  99. AudioSessionSetActive (false);
  100. if (audioUnit != 0)
  101. {
  102. AudioComponentInstanceDispose (audioUnit);
  103. audioUnit = 0;
  104. }
  105. }
  106. }
  107. bool isOpen() { return isRunning; }
  108. int getCurrentBufferSizeSamples() { return actualBufferSize; }
  109. double getCurrentSampleRate() { return sampleRate; }
  110. int getCurrentBitDepth() { return 16; }
  111. BigInteger getActiveOutputChannels() const { return activeOutputChans; }
  112. BigInteger getActiveInputChannels() const { return activeInputChans; }
  113. int getOutputLatencyInSamples() { return 0; } //xxx
  114. int getInputLatencyInSamples() { return 0; } //xxx
  115. void start (AudioIODeviceCallback* callback_)
  116. {
  117. if (isRunning && callback != callback_)
  118. {
  119. if (callback_ != nullptr)
  120. callback_->audioDeviceAboutToStart (this);
  121. const ScopedLock sl (callbackLock);
  122. callback = callback_;
  123. }
  124. }
  125. void stop()
  126. {
  127. if (isRunning)
  128. {
  129. AudioIODeviceCallback* lastCallback;
  130. {
  131. const ScopedLock sl (callbackLock);
  132. lastCallback = callback;
  133. callback = nullptr;
  134. }
  135. if (lastCallback != nullptr)
  136. lastCallback->audioDeviceStopped();
  137. }
  138. }
  139. bool isPlaying() { return isRunning && callback != nullptr; }
  140. String getLastError() { return lastError; }
  141. private:
  142. //==================================================================================================
  143. CriticalSection callbackLock;
  144. Float64 sampleRate;
  145. int numInputChannels, numOutputChannels;
  146. int preferredBufferSize;
  147. int actualBufferSize;
  148. bool isRunning;
  149. String lastError;
  150. AudioStreamBasicDescription format;
  151. AudioUnit audioUnit;
  152. UInt32 audioInputIsAvailable;
  153. AudioIODeviceCallback* callback;
  154. BigInteger activeOutputChans, activeInputChans;
  155. AudioSampleBuffer floatData;
  156. float* inputChannels[3];
  157. float* outputChannels[3];
  158. bool monoInputChannelNumber, monoOutputChannelNumber;
  159. void prepareFloatBuffers()
  160. {
  161. floatData.setSize (numInputChannels + numOutputChannels, actualBufferSize);
  162. zerostruct (inputChannels);
  163. zerostruct (outputChannels);
  164. for (int i = 0; i < numInputChannels; ++i)
  165. inputChannels[i] = floatData.getSampleData (i);
  166. for (int i = 0; i < numOutputChannels; ++i)
  167. outputChannels[i] = floatData.getSampleData (i + numInputChannels);
  168. }
  169. //==================================================================================================
  170. OSStatus process (AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp,
  171. UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData)
  172. {
  173. OSStatus err = noErr;
  174. if (audioInputIsAvailable)
  175. err = AudioUnitRender (audioUnit, ioActionFlags, inTimeStamp, 1, inNumberFrames, ioData);
  176. const ScopedLock sl (callbackLock);
  177. if (callback != nullptr)
  178. {
  179. if (audioInputIsAvailable && numInputChannels > 0)
  180. {
  181. short* shortData = (short*) ioData->mBuffers[0].mData;
  182. if (numInputChannels >= 2)
  183. {
  184. for (UInt32 i = 0; i < inNumberFrames; ++i)
  185. {
  186. inputChannels[0][i] = *shortData++ * (1.0f / 32768.0f);
  187. inputChannels[1][i] = *shortData++ * (1.0f / 32768.0f);
  188. }
  189. }
  190. else
  191. {
  192. if (monoInputChannelNumber > 0)
  193. ++shortData;
  194. for (UInt32 i = 0; i < inNumberFrames; ++i)
  195. {
  196. inputChannels[0][i] = *shortData++ * (1.0f / 32768.0f);
  197. ++shortData;
  198. }
  199. }
  200. }
  201. else
  202. {
  203. for (int i = numInputChannels; --i >= 0;)
  204. zeromem (inputChannels[i], sizeof (float) * inNumberFrames);
  205. }
  206. callback->audioDeviceIOCallback ((const float**) inputChannels, numInputChannels,
  207. outputChannels, numOutputChannels,
  208. (int) inNumberFrames);
  209. short* shortData = (short*) ioData->mBuffers[0].mData;
  210. int n = 0;
  211. if (numOutputChannels >= 2)
  212. {
  213. for (UInt32 i = 0; i < inNumberFrames; ++i)
  214. {
  215. shortData [n++] = (short) (outputChannels[0][i] * 32767.0f);
  216. shortData [n++] = (short) (outputChannels[1][i] * 32767.0f);
  217. }
  218. }
  219. else if (numOutputChannels == 1)
  220. {
  221. for (UInt32 i = 0; i < inNumberFrames; ++i)
  222. {
  223. const short s = (short) (outputChannels[monoOutputChannelNumber][i] * 32767.0f);
  224. shortData [n++] = s;
  225. shortData [n++] = s;
  226. }
  227. }
  228. else
  229. {
  230. zeromem (ioData->mBuffers[0].mData, 2 * sizeof (short) * inNumberFrames);
  231. }
  232. }
  233. else
  234. {
  235. zeromem (ioData->mBuffers[0].mData, 2 * sizeof (short) * inNumberFrames);
  236. }
  237. return err;
  238. }
  239. void updateDeviceInfo()
  240. {
  241. UInt32 size = sizeof (sampleRate);
  242. AudioSessionGetProperty (kAudioSessionProperty_CurrentHardwareSampleRate, &size, &sampleRate);
  243. size = sizeof (audioInputIsAvailable);
  244. AudioSessionGetProperty (kAudioSessionProperty_AudioInputAvailable, &size, &audioInputIsAvailable);
  245. }
  246. void propertyChanged (AudioSessionPropertyID inID, UInt32 inDataSize, const void* inPropertyValue)
  247. {
  248. if (! isRunning)
  249. return;
  250. if (inPropertyValue != nullptr)
  251. {
  252. CFDictionaryRef routeChangeDictionary = (CFDictionaryRef) inPropertyValue;
  253. CFNumberRef routeChangeReasonRef = (CFNumberRef) CFDictionaryGetValue (routeChangeDictionary,
  254. CFSTR (kAudioSession_AudioRouteChangeKey_Reason));
  255. SInt32 routeChangeReason;
  256. CFNumberGetValue (routeChangeReasonRef, kCFNumberSInt32Type, &routeChangeReason);
  257. if (routeChangeReason == kAudioSessionRouteChangeReason_OldDeviceUnavailable)
  258. fixAudioRouteIfSetToReceiver();
  259. }
  260. updateDeviceInfo();
  261. createAudioUnit();
  262. AudioSessionSetActive (true);
  263. if (audioUnit != 0)
  264. {
  265. UInt32 formatSize = sizeof (format);
  266. AudioUnitGetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &format, &formatSize);
  267. Float32 bufferDuration = preferredBufferSize / sampleRate;
  268. UInt32 bufferDurationSize = sizeof (bufferDuration);
  269. AudioSessionGetProperty (kAudioSessionProperty_CurrentHardwareIOBufferDuration, &bufferDurationSize, &bufferDurationSize);
  270. actualBufferSize = (int) (sampleRate * bufferDuration + 0.5);
  271. AudioOutputUnitStart (audioUnit);
  272. }
  273. }
  274. void interruptionListener (UInt32 inInterruption)
  275. {
  276. /*if (inInterruption == kAudioSessionBeginInterruption)
  277. {
  278. isRunning = false;
  279. AudioOutputUnitStop (audioUnit);
  280. if (juce_iPhoneShowModalAlert ("Audio Interrupted",
  281. "This could have been interrupted by another application or by unplugging a headset",
  282. @"Resume",
  283. @"Cancel"))
  284. {
  285. isRunning = true;
  286. propertyChanged (0, 0, 0);
  287. }
  288. }*/
  289. if (inInterruption == kAudioSessionEndInterruption)
  290. {
  291. isRunning = true;
  292. AudioSessionSetActive (true);
  293. AudioOutputUnitStart (audioUnit);
  294. }
  295. }
  296. //==================================================================================================
  297. static OSStatus processStatic (void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp,
  298. UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData)
  299. {
  300. return ((IPhoneAudioIODevice*) inRefCon)->process (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
  301. }
  302. static void propertyChangedStatic (void* inClientData, AudioSessionPropertyID inID, UInt32 inDataSize, const void* inPropertyValue)
  303. {
  304. ((IPhoneAudioIODevice*) inClientData)->propertyChanged (inID, inDataSize, inPropertyValue);
  305. }
  306. static void interruptionListenerStatic (void* inClientData, UInt32 inInterruption)
  307. {
  308. ((IPhoneAudioIODevice*) inClientData)->interruptionListener (inInterruption);
  309. }
  310. //==================================================================================================
  311. void resetFormat (const int numChannels)
  312. {
  313. memset (&format, 0, sizeof (format));
  314. format.mFormatID = kAudioFormatLinearPCM;
  315. format.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
  316. format.mBitsPerChannel = 8 * sizeof (short);
  317. format.mChannelsPerFrame = 2;
  318. format.mFramesPerPacket = 1;
  319. format.mBytesPerFrame = format.mBytesPerPacket = 2 * sizeof (short);
  320. }
  321. bool createAudioUnit()
  322. {
  323. if (audioUnit != 0)
  324. {
  325. AudioComponentInstanceDispose (audioUnit);
  326. audioUnit = 0;
  327. }
  328. resetFormat (2);
  329. AudioComponentDescription desc;
  330. desc.componentType = kAudioUnitType_Output;
  331. desc.componentSubType = kAudioUnitSubType_RemoteIO;
  332. desc.componentManufacturer = kAudioUnitManufacturer_Apple;
  333. desc.componentFlags = 0;
  334. desc.componentFlagsMask = 0;
  335. AudioComponent comp = AudioComponentFindNext (0, &desc);
  336. AudioComponentInstanceNew (comp, &audioUnit);
  337. if (audioUnit == 0)
  338. return false;
  339. const UInt32 one = 1;
  340. AudioUnitSetProperty (audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &one, sizeof (one));
  341. AudioChannelLayout layout;
  342. layout.mChannelBitmap = 0;
  343. layout.mNumberChannelDescriptions = 0;
  344. layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
  345. AudioUnitSetProperty (audioUnit, kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Input, 0, &layout, sizeof (layout));
  346. AudioUnitSetProperty (audioUnit, kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Output, 0, &layout, sizeof (layout));
  347. AURenderCallbackStruct inputProc;
  348. inputProc.inputProc = processStatic;
  349. inputProc.inputProcRefCon = this;
  350. AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &inputProc, sizeof (inputProc));
  351. AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &format, sizeof (format));
  352. AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &format, sizeof (format));
  353. AudioUnitInitialize (audioUnit);
  354. return true;
  355. }
  356. // If the routing is set to go through the receiver (i.e. the speaker, but quiet), this re-routes it
  357. // to make it loud. Needed because by default when using an input + output, the output is kept quiet.
  358. static void fixAudioRouteIfSetToReceiver()
  359. {
  360. CFStringRef audioRoute = 0;
  361. UInt32 propertySize = sizeof (audioRoute);
  362. if (AudioSessionGetProperty (kAudioSessionProperty_AudioRoute, &propertySize, &audioRoute) == noErr)
  363. {
  364. NSString* route = (NSString*) audioRoute;
  365. //DBG ("audio route: " + nsStringToJuce (route));
  366. if ([route hasPrefix: @"Receiver"])
  367. {
  368. UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;
  369. AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute, sizeof (audioRouteOverride), &audioRouteOverride);
  370. }
  371. CFRelease (audioRoute);
  372. }
  373. }
  374. JUCE_DECLARE_NON_COPYABLE (IPhoneAudioIODevice);
  375. };
  376. //==============================================================================
  377. class IPhoneAudioIODeviceType : public AudioIODeviceType
  378. {
  379. public:
  380. //==============================================================================
  381. IPhoneAudioIODeviceType()
  382. : AudioIODeviceType ("iPhone Audio")
  383. {
  384. }
  385. void scanForDevices() {}
  386. StringArray getDeviceNames (bool wantInputNames) const
  387. {
  388. return StringArray ("iPhone Audio");
  389. }
  390. int getDefaultDeviceIndex (bool forInput) const
  391. {
  392. return 0;
  393. }
  394. int getIndexOfDevice (AudioIODevice* device, bool asInput) const
  395. {
  396. return device != nullptr ? 0 : -1;
  397. }
  398. bool hasSeparateInputsAndOutputs() const { return false; }
  399. AudioIODevice* createDevice (const String& outputDeviceName,
  400. const String& inputDeviceName)
  401. {
  402. if (outputDeviceName.isNotEmpty() || inputDeviceName.isNotEmpty())
  403. return new IPhoneAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName
  404. : inputDeviceName);
  405. return nullptr;
  406. }
  407. private:
  408. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (IPhoneAudioIODeviceType);
  409. };
  410. //==============================================================================
  411. AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio()
  412. {
  413. return new IPhoneAudioIODeviceType();
  414. }