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.

592 lines
19KB

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