diff --git a/electron/native/bin/win32-x64/helpers-manifest.json b/electron/native/bin/win32-x64/helpers-manifest.json index dcc4ca13..3061bf01 100644 --- a/electron/native/bin/win32-x64/helpers-manifest.json +++ b/electron/native/bin/win32-x64/helpers-manifest.json @@ -5,10 +5,10 @@ "helpers": { "wgc-capture": { "binaryName": "wgc-capture.exe", - "binarySha256": "b016da7435ba68b9c235e85818b5f4e8c1647f2386b4f2205e9a690cd057f908", + "binarySha256": "a0e2f0b98a903f89ab4706a56623f141604c3a311102e8914c2780745e94c635", "sourceDir": "electron/native/wgc-capture", - "sourceFingerprint": "7ef55b0d4bb9674de2b263e9dad2de2a593d0cb6aa09aea9ab3dc1832010a9d0", - "updatedAt": "2026-05-06T11:48:48.808Z" + "sourceFingerprint": "820d13741c659a0357d4d7944f3dbf4ce778b9716725b47ec088f573db0e7311", + "updatedAt": "2026-05-06T13:15:19.804Z" }, "cursor-monitor": { "binaryName": "cursor-monitor.exe", diff --git a/electron/native/bin/win32-x64/wgc-capture.exe b/electron/native/bin/win32-x64/wgc-capture.exe index 54477a6c..c99ebf22 100644 Binary files a/electron/native/bin/win32-x64/wgc-capture.exe and b/electron/native/bin/win32-x64/wgc-capture.exe differ diff --git a/electron/native/wgc-capture/src/main.cpp b/electron/native/wgc-capture/src/main.cpp index 80e94c74..65c2ec33 100644 --- a/electron/native/wgc-capture/src/main.cpp +++ b/electron/native/wgc-capture/src/main.cpp @@ -229,6 +229,8 @@ static void writeCompanionAudioTimingMetadata( metadataFile << ",\"channels\":" << capture.channelCount(); metadataFile << ",\"gapFillCount\":" << capture.gapFillCount(); metadataFile << ",\"insertedSilenceFrames\":" << capture.insertedSilenceFrames(); + metadataFile << ",\"compactedDiscontinuityCount\":" << capture.compactedDiscontinuityCount(); + metadataFile << ",\"compactedDiscontinuityFrames\":" << capture.compactedDiscontinuityFrames(); const uint32_t discontinuityCount = capture.dataDiscontinuityCount(); if (discontinuityCount > 0) { metadataFile << ",\"dataDiscontinuityCount\":" << discontinuityCount; @@ -445,13 +447,16 @@ int main(int argc, char* argv[]) { if ( loopback.dataDiscontinuityCount() > 0 || loopback.timestampErrorCount() > 0 || - loopback.gapFillCount() > 0 + loopback.gapFillCount() > 0 || + loopback.compactedDiscontinuityCount() > 0 ) { std::cerr << "WARNING: System audio timing metadata includes discontinuities=" << loopback.dataDiscontinuityCount() << " timestampErrors=" << loopback.timestampErrorCount() << " gapFills=" << loopback.gapFillCount() << " insertedSilenceFrames=" << loopback.insertedSilenceFrames() + << " compactedDiscontinuities=" << loopback.compactedDiscontinuityCount() + << " compactedDiscontinuityFrames=" << loopback.compactedDiscontinuityFrames() << std::endl; } std::cout << "Audio path: " << config.audioOutputPath << std::endl; @@ -460,13 +465,16 @@ int main(int argc, char* argv[]) { if ( micCapture.dataDiscontinuityCount() > 0 || micCapture.timestampErrorCount() > 0 || - micCapture.gapFillCount() > 0 + micCapture.gapFillCount() > 0 || + micCapture.compactedDiscontinuityCount() > 0 ) { std::cerr << "WARNING: Microphone timing metadata includes discontinuities=" << micCapture.dataDiscontinuityCount() << " timestampErrors=" << micCapture.timestampErrorCount() << " gapFills=" << micCapture.gapFillCount() << " insertedSilenceFrames=" << micCapture.insertedSilenceFrames() + << " compactedDiscontinuities=" << micCapture.compactedDiscontinuityCount() + << " compactedDiscontinuityFrames=" << micCapture.compactedDiscontinuityFrames() << std::endl; } std::cout << "Mic path: " << config.micOutputPath << std::endl; diff --git a/electron/native/wgc-capture/src/wasapi_loopback.cpp b/electron/native/wgc-capture/src/wasapi_loopback.cpp index f7565ec7..80fa4dcc 100644 --- a/electron/native/wgc-capture/src/wasapi_loopback.cpp +++ b/electron/native/wgc-capture/src/wasapi_loopback.cpp @@ -11,7 +11,9 @@ namespace { constexpr int64_t kHundredNanosecondsPerSecond = 10000000; constexpr uint64_t kSilenceWriteChunkFrames = 4096; constexpr uint32_t kMinimumGapFillThresholdMs = 50; -constexpr uint32_t kDiscontinuityGapFillThresholdMs = 10; +// Wireless headsets can flag short WASAPI discontinuities that are less audible +// when compacted and later duration-corrected than when rendered as hard silence. +constexpr uint32_t kDiscontinuityGapFillThresholdMs = 140; constexpr uint32_t kBoundaryFadeInMs = 5; int64_t queryPerformanceCounterHns() { @@ -193,6 +195,8 @@ bool WasapiCapture::start() { timestampErrorCount_ = 0; gapFillCount_ = 0; insertedSilenceFrames_ = 0; + compactedDiscontinuityCount_ = 0; + compactedDiscontinuityFrames_ = 0; fadeInFramesRemaining_ = 0; writeWavHeader(outputFile_, 0); @@ -461,8 +465,14 @@ void WasapiCapture::captureThread() { const uint64_t gapThresholdFrames = gapThresholdFrameCount > 0 ? gapThresholdFrameCount : 1; - if (expectedStartFrame > writtenFrames + gapThresholdFrames) { - writeSilenceFrames(expectedStartFrame - writtenFrames, channels); + if (expectedStartFrame > writtenFrames) { + const uint64_t missingFrames = expectedStartFrame - writtenFrames; + if (missingFrames > gapThresholdFrames) { + writeSilenceFrames(missingFrames, channels); + } else if (hasDataDiscontinuity) { + compactedDiscontinuityCount_.fetch_add(1); + compactedDiscontinuityFrames_.fetch_add(missingFrames); + } } } } diff --git a/electron/native/wgc-capture/src/wasapi_loopback.h b/electron/native/wgc-capture/src/wasapi_loopback.h index 1a513446..01b6a01f 100644 --- a/electron/native/wgc-capture/src/wasapi_loopback.h +++ b/electron/native/wgc-capture/src/wasapi_loopback.h @@ -28,6 +28,8 @@ public: uint32_t timestampErrorCount() const { return timestampErrorCount_.load(); } uint32_t gapFillCount() const { return gapFillCount_.load(); } uint64_t insertedSilenceFrames() const { return insertedSilenceFrames_.load(); } + uint32_t compactedDiscontinuityCount() const { return compactedDiscontinuityCount_.load(); } + uint64_t compactedDiscontinuityFrames() const { return compactedDiscontinuityFrames_.load(); } private: bool initializeCommon(); @@ -61,5 +63,7 @@ private: std::atomic timestampErrorCount_{0}; std::atomic gapFillCount_{0}; std::atomic insertedSilenceFrames_{0}; + std::atomic compactedDiscontinuityCount_{0}; + std::atomic compactedDiscontinuityFrames_{0}; std::atomic fadeInFramesRemaining_{0}; };