Skip to content

Commit 357779c

Browse files
committed
init
1 parent 15b06a8 commit 357779c

25 files changed

+3134
-0
lines changed

.clang-format

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Generated from CLion C/C++ Code Style settings
2+
BasedOnStyle: LLVM
3+
AccessModifierOffset: -4
4+
AlignAfterOpenBracket: Align
5+
AlignConsecutiveAssignments: None
6+
AlignOperands: Align
7+
AllowAllArgumentsOnNextLine: false
8+
AllowAllConstructorInitializersOnNextLine: false
9+
AllowAllParametersOfDeclarationOnNextLine: false
10+
AllowShortBlocksOnASingleLine: Always
11+
AllowShortCaseLabelsOnASingleLine: false
12+
AllowShortFunctionsOnASingleLine: All
13+
AllowShortIfStatementsOnASingleLine: Always
14+
AllowShortLambdasOnASingleLine: All
15+
AllowShortLoopsOnASingleLine: true
16+
AlwaysBreakAfterReturnType: None
17+
AlwaysBreakTemplateDeclarations: Yes
18+
BreakBeforeBraces: Custom
19+
BraceWrapping:
20+
AfterCaseLabel: false
21+
AfterClass: false
22+
AfterControlStatement: Never
23+
AfterEnum: false
24+
AfterFunction: false
25+
AfterNamespace: false
26+
AfterUnion: false
27+
BeforeCatch: false
28+
BeforeElse: false
29+
IndentBraces: false
30+
SplitEmptyFunction: false
31+
SplitEmptyRecord: true
32+
BreakBeforeBinaryOperators: None
33+
BreakBeforeTernaryOperators: true
34+
BreakConstructorInitializers: BeforeColon
35+
BreakInheritanceList: BeforeColon
36+
ColumnLimit: 0
37+
CompactNamespaces: false
38+
ContinuationIndentWidth: 8
39+
IndentCaseLabels: true
40+
IndentPPDirectives: None
41+
IndentWidth: 4
42+
KeepEmptyLinesAtTheStartOfBlocks: true
43+
MaxEmptyLinesToKeep: 2
44+
NamespaceIndentation: All
45+
ObjCSpaceAfterProperty: false
46+
ObjCSpaceBeforeProtocolList: true
47+
PointerAlignment: Right
48+
ReflowComments: false
49+
SpaceAfterCStyleCast: true
50+
SpaceAfterLogicalNot: false
51+
SpaceAfterTemplateKeyword: false
52+
SpaceBeforeAssignmentOperators: true
53+
SpaceBeforeCpp11BracedList: false
54+
SpaceBeforeCtorInitializerColon: true
55+
SpaceBeforeInheritanceColon: true
56+
SpaceBeforeParens: ControlStatements
57+
SpaceBeforeRangeBasedForLoopColon: false
58+
SpaceInEmptyParentheses: false
59+
SpacesBeforeTrailingComments: 0
60+
SpacesInAngles: false
61+
SpacesInCStyleCastParentheses: false
62+
SpacesInContainerLiterals: false
63+
SpacesInParentheses: false
64+
SpacesInSquareBrackets: false
65+
TabWidth: 4
66+
UseTab: Never

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,5 @@
3030
*.exe
3131
*.out
3232
*.app
33+
34+
.idea

