3
0

MicrophoneSystemComponent_Windows.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  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. #include "MicrophoneSystemComponent.h"
  9. #include <audioclient.h>
  10. #include <mmdeviceapi.h>
  11. #include <functiondiscoverykeys_devpkey.h>
  12. #include <AzCore/std/chrono/chrono.h>
  13. #include <AzCore/std/parallel/atomic.h>
  14. #include <AzCore/std/parallel/thread.h>
  15. #include <AzCore/std/smart_ptr/unique_ptr.h>
  16. #include <AudioRingBuffer.h>
  17. #include <IAudioInterfacesCommonData.h>
  18. #if defined(USE_LIBSAMPLERATE)
  19. #include <samplerate.h>
  20. #endif // USE_LIBSAMPLERATE
  21. namespace Audio
  22. {
  23. ///////////////////////////////////////////////////////////////////////////////////////////////
  24. class MicrophoneSystemComponentWindows : public MicrophoneSystemComponent::Implementation
  25. {
  26. public:
  27. AZ_CLASS_ALLOCATOR(MicrophoneSystemComponentWindows, AZ::SystemAllocator);
  28. ///////////////////////////////////////////////////////////////////////////////////////////
  29. bool InitializeDevice() override
  30. {
  31. AZ_TracePrintf("WindowsMicrophone", "Initializing Microphone device - Windows!!\n");
  32. // Assert: m_enumerator, m_device, m_audioClient, m_audioCaptureClient are all nullptr!
  33. AZ_Assert(!m_enumerator && !m_device && !m_audioClient && !m_audioCaptureClient, "InitializeDevice - One or more pointers are not null before init!\n");
  34. // This Gem initializes very early, before Qt application is booted.
  35. // Qt calls OleInitialize internally, which initializes COM with Apartment Threaded model.
  36. // To avoid errors, we initialize COM here with the same model.
  37. CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
  38. const CLSID CLSID_MMDeviceEnumerator_UUID = __uuidof(MMDeviceEnumerator);
  39. const IID IID_IMMDeviceEnumerator_UUID = __uuidof(IMMDeviceEnumerator);
  40. HRESULT hresult = CoCreateInstance(
  41. CLSID_MMDeviceEnumerator_UUID, nullptr,
  42. CLSCTX_ALL, IID_IMMDeviceEnumerator_UUID,
  43. reinterpret_cast<void**>(&m_enumerator)
  44. );
  45. if (FAILED(hresult))
  46. {
  47. AZ_Error("WindowsMicrophone", false, "Failed to create an MMDeviceEnumerator!\n");
  48. return false;
  49. }
  50. if (m_enumerator)
  51. {
  52. hresult = m_enumerator->GetDefaultAudioEndpoint(EDataFlow::eCapture, ERole::eConsole, &m_device);
  53. if (!m_device || hresult == ERROR_NOT_FOUND)
  54. {
  55. AZ_Warning("WindowsMicrophone", false, "No Microphone Device found!\n");
  56. return false;
  57. }
  58. if (FAILED(hresult))
  59. {
  60. AZ_Error("WindowsMicrophone", false, "HRESULT %d received while getting the default endpoint!\n", hresult);
  61. return false;
  62. }
  63. IPropertyStore* m_deviceProps = nullptr;
  64. hresult = m_device->OpenPropertyStore(STGM_READ, &m_deviceProps);
  65. if (FAILED(hresult))
  66. {
  67. AZ_Warning("WindowsMicrophone", false, "Failed to open the enpoint device's properties!\n");
  68. // not a full failure
  69. }
  70. PROPVARIANT endpointName;
  71. PropVariantInit(&endpointName);
  72. hresult = m_deviceProps->GetValue(PKEY_Device_FriendlyName, &endpointName);
  73. if (FAILED(hresult))
  74. {
  75. AZ_Warning("WindowsMicrophone", false, "Failed to get the endpoint device's friendly name!\n");
  76. // not a full failure
  77. }
  78. else
  79. {
  80. AZ_TracePrintf("WindowsMicrophone", "Microphone Endpoint Device Initialized: %S\n", endpointName.pwszVal);
  81. m_deviceName = endpointName.pwszVal;
  82. }
  83. PropVariantClear(&endpointName);
  84. if (m_deviceProps)
  85. {
  86. m_deviceProps->Release();
  87. m_deviceProps = nullptr;
  88. }
  89. }
  90. return true;
  91. }
  92. ///////////////////////////////////////////////////////////////////////////////////////////
  93. void ShutdownDevice() override
  94. {
  95. AZ_TracePrintf("WindowsMicrophone", "Shutting down Microphone device - Windows!\n");
  96. // Assert: m_audioClient and m_audioCaptureClient are both nullptr! (i.e. the capture thread is not running)
  97. AZ_Assert(!m_audioClient && !m_audioCaptureClient, "ShutdownDevice - Audio Client pointers are not null! You need to call EndSession first!\n");
  98. if (m_device)
  99. {
  100. m_device->Release();
  101. m_device = nullptr;
  102. }
  103. if (m_enumerator)
  104. {
  105. m_enumerator->Release();
  106. m_enumerator = nullptr;
  107. }
  108. CoUninitialize();
  109. }
  110. ///////////////////////////////////////////////////////////////////////////////////////////
  111. bool StartSession() override
  112. {
  113. AZ_TracePrintf("WindowsMicrophone", "Starting Microphone session - Windows!\n");
  114. AZ_Assert(m_device != nullptr, "Attempting to start a Microphone session while the device is uninitialized - Windows!\n");
  115. // Get the IAudioClient from the device
  116. const IID IID_IAudioClient_UUID = __uuidof(IAudioClient);
  117. HRESULT hresult = m_device->Activate(IID_IAudioClient_UUID, CLSCTX_ALL, nullptr, reinterpret_cast<void**>(&m_audioClient));
  118. if (FAILED(hresult))
  119. {
  120. AZ_Error("WindowsMicrophone", false, "Failed to get an IAudioClient on the device - Windows!\n");
  121. return false;
  122. }
  123. // Get the mix format of the IAudioClient
  124. hresult = m_audioClient->GetMixFormat(&m_streamFormat);
  125. if (FAILED(hresult))
  126. {
  127. AZ_Error("WindowsMicrophone", false, "Failed to get the mix format from the IAudioClient - Windows!\n");
  128. return false;
  129. }
  130. // Initialize the IAudioClient
  131. // Note: REFERENCE_TIME = 100 nanoseconds (1e2)
  132. // --> 1e9 ns = 1 sec
  133. // --> 1e7 (REFTIMES_PER_SEC) * 1e2 (REFERENCE_TIME) ns = 1 sec
  134. const AZ::u64 REFTIMES_PER_SEC = 10000000;
  135. REFERENCE_TIME duration = REFTIMES_PER_SEC;
  136. hresult = m_audioClient->Initialize(
  137. AUDCLNT_SHAREMODE_SHARED, // Share Mode
  138. 0, // Stream Flags
  139. duration, // Buffer Duration
  140. 0, // Periodicity
  141. m_streamFormat, // Wave Format Ex
  142. nullptr // Audio Session GUID
  143. );
  144. if (FAILED(hresult))
  145. {
  146. AZ_Error("WindowsMicrophone", false, "Failed to initialize the IAudioClient - Windows!\n");
  147. return false;
  148. }
  149. // Get size of the allocated buffer
  150. hresult = m_audioClient->GetBufferSize(&m_bufferFrameCount);
  151. if (FAILED(hresult))
  152. {
  153. AZ_Error("WindowsMicrophone", false, "Failed to get the buffer size of the IAudioClient - Windows!\n");
  154. return false;
  155. }
  156. // Get the IAudioCaptureClient
  157. const IID IID_IAudioCaptureClient_UUID = __uuidof(IAudioCaptureClient);
  158. hresult = m_audioClient->GetService(IID_IAudioCaptureClient_UUID, reinterpret_cast<void**>(&m_audioCaptureClient));
  159. if (FAILED(hresult))
  160. {
  161. // Some possible results: (hresult == E_NOINTERFACE || hresult == AUDCLNT_E_NOT_INITIALIZED || hresult == AUDCLNT_E_WRONG_ENDPOINT_TYPE)
  162. AZ_Error("WindowsMicrophone", false, "Failed to get an IAudioCaptureClient service interface - Windows!\n");
  163. return false;
  164. }
  165. // Set format for internal sink
  166. SetFormatInternal(m_bufferFrameCount);
  167. if (!ValidateFormatInternal())
  168. {
  169. AZ_Error("WindowsMicrophone", false, "Failed to set a supported format - Windows!\n");
  170. return false;
  171. }
  172. AllocateBuffersInternal();
  173. m_bufferDuration = static_cast<double>(REFTIMES_PER_SEC) * (m_bufferFrameCount / m_streamFormat->nSamplesPerSec);
  174. // Start recording!
  175. hresult = m_audioClient->Start();
  176. if (FAILED(hresult))
  177. {
  178. AZ_Error("WindowsMicrophone", false, "Failed to start Microphone recording - Windows!\n");
  179. return false;
  180. }
  181. // Spawn a new thread with the basic capture loop: [GetNextPacketSize, GetBuffer, CopyData, ReleaseBuffer]
  182. m_capturing = true;
  183. AZStd::thread_desc threadDesc;
  184. threadDesc.m_name = "MicrophoneCapture-WASAPI";
  185. auto captureFunc = AZStd::bind(&MicrophoneSystemComponentWindows::RunAudioCapture, this);
  186. m_captureThread = AZStd::thread(threadDesc, captureFunc);
  187. return true;
  188. }
  189. ///////////////////////////////////////////////////////////////////////////////////////////
  190. void RunAudioCapture()
  191. {
  192. const AZ::u64 REFTIMES_PER_MILLISEC = 10000;
  193. AZ::u32 numFramesAvailable = 0;
  194. AZ::u32 packetLength = 0;
  195. DWORD bufferFlags = 0;
  196. AZ::u8* data = nullptr;
  197. while (m_capturing)
  198. {
  199. AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(static_cast<AZ::u64>(m_bufferDuration) / REFTIMES_PER_MILLISEC / 2));
  200. HRESULT hresult = m_audioCaptureClient->GetNextPacketSize(&packetLength);
  201. if (FAILED(hresult))
  202. {
  203. AZ_Error("WindowsMicrophone", false, "Failed to GetNextPacketSize, ending thread - Windows!\n");
  204. m_capturing = false;
  205. continue;
  206. }
  207. while (m_capturing && packetLength != 0)
  208. {
  209. bufferFlags = 0;
  210. hresult = m_audioCaptureClient->GetBuffer(&data, &numFramesAvailable, &bufferFlags, nullptr, nullptr);
  211. if (FAILED(hresult))
  212. {
  213. AZ_Error("WindowsMicrophone", false, "Failed to GetBuffer, ending thread - Windows!\n");
  214. packetLength = 0;
  215. m_capturing = false;
  216. continue;
  217. }
  218. if (bufferFlags & AUDCLNT_BUFFERFLAGS_SILENT)
  219. {
  220. data = nullptr; // signals internal buffer to write silence
  221. }
  222. if (!CopyDataInternal(data, numFramesAvailable))
  223. {
  224. numFramesAvailable = 0;
  225. }
  226. hresult = m_audioCaptureClient->ReleaseBuffer(numFramesAvailable);
  227. if (FAILED(hresult))
  228. {
  229. AZ_Error("WindowsMicrophone", false, "Failed to ReleaseBuffer, ending thread - Windows!\n");
  230. packetLength = 0;
  231. m_capturing = false;
  232. continue;
  233. }
  234. hresult = m_audioCaptureClient->GetNextPacketSize(&packetLength);
  235. if (FAILED(hresult))
  236. {
  237. AZ_Error("WindowsMicrophone", false, "Failed to GetNextPacketSize, ending thread - Windows!\n");
  238. packetLength = 0;
  239. m_capturing = false;
  240. continue;
  241. }
  242. }
  243. }
  244. // Any post-thread cleanup?
  245. }
  246. ///////////////////////////////////////////////////////////////////////////////////////////
  247. void EndSession() override
  248. {
  249. AZ_TracePrintf("WindowsMicrophone", "Ending Microphone session - Windows!\n");
  250. // Signal thread to end
  251. m_capturing = false;
  252. // Thread join
  253. if (m_captureThread.joinable())
  254. {
  255. m_captureThread.join();
  256. m_captureThread = AZStd::thread(); // destroy
  257. }
  258. AZ_TracePrintf("WindowsMicrophone", "Microphone capture thread ended - Windows!");
  259. if (m_audioClient)
  260. {
  261. // Stop recording!
  262. HRESULT hresult = m_audioClient->Stop();
  263. if (FAILED(hresult))
  264. {
  265. AZ_Error("WindowsMicrophone", false, "Failed to stop Microphone recording - Windows!\n");
  266. }
  267. }
  268. if (m_audioCaptureClient)
  269. {
  270. m_audioCaptureClient->Release();
  271. m_audioCaptureClient = nullptr;
  272. }
  273. if (m_audioClient)
  274. {
  275. m_audioClient->Release();
  276. m_audioClient = nullptr;
  277. }
  278. CoTaskMemFree(m_streamFormat);
  279. m_streamFormat = nullptr;
  280. DeallocateBuffersInternal();
  281. }
  282. ///////////////////////////////////////////////////////////////////////////////////////////
  283. bool IsCapturing() override
  284. {
  285. return m_capturing;
  286. }
  287. ///////////////////////////////////////////////////////////////////////////////////////////
  288. SAudioInputConfig GetFormatConfig() const override
  289. {
  290. return m_config;
  291. }
  292. ///////////////////////////////////////////////////////////////////////////////////////////
  293. // Returns the number of sample frames obtained
  294. AZStd::size_t GetData(void** outputData, AZStd::size_t numFrames, const SAudioInputConfig& targetConfig, bool shouldDeinterleave) override
  295. {
  296. bool changeSampleType = (targetConfig.m_sampleType != m_config.m_sampleType);
  297. bool changeSampleRate = (targetConfig.m_sampleRate != m_config.m_sampleRate);
  298. bool changeNumChannels = (targetConfig.m_numChannels != m_config.m_numChannels);
  299. #if defined(USE_LIBSAMPLERATE)
  300. bool micFormatIsInt = (m_config.m_sampleType == AudioInputSampleType::Int);
  301. bool targetFormatIsInt = (targetConfig.m_sampleType == AudioInputSampleType::Int);
  302. bool stereoToMono = (targetConfig.m_numChannels == 1 && m_config.m_numChannels == 2);
  303. // Handle the default no-change case
  304. if (!(changeSampleType || changeSampleRate || changeNumChannels))
  305. {
  306. return m_captureData->ConsumeData(outputData, numFrames, m_config.m_numChannels, shouldDeinterleave);
  307. }
  308. // Consume mic data into the 'working' conversion buffer (In)...
  309. numFrames = m_captureData->ConsumeData(reinterpret_cast<void**>(&m_conversionBufferIn.m_data), numFrames, m_config.m_numChannels, false);
  310. if (micFormatIsInt && (changeSampleType || changeSampleRate || changeNumChannels))
  311. {
  312. // Do a prep [Int]-->[Float] conversion...
  313. src_short_to_float_array(
  314. reinterpret_cast<AZ::s16*>(m_conversionBufferIn.m_data),
  315. reinterpret_cast<float*>(m_conversionBufferOut.m_data),
  316. static_cast<int>(numFrames * m_config.m_numChannels)
  317. );
  318. // Swap to move the 'working' buffer back to the 'In' buffer.
  319. AZStd::swap(m_conversionBufferIn.m_data, m_conversionBufferOut.m_data);
  320. }
  321. if (changeSampleRate)
  322. {
  323. if (m_srcState && targetConfig.m_sampleRate < m_config.m_sampleRate)
  324. {
  325. // Setup Conversion Data
  326. m_srcData.end_of_input = 0;
  327. m_srcData.input_frames = static_cast<long>(numFrames);
  328. m_srcData.output_frames = static_cast<long>(numFrames);
  329. m_srcData.data_in = reinterpret_cast<float*>(m_conversionBufferIn.m_data);
  330. m_srcData.data_out = reinterpret_cast<float*>(m_conversionBufferOut.m_data);
  331. // Conversion ratio is output_sample_rate / input_sample_rate
  332. m_srcData.src_ratio = static_cast<double>(targetConfig.m_sampleRate) / static_cast<double>(m_config.m_sampleRate);
  333. // Process
  334. int error = src_process(m_srcState, &m_srcData);
  335. if (error != 0)
  336. {
  337. AZ_TracePrintf("WindowsMicrophone", "SRC(src_process): %s - Windows!\n", src_strerror(error));
  338. }
  339. AZ_Warning("WindowsMicrophone", numFrames == m_srcData.input_frames_used,
  340. "SRC(src_process): Num Frames requested (%u) was different than Num Frames processed (%u) - Windows!\n",
  341. numFrames, m_srcData.input_frames_used);
  342. numFrames = m_srcData.output_frames_gen;
  343. // Swap to move the 'working' buffer back to the 'In' buffer.
  344. AZStd::swap(m_conversionBufferIn.m_data, m_conversionBufferOut.m_data);
  345. }
  346. else
  347. {
  348. // Unable to continue: Either upsampling is requested or there is no resampler state.
  349. // todo: Upsampling could be done if we reallocate the conversion buffers. Right now we assume a max size of the buffer
  350. // based on the size of the ringbuffer. If samplerate is to increase, those buffers would need to increase in size
  351. // too. GetData is the only point which we know the target samplerate.
  352. return 0;
  353. }
  354. }
  355. if (changeNumChannels)
  356. {
  357. if (stereoToMono)
  358. {
  359. // Samples are interleaved now, copy only left channel to the output
  360. float* bufferInputData = reinterpret_cast<float*>(m_conversionBufferIn.m_data);
  361. float* bufferOutputData = reinterpret_cast<float*>(m_conversionBufferOut.m_data);
  362. for (AZ::u32 frame = 0; frame < numFrames; ++frame)
  363. {
  364. bufferOutputData[frame] = *bufferInputData++;
  365. ++bufferInputData;
  366. }
  367. }
  368. else // monoToStereo
  369. {
  370. // Split single samples to both left and right channels
  371. if (shouldDeinterleave)
  372. {
  373. float* bufferInputData = reinterpret_cast<float*>(m_conversionBufferIn.m_data);
  374. float** bufferOutputData = reinterpret_cast<float**>(m_conversionBufferOut.m_data);
  375. for (AZ::u32 frame = 0; frame < numFrames; ++frame)
  376. {
  377. bufferOutputData[0][frame] = bufferOutputData[1][frame] = bufferInputData[frame];
  378. }
  379. }
  380. else
  381. {
  382. float* bufferInputData = reinterpret_cast<float*>(m_conversionBufferIn.m_data);
  383. float* bufferOutputData = reinterpret_cast<float*>(m_conversionBufferOut.m_data);
  384. for (AZ::u32 frame = 0; frame < numFrames; ++frame)
  385. {
  386. *bufferOutputData++ = bufferInputData[frame];
  387. *bufferOutputData++ = bufferInputData[frame];
  388. }
  389. }
  390. }
  391. // Swap to move the 'working' buffer back to the 'In' buffer.
  392. AZStd::swap(m_conversionBufferIn.m_data, m_conversionBufferOut.m_data);
  393. }
  394. if (targetFormatIsInt)
  395. {
  396. // Do a final [Float]-->[Int] conversion...
  397. src_float_to_short_array(
  398. reinterpret_cast<float*>(m_conversionBufferIn.m_data),
  399. *reinterpret_cast<AZ::s16**>(outputData),
  400. static_cast<int>(numFrames * m_config.m_numChannels)
  401. );
  402. }
  403. else
  404. {
  405. // Otherwise, we're done, just memcpy the 'working' buffer to the output.
  406. ::memcpy(outputData, m_conversionBufferIn.m_data, numFrames * targetConfig.m_numChannels * (targetConfig.m_bitsPerSample >> 3));
  407. }
  408. return numFrames;
  409. #else
  410. if (changeSampleType || changeSampleRate || changeNumChannels)
  411. {
  412. // Without the SRC library, any change is unsupported!
  413. return 0;
  414. }
  415. else
  416. {
  417. // No change to the data from Input to Output
  418. return m_captureData->ConsumeData(outputData, numFrames, m_config.m_numChannels, shouldDeinterleave);
  419. }
  420. #endif // USE_LIBSAMPLERATE
  421. }
  422. ///////////////////////////////////////////////////////////////////////////////////////////
  423. void SetFormatInternal(AZ::u32 bufferFrameCount)
  424. {
  425. if (m_streamFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
  426. {
  427. auto streamFormatExt = reinterpret_cast<WAVEFORMATEXTENSIBLE*>(m_streamFormat);
  428. if (streamFormatExt)
  429. {
  430. if (streamFormatExt->SubFormat == KSDATAFORMAT_SUBTYPE_PCM)
  431. {
  432. AZ_TracePrintf("WindowsMicrophone", "PCM Format - Windows!\n");
  433. m_config.m_sampleType = AudioInputSampleType::Int;
  434. }
  435. else if (streamFormatExt->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)
  436. {
  437. AZ_TracePrintf("WindowsMicrophone", "IEEE Float Format - Windows!\n");
  438. m_config.m_sampleType = AudioInputSampleType::Float;
  439. }
  440. else
  441. {
  442. m_config.m_sampleType = AudioInputSampleType::Unsupported;
  443. }
  444. if (streamFormatExt->dwChannelMask == KSAUDIO_SPEAKER_MONO)
  445. {
  446. AZ_TracePrintf("WindowsMicrophone", "Channel Format: Mono - Windows!\n");
  447. m_config.m_numChannels = 1;
  448. }
  449. else if (streamFormatExt->dwChannelMask == KSAUDIO_SPEAKER_STEREO)
  450. {
  451. AZ_TracePrintf("WindowsMicrophone", "Channel Format: Stereo - Windows!\n");
  452. m_config.m_numChannels = 2;
  453. }
  454. else
  455. {
  456. AZ_Error("WindowsMicrophone", false, "Only Mono and Stereo microphone inputs are supported - Windows!\n");
  457. m_config.m_numChannels = 0;
  458. }
  459. m_config.m_sampleRate = m_streamFormat->nSamplesPerSec;
  460. m_config.m_bitsPerSample = m_streamFormat->wBitsPerSample;
  461. AZ_TracePrintf("WindowsMicrophone", "Sample Rate: %u - Windows!\n", m_config.m_sampleRate);
  462. AZ_TracePrintf("WindowsMicrophone", "Bits Per Sample: %u - Windows!\n", m_config.m_bitsPerSample);
  463. m_config.m_sourceType = Audio::AudioInputSourceType::Microphone;
  464. m_config.SetBufferSizeFromFrameCount(bufferFrameCount);
  465. }
  466. }
  467. else
  468. {
  469. // Untested code path.
  470. // Every device I tested went through the wave format extensible path...
  471. if (m_streamFormat->wFormatTag == WAVE_FORMAT_PCM)
  472. {
  473. m_config.m_sampleType = AudioInputSampleType::Int;
  474. }
  475. else if (m_streamFormat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
  476. {
  477. m_config.m_sampleType = AudioInputSampleType::Float;
  478. }
  479. else
  480. {
  481. m_config.m_sampleType = AudioInputSampleType::Unsupported;
  482. }
  483. m_config.m_numChannels = m_streamFormat->nChannels;
  484. m_config.m_sampleRate = m_streamFormat->nSamplesPerSec;
  485. m_config.m_bitsPerSample = m_streamFormat->wBitsPerSample;
  486. m_config.SetBufferSizeFromFrameCount(bufferFrameCount);
  487. }
  488. }
  489. ///////////////////////////////////////////////////////////////////////////////////////////
  490. bool ValidateFormatInternal()
  491. {
  492. bool valid = true;
  493. if (m_config.m_numChannels < 1 || m_config.m_numChannels > 2)
  494. {
  495. AZ_TracePrintf("WindowsMicrophone", "Only Mono and Stereo Microphone inputs are supported - Windows!\n");
  496. valid = false;
  497. }
  498. if (m_config.m_sampleType == AudioInputSampleType::Unsupported)
  499. {
  500. AZ_TracePrintf("WindowsMicrophone", "Unsupported sample format detected - Windows!\n");
  501. valid = false;
  502. }
  503. if (m_config.m_sampleType == AudioInputSampleType::Int && m_config.m_bitsPerSample != 16)
  504. {
  505. AZ_TracePrintf("WindowsMicrophone", "Only bitdepths of 16 bits are supported with integer samples - Windows!\n");
  506. valid = false;
  507. }
  508. if (m_config.m_bufferSize == 0)
  509. {
  510. AZ_TracePrintf("WindowsMicrophone", "Buffer size for the Microphone input has not been set - Windows!\n");
  511. valid = false;
  512. }
  513. return valid;
  514. }
  515. ///////////////////////////////////////////////////////////////////////////////////////////
  516. void AllocateBuffersInternal()
  517. {
  518. AZ_Assert(m_config.m_bufferSize > 0, "Format was checked already, but buffer size of the Microhpone input is zero - Windows!\n");
  519. DeallocateBuffersInternal();
  520. if (m_config.m_sampleType == AudioInputSampleType::Float)
  521. {
  522. AZ_Assert(m_config.m_bitsPerSample == 32, "Format was checked already, but non-32-bit float samples are detected - Windows!\n");
  523. m_captureData.reset(aznew Audio::RingBuffer<float>(m_config.GetSampleCountFromBufferSize()));
  524. }
  525. else if (m_config.m_sampleType == AudioInputSampleType::Int)
  526. {
  527. AZ_Assert(m_config.m_bitsPerSample == 16, "Format was checked already, but non-16-bit integer samples are detected - Windows!\n");
  528. m_captureData.reset(aznew Audio::RingBuffer<AZ::s16>(m_config.GetSampleCountFromBufferSize()));
  529. }
  530. #if defined(USE_LIBSAMPLERATE)
  531. // New SRC State
  532. if (!m_srcState)
  533. {
  534. int error = 0;
  535. m_srcState = src_new(SRC_SINC_MEDIUM_QUALITY, m_config.m_numChannels, &error);
  536. if (m_srcState)
  537. {
  538. AZStd::size_t conversionBufferMaxSize = m_config.GetSampleCountFromBufferSize() * sizeof(float); // Use this because float is the biggest sample type.
  539. m_conversionBufferIn.m_data = static_cast<AZ::u8*>(azmalloc(conversionBufferMaxSize, MEMORY_ALLOCATION_ALIGNMENT, AZ::SystemAllocator));
  540. m_conversionBufferIn.m_sizeBytes = conversionBufferMaxSize;
  541. m_conversionBufferOut.m_data = static_cast<AZ::u8*>(azmalloc(conversionBufferMaxSize, MEMORY_ALLOCATION_ALIGNMENT, AZ::SystemAllocator));
  542. m_conversionBufferOut.m_sizeBytes = conversionBufferMaxSize;
  543. ::memset(m_conversionBufferIn.m_data, 0, m_conversionBufferIn.m_sizeBytes);
  544. ::memset(m_conversionBufferOut.m_data, 0, m_conversionBufferOut.m_sizeBytes);
  545. }
  546. else
  547. {
  548. AZ_TracePrintf("WindowsMicrophone", "SRC(src_new): %s - Windows!\n", src_strerror(error));
  549. }
  550. }
  551. #endif // USE_LIBSAMPLERATE
  552. }
  553. ///////////////////////////////////////////////////////////////////////////////////////////
  554. void DeallocateBuffersInternal()
  555. {
  556. if (m_captureData)
  557. {
  558. m_captureData.reset();
  559. }
  560. #if defined(USE_LIBSAMPLERATE)
  561. // Cleanup SRC State
  562. m_srcState = src_delete(m_srcState);
  563. m_srcState = nullptr;
  564. if (m_conversionBufferIn.m_data)
  565. {
  566. azfree(m_conversionBufferIn.m_data, AZ::SystemAllocator);
  567. m_conversionBufferIn.m_data = nullptr;
  568. m_conversionBufferIn.m_sizeBytes = 0;
  569. }
  570. if (m_conversionBufferOut.m_data)
  571. {
  572. azfree(m_conversionBufferOut.m_data, AZ::SystemAllocator);
  573. m_conversionBufferOut.m_data = nullptr;
  574. m_conversionBufferOut.m_sizeBytes = 0;
  575. }
  576. #endif // USE_LIBSAMPLERATE
  577. }
  578. ///////////////////////////////////////////////////////////////////////////////////////////
  579. bool CopyDataInternal(const AZ::u8* inputData, AZStd::size_t numFrames)
  580. {
  581. // This function should return false if unable to copy all the frames! That way we can pass 0 to ReleaseBuffer to signal
  582. // that the buffer was not consumed yet. The api states that between GetBuffer and corresponding ReleaseBuffer calls, you
  583. // need to read all of it or none of it. If unable to copy the entire buffer, calling ReleaseBuffer with 0 means that the
  584. // next call to GetBuffer will return the same buffer that wasn't consumed.
  585. AZStd::size_t numFramesCopied = m_captureData->AddData(inputData, numFrames, m_config.m_numChannels);
  586. return numFramesCopied > 0;
  587. }
  588. private:
  589. // Device and format data...
  590. IAudioClient* m_audioClient = nullptr;
  591. IAudioCaptureClient* m_audioCaptureClient = nullptr;
  592. IMMDevice* m_device = nullptr;
  593. IMMDeviceEnumerator* m_enumerator = nullptr;
  594. WAVEFORMATEX* m_streamFormat = nullptr;
  595. // Thread data...
  596. AZStd::atomic<bool> m_capturing = false;
  597. AZStd::wstring m_deviceName;
  598. AZStd::thread m_captureThread;
  599. // Wasapi buffer data...
  600. double m_bufferDuration = 0; // in REFTIMES_PER_SEC (1e7)
  601. AZ::u32 m_bufferFrameCount = 0; // number of frames the Wasapi buffer holds
  602. Audio::SAudioInputConfig m_config; // the format configuration
  603. AZStd::unique_ptr<Audio::RingBufferBase> m_captureData = nullptr;
  604. #if defined(USE_LIBSAMPLERATE)
  605. // Sample Rate Converter data...
  606. SRC_STATE* m_srcState = nullptr;
  607. SRC_DATA m_srcData;
  608. AudioStreamData m_conversionBufferIn;
  609. AudioStreamData m_conversionBufferOut;
  610. #endif // USE_LIBSAMPLERATE
  611. };
  612. ///////////////////////////////////////////////////////////////////////////////////////////////
  613. MicrophoneSystemComponent::Implementation* MicrophoneSystemComponent::Implementation::Create()
  614. {
  615. return aznew MicrophoneSystemComponentWindows();
  616. }
  617. } // namespace Audio