Add an option for mono mic capture in ZoomIt

This commit is contained in:
foxmsft
2026-02-05 01:05:33 +01:00
committed by Alex Mihaiuc
parent bb4c548a4b
commit 450d6db343
12 changed files with 71 additions and 6 deletions

View File

@@ -34,13 +34,15 @@ namespace winrt
using namespace Windows::Devices::Enumeration;
}
AudioSampleGenerator::AudioSampleGenerator(bool captureMicrophone, bool captureSystemAudio)
AudioSampleGenerator::AudioSampleGenerator(bool captureMicrophone, bool captureSystemAudio, bool micMonoMix)
: m_captureMicrophone(captureMicrophone)
, m_captureSystemAudio(captureSystemAudio)
, m_micMonoMix(micMonoMix)
{
OutputDebugStringA(("AudioSampleGenerator created, captureMicrophone=" +
std::string(captureMicrophone ? "true" : "false") +
", captureSystemAudio=" + std::string(captureSystemAudio ? "true" : "false") + "\n").c_str());
", captureSystemAudio=" + std::string(captureSystemAudio ? "true" : "false") +
", micMonoMix=" + std::string(micMonoMix ? "true" : "false") + "\n").c_str());
m_audioEvent.create(wil::EventOptions::ManualReset);
m_endEvent.create(wil::EventOptions::ManualReset);
m_startEvent.create(wil::EventOptions::ManualReset);
@@ -631,6 +633,30 @@ void AudioSampleGenerator::OnAudioQuantumStarted(winrt::AudioGraph const& sender
uint32_t expectedSamplesPerQuantum = (m_graphSampleRate / 100) * m_graphChannels;
uint32_t numMicSamples = audioBuffer.Length() / sizeof(float);
// Apply mono mixing to microphone audio if enabled
// This converts stereo mic input (with same signal on both channels) to true mono
// by averaging the channels and writing the result to both channels
if (m_micMonoMix && m_captureMicrophone && numMicSamples > 0 && m_graphChannels >= 2)
{
float* micData = reinterpret_cast<float*>(sampleBuffer.data());
uint32_t numFrames = numMicSamples / m_graphChannels;
for (uint32_t i = 0; i < numFrames; i++)
{
// Sum all channels for this frame
float sum = 0.0f;
for (uint32_t ch = 0; ch < m_graphChannels; ch++)
{
sum += micData[i * m_graphChannels + ch];
}
// Power-preserving mix: divide by sqrt(N) to maintain perceived loudness
float mono = sum / std::sqrt(static_cast<float>(m_graphChannels));
for (uint32_t ch = 0; ch < m_graphChannels; ch++)
{
micData[i * m_graphChannels + ch] = mono;
}
}
}
// Drain loopback samples regardless of whether we have mic audio
if (m_loopbackCapture)
{

View File

@@ -5,7 +5,7 @@
class AudioSampleGenerator
{
public:
AudioSampleGenerator(bool captureMicrophone = true, bool captureSystemAudio = true);
AudioSampleGenerator(bool captureMicrophone = true, bool captureSystemAudio = true, bool micMonoMix = false);
~AudioSampleGenerator();
winrt::Windows::Foundation::IAsyncAction InitializeAsync();
@@ -70,4 +70,5 @@ private:
std::atomic<bool> m_started = false;
bool m_captureMicrophone = true;
bool m_captureSystemAudio = true;
bool m_micMonoMix = false;
};

View File

@@ -861,6 +861,7 @@ VideoRecordingSession::VideoRecordingSession(
uint32_t frameRate,
bool captureAudio,
bool captureSystemAudio,
bool micMonoMix,
winrt::Streams::IRandomAccessStream const& stream)
{
m_device = device;
@@ -964,7 +965,7 @@ VideoRecordingSession::VideoRecordingSession(
winrt::check_hresult(m_d3dDevice->CreateRenderTargetView(backBuffer.get(), nullptr, m_renderTargetView.put()));
// Always create audio generator for loopback capture; captureAudio controls microphone
m_audioGenerator = std::make_unique<AudioSampleGenerator>(captureAudio, captureSystemAudio);
m_audioGenerator = std::make_unique<AudioSampleGenerator>(captureAudio, captureSystemAudio, micMonoMix);
}
@@ -1112,9 +1113,10 @@ std::shared_ptr<VideoRecordingSession> VideoRecordingSession::Create(
uint32_t frameRate,
bool captureAudio,
bool captureSystemAudio,
bool micMonoMix,
winrt::Streams::IRandomAccessStream const& stream)
{
return std::shared_ptr<VideoRecordingSession>(new VideoRecordingSession(device, item, crop, frameRate, captureAudio, captureSystemAudio, stream));
return std::shared_ptr<VideoRecordingSession>(new VideoRecordingSession(device, item, crop, frameRate, captureAudio, captureSystemAudio, micMonoMix, stream));
}
//----------------------------------------------------------------------------

View File

@@ -28,6 +28,7 @@ public:
uint32_t frameRate,
bool captureAudio,
bool captureSystemAudio,
bool micMonoMix,
winrt::Streams::IRandomAccessStream const& stream);
~VideoRecordingSession();
@@ -188,6 +189,7 @@ private:
uint32_t frameRate,
bool captureAudio,
bool captureSystemAudio,
bool micMonoMix,
winrt::Streams::IRandomAccessStream const& stream);
void CloseInternal();

View File

@@ -279,6 +279,7 @@ BEGIN
LTEXT "To record a specific window, enter the hotkey with the Alt key in the opposite mode.",IDC_STATIC,7,55,251,19
CONTROL "Capture &system audio",IDC_CAPTURE_SYSTEM_AUDIO,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,7,149,83,10
CONTROL "&Capture audio input:",IDC_CAPTURE_AUDIO,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,7,161,83,10
CONTROL "Mono",IDC_MIC_MONO_MIX,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,98,161,30,10
COMBOBOX IDC_MICROPHONE,81,176,152,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Microphone:",IDC_MICROPHONE_LABEL,32,178,47,8
END

View File

@@ -51,6 +51,7 @@ DWORD g_RecordScalingMP4 = 100;
RecordingFormat g_RecordingFormat = RecordingFormat::MP4;
BOOLEAN g_CaptureSystemAudio = TRUE;
BOOLEAN g_CaptureAudio = FALSE;
BOOLEAN g_MicMonoMix = FALSE;
TCHAR g_MicrophoneDeviceId[MAX_PATH] = {0};
TCHAR g_RecordingSaveLocationBuffer[MAX_PATH] = {0};
TCHAR g_ScreenshotSaveLocationBuffer[MAX_PATH] = {0};
@@ -99,6 +100,7 @@ REG_SETTING RegSettings[] = {
{ L"RecordScalingMP4", SETTING_TYPE_DWORD, 0, &g_RecordScalingMP4, static_cast<DOUBLE>(g_RecordScalingMP4) },
{ L"CaptureAudio", SETTING_TYPE_BOOLEAN, 0, &g_CaptureAudio, static_cast<DOUBLE>(g_CaptureAudio) },
{ L"CaptureSystemAudio", SETTING_TYPE_BOOLEAN, 0, &g_CaptureSystemAudio, static_cast<DOUBLE>(g_CaptureSystemAudio) },
{ L"MicMonoMix", SETTING_TYPE_BOOLEAN, 0, &g_MicMonoMix, static_cast<DOUBLE>(g_MicMonoMix) },
{ L"MicrophoneDeviceId", SETTING_TYPE_STRING, sizeof(g_MicrophoneDeviceId), g_MicrophoneDeviceId, static_cast<DOUBLE>(0) },
{ L"RecordingSaveLocation", SETTING_TYPE_STRING, sizeof(g_RecordingSaveLocationBuffer), g_RecordingSaveLocationBuffer, static_cast<DOUBLE>(0) },
{ L"ScreenshotSaveLocation", SETTING_TYPE_STRING, sizeof(g_ScreenshotSaveLocationBuffer), g_ScreenshotSaveLocationBuffer, static_cast<DOUBLE>(0) },

View File

@@ -3840,6 +3840,9 @@ INT_PTR CALLBACK OptionsProc( HWND hDlg, UINT message,
CheckDlgButton( g_OptionsTabs[RECORD_PAGE].hPage, IDC_CAPTURE_AUDIO,
g_CaptureAudio ? BST_CHECKED: BST_UNCHECKED );
CheckDlgButton( g_OptionsTabs[RECORD_PAGE].hPage, IDC_MIC_MONO_MIX,
g_MicMonoMix ? BST_CHECKED: BST_UNCHECKED );
//
// The framerate drop down list is not used in the current version (might be added in the future)
//
@@ -4260,6 +4263,7 @@ INT_PTR CALLBACK OptionsProc( HWND hDlg, UINT message,
g_ShowExpiredTime = IsDlgButtonChecked( g_OptionsTabs[BREAK_PAGE].hPage, IDC_CHECK_SHOW_EXPIRED ) == BST_CHECKED;
g_CaptureSystemAudio = IsDlgButtonChecked(g_OptionsTabs[RECORD_PAGE].hPage, IDC_CAPTURE_SYSTEM_AUDIO) == BST_CHECKED;
g_CaptureAudio = IsDlgButtonChecked(g_OptionsTabs[RECORD_PAGE].hPage, IDC_CAPTURE_AUDIO) == BST_CHECKED;
g_MicMonoMix = IsDlgButtonChecked(g_OptionsTabs[RECORD_PAGE].hPage, IDC_MIC_MONO_MIX) == BST_CHECKED;
GetDlgItemText( g_OptionsTabs[BREAK_PAGE].hPage, IDC_TIMER, text, 3 );
text[2] = 0;
newTimeout = _tstoi( text );
@@ -5605,6 +5609,7 @@ winrt::fire_and_forget StartRecordingAsync( HWND hWnd, LPRECT rcCrop, HWND hWndR
g_RecordFrameRate,
g_CaptureAudio,
g_CaptureSystemAudio,
g_MicMonoMix,
stream );
recordingStarted = (g_RecordingSession != nullptr);

View File

@@ -111,6 +111,7 @@
#define IDC_SMOOTH_IMAGE 1107
#define IDC_CAPTURE_SYSTEM_AUDIO 1108
#define IDC_MICROPHONE_LABEL 1109
#define IDC_MIC_MONO_MIX 1110
#define IDC_SAVE 40002
#define IDC_COPY 40004
#define IDC_RECORD 40006