AudioCapture.cpp

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
#include "AudioCapture.h"
2+
#include <Functiondiscoverykeys.h>
3+
#include <Mmdeviceapi.h>// For IMMDeviceEnumerator and related
4+
#include <combaseapi.h>
5+
#include <iostream>
6+
#include <mfapi.h>
7+
#include <stdexcept>
8+
#include <string>
9+
10+
// Helper function to throw HRESULT errors as exceptions
11+
void ThrowIfFailed(HRESULT hr) {
12+
if (FAILED(hr)) {
13+
throw std::runtime_error("HRESULT failed: " + std::to_string(hr));
14+
}
15+
}
16+
17+
AudioCapture::AudioCapture() {
18+
m_SampleReadyEvent = CreateEventEx(nullptr, nullptr, 0, EVENT_ALL_ACCESS);
19+
if (m_SampleReadyEvent == INVALID_HANDLE_VALUE) {
20+
ThrowIfFailed(HRESULT_FROM_WIN32(GetLastError()));
21+
}
22+
}
23+
24+
AudioCapture::~AudioCapture() {
25+
if (m_SampleReadyEvent != INVALID_HANDLE_VALUE) {
26+
CloseHandle(m_SampleReadyEvent);
27+
m_SampleReadyEvent = INVALID_HANDLE_VALUE;
28+
}
29+
CoTaskMemFree(m_MixFormat);
30+
}
31+
32+
HRESULT AudioCapture::Initialize() {
33+
if (m_IsInitialized) {
34+
return S_OK;
35+
}
36+
HRESULT hr = S_OK;
37+
38+
// Get the default audio render device
39+
Microsoft::WRL::ComPtr<IMMDeviceEnumerator> deviceEnumerator;
40+
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator),
41+
&deviceEnumerator);
42+
ThrowIfFailed(hr);
43+
44+
Microsoft::WRL::ComPtr<IMMDevice> audioDevice;
45+
hr = deviceEnumerator->GetDefaultAudioEndpoint(eRender, static_cast<ERole>(eConsole), &audioDevice);
46+
if (FAILED(hr)) {
47+
// Try getting the default communication device
48+
hr = deviceEnumerator->GetDefaultAudioEndpoint(eRender, static_cast<ERole>(eCommunications), &audioDevice);
49+
if (FAILED(hr)) {
50+
// If still faild use all
51+
hr = deviceEnumerator->GetDefaultAudioEndpoint(eRender, static_cast<ERole>(eAll), &audioDevice);
52+
if (FAILED(hr)) {
53+
throw std::runtime_error("Failed to get default audio render device");
54+
}
55+
}
56+
}
57+
58+
ThrowIfFailed(hr);
59+
60+
// Activate the audio client
61+
hr = audioDevice->Activate(__uuidof(IAudioClient3), CLSCTX_ALL, nullptr, &m_AudioClient);
62+
ThrowIfFailed(hr);
63+
64+
// Get the mix format
65+
hr = m_AudioClient->GetMixFormat(&m_MixFormat);
66+
ThrowIfFailed(hr);
67+
68+
// convert from Float to 16-bit PCM
69+
switch (m_MixFormat->wFormatTag) {
70+
case WAVE_FORMAT_PCM:
71+
// nothing to do
72+
break;
73+
74+
case WAVE_FORMAT_IEEE_FLOAT:
75+
m_MixFormat->wFormatTag = WAVE_FORMAT_PCM;
76+
m_MixFormat->wBitsPerSample = 16;
77+
m_MixFormat->nBlockAlign = m_MixFormat->nChannels * m_MixFormat->wBitsPerSample / 8;
78+
m_MixFormat->nAvgBytesPerSec = m_MixFormat->nSamplesPerSec * m_MixFormat->nBlockAlign;
79+
break;
80+
81+
case WAVE_FORMAT_EXTENSIBLE: {
82+
WAVEFORMATEXTENSIBLE *pWaveFormatExtensible = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(m_MixFormat);
83+
if (pWaveFormatExtensible->SubFormat == KSDATAFORMAT_SUBTYPE_PCM) {
84+
// nothing to do
85+
} else if (pWaveFormatExtensible->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) {
86+
pWaveFormatExtensible->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
87+
pWaveFormatExtensible->Format.wBitsPerSample = 16;
88+
pWaveFormatExtensible->Format.nBlockAlign =
89+
pWaveFormatExtensible->Format.nChannels *
90+
pWaveFormatExtensible->Format.wBitsPerSample /
91+
8;
92+
pWaveFormatExtensible->Format.nAvgBytesPerSec =
93+
pWaveFormatExtensible->Format.nSamplesPerSec *
94+
pWaveFormatExtensible->Format.nBlockAlign;
95+
pWaveFormatExtensible->Samples.wValidBitsPerSample =
96+
pWaveFormatExtensible->Format.wBitsPerSample;
97+
98+
// leave the channel mask as-is
99+
} else {
100+
// we can only handle float or PCM
101+
hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
102+
ThrowIfFailed(hr);
103+
}
104+
break;
105+
}
106+
107+
default:
108+
// we can only handle float or PCM
109+
hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
110+
ThrowIfFailed(hr);
111+
break;
112+
}
113+
114+
// Get engine period
115+
hr = m_AudioClient->GetSharedModeEnginePeriod(m_MixFormat, &m_DefaultPeriodInFrames, &m_FundamentalPeriodInFrames,
116+
&m_MinPeriodInFrames, &m_MaxPeriodInFrames);
117+
ThrowIfFailed(hr);
118+
119+
// Initialize the AudioClient in Shared Mode with the user specified buffer
120+
if (!m_isLowLatency) {
121+
hr = m_AudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED,
122+
AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
123+
200000,
124+
0,
125+
m_MixFormat,
126+
nullptr);
127+
} else {
128+
hr = m_AudioClient->InitializeSharedAudioStream(
129+
AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
130+
m_MinPeriodInFrames,
131+
m_MixFormat,
132+
nullptr);
133+
}
134+
135+
ThrowIfFailed(hr);
136+
137+
// Get the maximum size of the AudioClient Buffer
138+
hr = m_AudioClient->GetBufferSize(&m_BufferFrames);
139+
ThrowIfFailed(hr);
140+
141+
// Get the capture client
142+
hr = m_AudioClient->GetService(__uuidof(IAudioCaptureClient), (void **) &m_AudioCaptureClient);
143+
ThrowIfFailed(hr);
144+
145+
hr = m_AudioClient->SetEventHandle(m_SampleReadyEvent);
146+
ThrowIfFailed(hr);
147+
148+
m_IsInitialized = true;
149+
return hr;
150+
}
151+
152+
153+
HRESULT AudioCapture::StartCapture(const std::function<void(short *, uint32_t, uint32_t)> &handler) {
154+
if (!m_IsInitialized) {
155+
return E_FAIL;
156+
}
157+
HRESULT hr = m_AudioClient->Start();
158+
if (FAILED(hr)) {
159+
return hr;
160+
}
161+
162+
// Main capture loop
163+
while (true) {
164+
WaitForSingleObject(m_SampleReadyEvent, INFINITE);
165+
hr = OnAudioSampleReady(handler);
166+
if (FAILED(hr)) {
167+
break;
168+
}
169+
}
170+
171+
return hr;
172+
}
173+
174+
HRESULT AudioCapture::StopCapture() {
175+
HRESULT hr = S_OK;
176+
if (!m_IsInitialized) {
177+
return E_FAIL;
178+
}
179+
180+
hr = m_AudioClient->Stop();
181+
if (FAILED(hr)) {
182+
return hr;
183+
}
184+
m_IsInitialized = false;
185+
186+
return hr;
187+
}
188+
189+
HRESULT AudioCapture::OnAudioSampleReady(const std::function<void(short *, uint32_t, uint32_t)> &handler) {
190+
HRESULT hr = S_OK;
191+
UINT32 framesAvailable = 0;
192+
BYTE *data = nullptr;
193+
DWORD captureFlags;
194+
UINT64 devicePosition = 0;
195+
UINT64 qpcPosition = 0;
196+
DWORD cbBytesToCapture = 0;
197+
198+
// GetNextPacketSize in loop
199+
for (
200+
hr = m_AudioCaptureClient->GetNextPacketSize(&framesAvailable);
201+
SUCCEEDED(hr) && framesAvailable > 0;
202+
hr = m_AudioCaptureClient->GetNextPacketSize(&framesAvailable)) {
203+
cbBytesToCapture = framesAvailable * m_MixFormat->nBlockAlign;
204+
if ((m_cbDataSize + cbBytesToCapture) < m_cbDataSize) {
205+
hr = StopCapture();
206+
if (FAILED(hr)) {
207+
return hr;
208+
}
209+
break;
210+
}
211+
212+
hr = m_AudioCaptureClient->GetBuffer(&data, &framesAvailable, &captureFlags, &devicePosition, &qpcPosition);
213+
214+
215+
if (FAILED(hr)) {
216+
return hr;
217+
}
218+
219+
// if (captureFlags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) {
220+
// std::cout << std::endl;
221+
// }
222+
223+
if ((captureFlags & AUDCLNT_BUFFERFLAGS_SILENT) != 0) {
224+
memset(data, 0, framesAvailable * m_MixFormat->nBlockAlign);
225+
}
226+
227+
if (framesAvailable) {
228+
handler(reinterpret_cast<short *>(data), framesAvailable, m_MixFormat->nSamplesPerSec);
229+
}
230+
231+
m_cbDataSize += cbBytesToCapture;
232+
m_AudioCaptureClient->ReleaseBuffer(framesAvailable);
233+
}
234+
return hr;
235+
}
236+
237+
void AudioCapture::SetIsLowLatency(bool isLowLatency) {
238+
m_isLowLatency = isLowLatency;
239+
}

