fix(windows): honor selected microphone in native capture

This commit is contained in:
webadderall
2026-03-26 11:17:39 +11:00
parent 04b8edf33d
commit 6f6ef68cc4
4 changed files with 62 additions and 6 deletions
+3
View File
@@ -3041,6 +3041,9 @@ body{background:transparent;overflow:hidden;width:100vw;height:100vh}
const micPath = path.join(recordingsDir, `recording-${timestamp}.mic.wav`)
config.captureMic = true
config.micOutputPath = micPath
if (options.microphoneDeviceId) {
config.micDeviceId = options.microphoneDeviceId
}
if (options.microphoneLabel) {
config.micDeviceName = options.microphoneLabel
}
+6 -1
View File
@@ -29,6 +29,7 @@ struct CaptureConfig {
std::string outputPath;
std::string audioOutputPath;
std::string micOutputPath;
std::string micDeviceId;
std::string micDeviceName;
int fps = 60;
int width = 0;
@@ -113,6 +114,7 @@ static bool parseSimpleJson(const std::string& json, CaptureConfig& config) {
config.audioOutputPath = findString("audioOutputPath");
config.micOutputPath = findString("micOutputPath");
config.micDeviceId = findString("micDeviceId");
config.micDeviceName = findString("micDeviceName");
auto findBool = [&](const std::string& key) -> bool {
@@ -276,7 +278,10 @@ int main(int argc, char* argv[]) {
}
if (config.captureMic && !config.micOutputPath.empty()) {
micInitialized = micCapture.initializeMic(config.micOutputPath, config.micDeviceName);
micInitialized = micCapture.initializeMic(
config.micOutputPath,
config.micDeviceId,
config.micDeviceName);
if (!micInitialized) {
std::cerr << "WARNING: Failed to initialize WASAPI mic capture" << std::endl;
}
@@ -29,6 +29,36 @@ static std::wstring utf8ToWide(const std::string& str) {
return wstr;
}
IMMDevice* WasapiCapture::findCaptureDeviceById(const std::wstring& targetId) {
IMMDeviceCollection* collection = nullptr;
HRESULT hr = enumerator_->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE, &collection);
if (FAILED(hr)) return nullptr;
UINT count = 0;
collection->GetCount(&count);
for (UINT i = 0; i < count; i++) {
IMMDevice* dev = nullptr;
collection->Item(i, &dev);
LPWSTR deviceId = nullptr;
hr = dev->GetId(&deviceId);
if (SUCCEEDED(hr) && deviceId) {
const bool matches = targetId == deviceId;
CoTaskMemFree(deviceId);
if (matches) {
collection->Release();
return dev;
}
}
dev->Release();
}
collection->Release();
return nullptr;
}
IMMDevice* WasapiCapture::findCaptureDeviceByName(const std::wstring& targetName) {
IMMDeviceCollection* collection = nullptr;
HRESULT hr = enumerator_->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE, &collection);
@@ -37,6 +67,8 @@ IMMDevice* WasapiCapture::findCaptureDeviceByName(const std::wstring& targetName
UINT count = 0;
collection->GetCount(&count);
IMMDevice* partialMatch = nullptr;
for (UINT i = 0; i < count; i++) {
IMMDevice* dev = nullptr;
collection->Item(i, &dev);
@@ -50,15 +82,21 @@ IMMDevice* WasapiCapture::findCaptureDeviceByName(const std::wstring& targetName
PropVariantClear(&pv);
store->Release();
if (name.find(targetName) != std::wstring::npos || targetName.find(name) != std::wstring::npos) {
if (name == targetName) {
collection->Release();
return dev;
}
if (!partialMatch && (name.find(targetName) != std::wstring::npos || targetName.find(name) != std::wstring::npos)) {
partialMatch = dev;
continue;
}
dev->Release();
}
collection->Release();
return nullptr;
return partialMatch;
}
bool WasapiCapture::initializeLoopback(const std::string& outputPath) {
@@ -76,7 +114,10 @@ bool WasapiCapture::initializeLoopback(const std::string& outputPath) {
return initializeCommon();
}
bool WasapiCapture::initializeMic(const std::string& outputPath, const std::string& deviceName) {
bool WasapiCapture::initializeMic(
const std::string& outputPath,
const std::string& deviceId,
const std::string& deviceName) {
outputPath_ = outputPath;
streamFlags_ = 0;
@@ -85,7 +126,10 @@ bool WasapiCapture::initializeMic(const std::string& outputPath, const std::stri
IID_IMMDeviceEnumerator_, reinterpret_cast<void**>(&enumerator_));
if (FAILED(hr)) return false;
if (!deviceName.empty()) {
if (!deviceId.empty()) {
device_ = findCaptureDeviceById(utf8ToWide(deviceId));
}
if (!device_ && !deviceName.empty()) {
device_ = findCaptureDeviceByName(utf8ToWide(deviceName));
}
if (!device_) {
@@ -14,7 +14,10 @@ public:
~WasapiCapture();
bool initializeLoopback(const std::string& outputPath);
bool initializeMic(const std::string& outputPath, const std::string& deviceName = "");
bool initializeMic(
const std::string& outputPath,
const std::string& deviceId = "",
const std::string& deviceName = "");
bool start();
bool pause();
bool resume();
@@ -24,6 +27,7 @@ private:
bool initializeCommon();
void captureThread();
bool writeWavHeader(HANDLE file, DWORD dataSize);
IMMDevice* findCaptureDeviceById(const std::wstring& id);
IMMDevice* findCaptureDeviceByName(const std::wstring& name);
std::string outputPath_;