3
0

MicrophoneSystemComponent_iOS.mm 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #import <Foundation/Foundation.h>
  9. #import <AudioToolbox/AudioToolbox.h>
  10. #include "MicrophoneSystemComponent.h"
  11. #include "SimpleDownsample.h"
  12. #include <AzCore/std/smart_ptr/unique_ptr.h>
  13. #include <AzCore/EBus/EBus.h>
  14. #include <AudioRingBuffer.h>
  15. #include <IAudioInterfacesCommonData.h>
  16. #if defined(USE_LIBSAMPLERATE)
  17. #include <samplerate.h>
  18. #endif // USE_LIBSAMPLERATE
  19. namespace Audio
  20. {
  21. class MicrophoneSystemComponentIOS : public MicrophoneSystemComponent::Implementation
  22. {
  23. public:
  24. bool InitializeDevice() override
  25. {
  26. m_isCapturing = false;
  27. OSStatus status;
  28. AudioComponentDescription desc;
  29. desc.componentType = kAudioUnitType_Output;
  30. desc.componentSubType = kAudioUnitSubType_RemoteIO;
  31. desc.componentFlags = 0;
  32. desc.componentFlagsMask = 0;
  33. desc.componentManufacturer = kAudioUnitManufacturer_Apple;
  34. AudioComponent inputComponent = AudioComponentFindNext(nullptr, &desc);
  35. status = AudioComponentInstanceNew(inputComponent, &m_audioUnit);
  36. if(status != 0)
  37. {
  38. AZ_Error("iOSMicrophone", false, "Error creating audio component: %d", status);
  39. return false;
  40. }
  41. AZ::u32 flag = 1;
  42. status = AudioUnitSetProperty(m_audioUnit,
  43. kAudioOutputUnitProperty_EnableIO,
  44. kAudioUnitScope_Input,
  45. kInputBus,
  46. &flag,
  47. sizeof(flag));
  48. if(status != 0)
  49. {
  50. AZ_Error("iOSMicrophone", false, "Error enabling audio input IO: %d", status);
  51. return false;
  52. }
  53. // disable output
  54. flag = 0;
  55. status = AudioUnitSetProperty(m_audioUnit,
  56. kAudioOutputUnitProperty_EnableIO,
  57. kAudioUnitScope_Output,
  58. kOutputBus,
  59. &flag,
  60. sizeof(flag));
  61. if(status != 0)
  62. {
  63. AZ_Error("iOSMicrophone", false, "Error enabling audio input IO: %d", status);
  64. return false;
  65. }
  66. AudioStreamBasicDescription audioFormat;
  67. audioFormat.mSampleRate = 44100.00;
  68. audioFormat.mFormatID = kAudioFormatLinearPCM;
  69. audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
  70. audioFormat.mFramesPerPacket = 1;
  71. audioFormat.mChannelsPerFrame = 1;
  72. audioFormat.mBitsPerChannel = 16;
  73. audioFormat.mBytesPerPacket = 2;
  74. audioFormat.mBytesPerFrame = 2;
  75. m_config.m_sampleRate = audioFormat.mSampleRate;
  76. m_config.m_numChannels = audioFormat.mChannelsPerFrame;
  77. m_config.m_bitsPerSample = audioFormat.mBitsPerChannel;
  78. m_config.SetBufferSizeFromFrameCount(8192);
  79. status = AudioUnitSetProperty(m_audioUnit,
  80. kAudioUnitProperty_StreamFormat,
  81. kAudioUnitScope_Output,
  82. kInputBus,
  83. &audioFormat,
  84. sizeof(audioFormat));
  85. if(status != 0)
  86. {
  87. AZ_Error("iOSMicrophone", false, "Error setting microphone inuput configuration: %d", status);
  88. return false;
  89. }
  90. AURenderCallbackStruct callbackStruct;
  91. callbackStruct.inputProc = recordingCallback;
  92. callbackStruct.inputProcRefCon = this;
  93. status = AudioUnitSetProperty(m_audioUnit,
  94. kAudioOutputUnitProperty_SetInputCallback,
  95. kAudioUnitScope_Global,
  96. kInputBus,
  97. &callbackStruct,
  98. sizeof(callbackStruct));
  99. if(status != 0)
  100. {
  101. AZ_Error("iOSMicrophone", false, "Error setting microphone callback: %d", status);
  102. return false;
  103. }
  104. // This disables buffer allocation as we provide our own
  105. flag = 0;
  106. status = AudioUnitSetProperty(m_audioUnit,
  107. kAudioUnitProperty_ShouldAllocateBuffer,
  108. kAudioUnitScope_Output,
  109. kInputBus,
  110. &flag,
  111. sizeof(flag));
  112. // Initialise
  113. status = AudioUnitInitialize(m_audioUnit);
  114. if(status != 0)
  115. {
  116. AZ_Error("iOSMicrophone", false, "Error initializing microphone: %d", status);
  117. return false;
  118. }
  119. return true;
  120. }
  121. void ShutdownDevice() override
  122. {
  123. m_isCapturing = false;
  124. AudioUnitUninitialize(m_audioUnit);
  125. }
  126. bool StartSession() override
  127. {
  128. m_captureData.reset(aznew Audio::RingBuffer<AZ::s16>(m_config.GetSampleCountFromBufferSize()));
  129. OSStatus status = AudioOutputUnitStart(m_audioUnit);
  130. if(status != 0)
  131. {
  132. AZ_Error("iOSMicrophone", false, "Error starting microphone: %d", status);
  133. return false;
  134. }
  135. m_isCapturing = true;
  136. return true;
  137. }
  138. void EndSession() override
  139. {
  140. OSStatus status = AudioOutputUnitStop(m_audioUnit);
  141. if(status != 0)
  142. {
  143. AZ_Error("iOSMicrophone", false, "Error stopping microphone: %d", status);
  144. }
  145. m_isCapturing = false;
  146. if(m_captureData)
  147. {
  148. m_captureData.reset();
  149. }
  150. }
  151. bool IsCapturing() override
  152. {
  153. return m_isCapturing;
  154. }
  155. SAudioInputConfig GetFormatConfig() const override
  156. {
  157. return m_config;
  158. }
  159. AZStd::size_t GetData(void** outputData, AZStd::size_t numFrames, const SAudioInputConfig& targetConfig, bool shouldDeinterleave) override
  160. {
  161. #if defined(USE_LIBSAMPLERATE)
  162. // pending port of LIBSAMPLERATE to iOS
  163. return {};
  164. #else
  165. bool changeSampleType = (targetConfig.m_sampleType != m_config.m_sampleType);
  166. bool changeSampleRate = (targetConfig.m_sampleRate != m_config.m_sampleRate);
  167. bool changeNumChannels = (targetConfig.m_numChannels != m_config.m_numChannels);
  168. if (changeSampleType || changeNumChannels)
  169. {
  170. // Without the SRC library, any change is unsupported!
  171. return {};
  172. }
  173. else if (changeSampleRate)
  174. {
  175. if(targetConfig.m_sampleRate > m_config.m_sampleRate)
  176. {
  177. AZ_Error("MacOSMicrophone", false, "Target sample rate is larger than source sample rate, this is not supported");
  178. return {};
  179. }
  180. auto sourceBuffer = new AZ::s16[numFrames];
  181. AZStd::size_t targetSize = GetDownsampleSize(numFrames, m_config.m_sampleRate, targetConfig.m_sampleRate);
  182. auto targetBuffer = new AZ::s16[targetSize];
  183. numFrames = m_captureData->ConsumeData(reinterpret_cast<void**>(&sourceBuffer), numFrames, m_config.m_numChannels, false);
  184. if(numFrames > 0)
  185. {
  186. Downsample(sourceBuffer, numFrames, m_config.m_sampleRate, targetBuffer, targetSize, targetConfig.m_sampleRate);
  187. // swap target data to output
  188. ::memcpy(*outputData, targetBuffer, numFrames * targetConfig.m_numChannels * (targetConfig.m_bitsPerSample >> 3));
  189. }
  190. delete [] sourceBuffer;
  191. delete [] targetBuffer;
  192. return numFrames;
  193. }
  194. else
  195. {
  196. // No change to the data from Input to Output
  197. return m_captureData->ConsumeData(outputData, numFrames, m_config.m_numChannels, shouldDeinterleave);
  198. }
  199. #endif
  200. return {};
  201. }
  202. void ProcessAudio(AudioBufferList* bufferList)
  203. {
  204. AudioBuffer sourceBuffer = bufferList->mBuffers[0];
  205. m_captureData->AddData((AZ::s16*)sourceBuffer.mData, sourceBuffer.mDataByteSize / 2, m_config.m_numChannels);
  206. }
  207. AudioComponentInstance m_audioUnit;
  208. private:
  209. static OSStatus recordingCallback(void* inRefCon,
  210. AudioUnitRenderActionFlags* ioActionFlags,
  211. const AudioTimeStamp* inTimeStamp,
  212. AZ::u32 inBusNumber,
  213. AZ::u32 inNumberFrames,
  214. AudioBufferList* ioData) {
  215. auto impl = reinterpret_cast<MicrophoneSystemComponentIOS*>(inRefCon);
  216. // Samples are 16 bits = 2 bytes.
  217. // 1 frame includes only 1 sample
  218. // one channel as mono was selected in setup
  219. AudioBuffer buffer;
  220. buffer.mNumberChannels = 1;
  221. buffer.mDataByteSize = inNumberFrames * 2;
  222. buffer.mData = new AZ::u8[buffer.mDataByteSize];
  223. // Put buffer in a AudioBufferList
  224. AudioBufferList bufferList;
  225. bufferList.mNumberBuffers = 1;
  226. bufferList.mBuffers[0] = buffer;
  227. // Obtain recorded samples
  228. AudioUnitRender(impl->m_audioUnit,
  229. ioActionFlags,
  230. inTimeStamp,
  231. inBusNumber,
  232. inNumberFrames,
  233. &bufferList);
  234. impl->ProcessAudio(&bufferList);
  235. // release the malloc'ed data in the buffer we created earlier
  236. delete [] (AZ::u8*)bufferList.mBuffers[0].mData;
  237. return noErr;
  238. }
  239. Audio::SAudioInputConfig m_config;
  240. bool m_isCapturing = false;
  241. AZStd::unique_ptr<Audio::RingBufferBase> m_captureData = nullptr;
  242. const AudioUnitElement kInputBus = 1;
  243. const AudioUnitElement kOutputBus = 0;
  244. };
  245. ///////////////////////////////////////////////////////////////////////////////////////////////
  246. MicrophoneSystemComponent::Implementation* MicrophoneSystemComponent::Implementation::Create()
  247. {
  248. return aznew MicrophoneSystemComponentIOS();
  249. }
  250. }