AudioCapture.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#pragma once
2+
3+
#include <Windows.h>
4+
#include <Audioclient.h>
5+
#include <Mmdeviceapi.h>
6+
#include <Functiondiscoverykeys_devpkey.h>
7+
#include <wrl/client.h>
8+
#include <fstream>
9+
#include <functional>
10+
11+
class AudioCapture {
12+
public:
13+
AudioCapture();
14+
~AudioCapture();
15+
16+
HRESULT Initialize();
17+
HRESULT StartCapture(const std::function<void(short *, uint32_t, uint32_t)> &handler);
18+
HRESULT StopCapture();
19+
void SetIsLowLatency(bool isLowLatency);
20+
21+
private:
22+
HRESULT OnAudioSampleReady(const std::function<void(short *, uint32_t,uint32_t)> &handler);
23+
bool m_IsInitialized = false;
24+
25+
WAVEFORMATEX *m_MixFormat = nullptr;
26+
Microsoft::WRL::ComPtr<IAudioClient3> m_AudioClient;
27+
Microsoft::WRL::ComPtr<IAudioCaptureClient> m_AudioCaptureClient;
28+
HANDLE m_SampleReadyEvent = INVALID_HANDLE_VALUE;
29+
UINT32 m_BufferFrames = 0;
30+
UINT32 m_DefaultPeriodInFrames = 0;
31+
UINT32 m_FundamentalPeriodInFrames = 0;
32+
UINT32 m_MinPeriodInFrames = 0;
33+
UINT32 m_MaxPeriodInFrames = 0;
34+
DWORD m_cbHeaderSize = 0;
35+
DWORD m_cbDataSize = 0;
36+
bool m_isLowLatency = false;
37+
};

0 commit comments

Comments
 (0)