mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-03 09:46:54 +02:00
Add an option for mono mic capture in ZoomIt
This commit is contained in:
@@ -34,13 +34,15 @@ namespace winrt
|
|||||||
using namespace Windows::Devices::Enumeration;
|
using namespace Windows::Devices::Enumeration;
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioSampleGenerator::AudioSampleGenerator(bool captureMicrophone, bool captureSystemAudio)
|
AudioSampleGenerator::AudioSampleGenerator(bool captureMicrophone, bool captureSystemAudio, bool micMonoMix)
|
||||||
: m_captureMicrophone(captureMicrophone)
|
: m_captureMicrophone(captureMicrophone)
|
||||||
, m_captureSystemAudio(captureSystemAudio)
|
, m_captureSystemAudio(captureSystemAudio)
|
||||||
|
, m_micMonoMix(micMonoMix)
|
||||||
{
|
{
|
||||||
OutputDebugStringA(("AudioSampleGenerator created, captureMicrophone=" +
|
OutputDebugStringA(("AudioSampleGenerator created, captureMicrophone=" +
|
||||||
std::string(captureMicrophone ? "true" : "false") +
|
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_audioEvent.create(wil::EventOptions::ManualReset);
|
||||||
m_endEvent.create(wil::EventOptions::ManualReset);
|
m_endEvent.create(wil::EventOptions::ManualReset);
|
||||||
m_startEvent.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 expectedSamplesPerQuantum = (m_graphSampleRate / 100) * m_graphChannels;
|
||||||
uint32_t numMicSamples = audioBuffer.Length() / sizeof(float);
|
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
|
// Drain loopback samples regardless of whether we have mic audio
|
||||||
if (m_loopbackCapture)
|
if (m_loopbackCapture)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
class AudioSampleGenerator
|
class AudioSampleGenerator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AudioSampleGenerator(bool captureMicrophone = true, bool captureSystemAudio = true);
|
AudioSampleGenerator(bool captureMicrophone = true, bool captureSystemAudio = true, bool micMonoMix = false);
|
||||||
~AudioSampleGenerator();
|
~AudioSampleGenerator();
|
||||||
|
|
||||||
winrt::Windows::Foundation::IAsyncAction InitializeAsync();
|
winrt::Windows::Foundation::IAsyncAction InitializeAsync();
|
||||||
@@ -70,4 +70,5 @@ private:
|
|||||||
std::atomic<bool> m_started = false;
|
std::atomic<bool> m_started = false;
|
||||||
bool m_captureMicrophone = true;
|
bool m_captureMicrophone = true;
|
||||||
bool m_captureSystemAudio = true;
|
bool m_captureSystemAudio = true;
|
||||||
|
bool m_micMonoMix = false;
|
||||||
};
|
};
|
||||||
@@ -861,6 +861,7 @@ VideoRecordingSession::VideoRecordingSession(
|
|||||||
uint32_t frameRate,
|
uint32_t frameRate,
|
||||||
bool captureAudio,
|
bool captureAudio,
|
||||||
bool captureSystemAudio,
|
bool captureSystemAudio,
|
||||||
|
bool micMonoMix,
|
||||||
winrt::Streams::IRandomAccessStream const& stream)
|
winrt::Streams::IRandomAccessStream const& stream)
|
||||||
{
|
{
|
||||||
m_device = device;
|
m_device = device;
|
||||||
@@ -964,7 +965,7 @@ VideoRecordingSession::VideoRecordingSession(
|
|||||||
winrt::check_hresult(m_d3dDevice->CreateRenderTargetView(backBuffer.get(), nullptr, m_renderTargetView.put()));
|
winrt::check_hresult(m_d3dDevice->CreateRenderTargetView(backBuffer.get(), nullptr, m_renderTargetView.put()));
|
||||||
|
|
||||||
// Always create audio generator for loopback capture; captureAudio controls microphone
|
// 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,
|
uint32_t frameRate,
|
||||||
bool captureAudio,
|
bool captureAudio,
|
||||||
bool captureSystemAudio,
|
bool captureSystemAudio,
|
||||||
|
bool micMonoMix,
|
||||||
winrt::Streams::IRandomAccessStream const& stream)
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ public:
|
|||||||
uint32_t frameRate,
|
uint32_t frameRate,
|
||||||
bool captureAudio,
|
bool captureAudio,
|
||||||
bool captureSystemAudio,
|
bool captureSystemAudio,
|
||||||
|
bool micMonoMix,
|
||||||
winrt::Streams::IRandomAccessStream const& stream);
|
winrt::Streams::IRandomAccessStream const& stream);
|
||||||
~VideoRecordingSession();
|
~VideoRecordingSession();
|
||||||
|
|
||||||
@@ -188,6 +189,7 @@ private:
|
|||||||
uint32_t frameRate,
|
uint32_t frameRate,
|
||||||
bool captureAudio,
|
bool captureAudio,
|
||||||
bool captureSystemAudio,
|
bool captureSystemAudio,
|
||||||
|
bool micMonoMix,
|
||||||
winrt::Streams::IRandomAccessStream const& stream);
|
winrt::Streams::IRandomAccessStream const& stream);
|
||||||
void CloseInternal();
|
void CloseInternal();
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
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 &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 "&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
|
COMBOBOX IDC_MICROPHONE,81,176,152,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||||
LTEXT "Microphone:",IDC_MICROPHONE_LABEL,32,178,47,8
|
LTEXT "Microphone:",IDC_MICROPHONE_LABEL,32,178,47,8
|
||||||
END
|
END
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ DWORD g_RecordScalingMP4 = 100;
|
|||||||
RecordingFormat g_RecordingFormat = RecordingFormat::MP4;
|
RecordingFormat g_RecordingFormat = RecordingFormat::MP4;
|
||||||
BOOLEAN g_CaptureSystemAudio = TRUE;
|
BOOLEAN g_CaptureSystemAudio = TRUE;
|
||||||
BOOLEAN g_CaptureAudio = FALSE;
|
BOOLEAN g_CaptureAudio = FALSE;
|
||||||
|
BOOLEAN g_MicMonoMix = FALSE;
|
||||||
TCHAR g_MicrophoneDeviceId[MAX_PATH] = {0};
|
TCHAR g_MicrophoneDeviceId[MAX_PATH] = {0};
|
||||||
TCHAR g_RecordingSaveLocationBuffer[MAX_PATH] = {0};
|
TCHAR g_RecordingSaveLocationBuffer[MAX_PATH] = {0};
|
||||||
TCHAR g_ScreenshotSaveLocationBuffer[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"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"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"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"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"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) },
|
{ L"ScreenshotSaveLocation", SETTING_TYPE_STRING, sizeof(g_ScreenshotSaveLocationBuffer), g_ScreenshotSaveLocationBuffer, static_cast<DOUBLE>(0) },
|
||||||
|
|||||||
@@ -3840,6 +3840,9 @@ INT_PTR CALLBACK OptionsProc( HWND hDlg, UINT message,
|
|||||||
CheckDlgButton( g_OptionsTabs[RECORD_PAGE].hPage, IDC_CAPTURE_AUDIO,
|
CheckDlgButton( g_OptionsTabs[RECORD_PAGE].hPage, IDC_CAPTURE_AUDIO,
|
||||||
g_CaptureAudio ? BST_CHECKED: BST_UNCHECKED );
|
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)
|
// 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_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_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_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 );
|
GetDlgItemText( g_OptionsTabs[BREAK_PAGE].hPage, IDC_TIMER, text, 3 );
|
||||||
text[2] = 0;
|
text[2] = 0;
|
||||||
newTimeout = _tstoi( text );
|
newTimeout = _tstoi( text );
|
||||||
@@ -5605,6 +5609,7 @@ winrt::fire_and_forget StartRecordingAsync( HWND hWnd, LPRECT rcCrop, HWND hWndR
|
|||||||
g_RecordFrameRate,
|
g_RecordFrameRate,
|
||||||
g_CaptureAudio,
|
g_CaptureAudio,
|
||||||
g_CaptureSystemAudio,
|
g_CaptureSystemAudio,
|
||||||
|
g_MicMonoMix,
|
||||||
stream );
|
stream );
|
||||||
|
|
||||||
recordingStarted = (g_RecordingSession != nullptr);
|
recordingStarted = (g_RecordingSession != nullptr);
|
||||||
|
|||||||
@@ -111,6 +111,7 @@
|
|||||||
#define IDC_SMOOTH_IMAGE 1107
|
#define IDC_SMOOTH_IMAGE 1107
|
||||||
#define IDC_CAPTURE_SYSTEM_AUDIO 1108
|
#define IDC_CAPTURE_SYSTEM_AUDIO 1108
|
||||||
#define IDC_MICROPHONE_LABEL 1109
|
#define IDC_MICROPHONE_LABEL 1109
|
||||||
|
#define IDC_MIC_MONO_MIX 1110
|
||||||
#define IDC_SAVE 40002
|
#define IDC_SAVE 40002
|
||||||
#define IDC_COPY 40004
|
#define IDC_COPY 40004
|
||||||
#define IDC_RECORD 40006
|
#define IDC_RECORD 40006
|
||||||
|
|||||||
@@ -93,6 +93,8 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
|||||||
|
|
||||||
public BoolProperty CaptureAudio { get; set; }
|
public BoolProperty CaptureAudio { get; set; }
|
||||||
|
|
||||||
|
public BoolProperty MicMonoMix { get; set; }
|
||||||
|
|
||||||
public StringProperty MicrophoneDeviceId { get; set; }
|
public StringProperty MicrophoneDeviceId { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<local:NavigablePage
|
<local:NavigablePage
|
||||||
x:Class="Microsoft.PowerToys.Settings.UI.Views.ZoomItPage"
|
x:Class="Microsoft.PowerToys.Settings.UI.Views.ZoomItPage"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
@@ -281,6 +281,12 @@
|
|||||||
<tkcontrols:SettingsCard Name="ZoomItRecordCaptureAudio" ContentAlignment="Left">
|
<tkcontrols:SettingsCard Name="ZoomItRecordCaptureAudio" ContentAlignment="Left">
|
||||||
<CheckBox x:Uid="ZoomIt_Record_CaptureAudio" IsChecked="{x:Bind ViewModel.RecordCaptureAudio, Mode=TwoWay}" />
|
<CheckBox x:Uid="ZoomIt_Record_CaptureAudio" IsChecked="{x:Bind ViewModel.RecordCaptureAudio, Mode=TwoWay}" />
|
||||||
</tkcontrols:SettingsCard>
|
</tkcontrols:SettingsCard>
|
||||||
|
<tkcontrols:SettingsCard Name="ZoomItRecordMicMonoMix" ContentAlignment="Left">
|
||||||
|
<CheckBox
|
||||||
|
x:Uid="ZoomIt_Record_MicMonoMix"
|
||||||
|
IsChecked="{x:Bind ViewModel.RecordMicMonoMix, Mode=TwoWay}"
|
||||||
|
Visibility="{x:Bind ViewModel.RecordCaptureAudio, Mode=OneWay}" />
|
||||||
|
</tkcontrols:SettingsCard>
|
||||||
<tkcontrols:SettingsCard
|
<tkcontrols:SettingsCard
|
||||||
Name="ZoomItRecordMicrophone"
|
Name="ZoomItRecordMicrophone"
|
||||||
x:Uid="ZoomIt_Record_Microphone"
|
x:Uid="ZoomIt_Record_Microphone"
|
||||||
|
|||||||
@@ -4718,6 +4718,9 @@ The break timer font matches the text font.</value>
|
|||||||
<data name="ZoomIt_Record_CaptureAudio.Content" xml:space="preserve">
|
<data name="ZoomIt_Record_CaptureAudio.Content" xml:space="preserve">
|
||||||
<value>Capture audio input</value>
|
<value>Capture audio input</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="ZoomIt_Record_MicMonoMix.Content" xml:space="preserve">
|
||||||
|
<value>Mono</value>
|
||||||
|
</data>
|
||||||
<data name="ZoomIt_Record_Microphone.Header" xml:space="preserve">
|
<data name="ZoomIt_Record_Microphone.Header" xml:space="preserve">
|
||||||
<value>Microphone</value>
|
<value>Microphone</value>
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
@@ -878,6 +878,20 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool RecordMicMonoMix
|
||||||
|
{
|
||||||
|
get => _zoomItSettings.Properties.MicMonoMix.Value;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_zoomItSettings.Properties.MicMonoMix.Value != value)
|
||||||
|
{
|
||||||
|
_zoomItSettings.Properties.MicMonoMix.Value = value;
|
||||||
|
OnPropertyChanged(nameof(RecordMicMonoMix));
|
||||||
|
NotifySettingsChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public string RecordMicrophoneDeviceId
|
public string RecordMicrophoneDeviceId
|
||||||
{
|
{
|
||||||
get => _zoomItSettings.Properties.MicrophoneDeviceId.Value;
|
get => _zoomItSettings.Properties.MicrophoneDeviceId.Value;
|
||||||
|
|||||||
Reference in New Issue
Block a user