refactor: extract WASAPI capture into AudioCapture class#6
Merged
Conversation
Step 3 of the cli_args_debugger.cpp decomposition (steps 1 and 2 were #3 and #4). Lifts the entire microphone pipeline out of ArgumentDebuggerWindow into its own class in audio_capture.{hpp,cpp}. What moves: - 11 member fields (device_enumerator_, capture_device_, audio_client_, capture_client_, mix_format_, audio_event_, audio_thread_, mic_level_, mic_available_, audio_thread_running_, mic_name_) - 4 methods (AudioCaptureThread static entry, AudioCaptureThreadImpl, InitializeMicrophone, PollMicrophone) - The two audio-specific hunks of OnDestroy and Cleanup What changes for consumers: - ArgumentDebuggerWindow now holds `AudioCapture audio_capture_` as a regular data member. Initialize() brings the pipeline up; Stop() takes it down cooperatively; IsAvailable() / Level() / Name() feed the meter in RenderFrame. - seh_wrapper.cpp now casts its LPVOID parameter to AudioCapture* and calls ThreadMain() on it. The ArgumentDebuggerWindow forward declaration in that file is gone. PollMicrophone (CCN 50 in the old file) is split: ResolveFormat, PeakFloat32 / PeakPcm16 / PeakPcm24 / PeakPcm32, PeakForFormat, plus a thin PollOnce that sequences GetNextPacketSize / GetBuffer / peak / ReleaseBuffer. PollOnce ends up at CCN 16; the peak helpers are all single-digit. cli_args_debugger.cpp: 1846 -> 1382 lines (-464). Only RenderFrame (CCN 21) remains over the static-analysis threshold in the main file.
kryuchenko
pushed a commit
that referenced
this pull request
Apr 18, 2026
Step 4 (final) of the cli_args_debugger.cpp decomposition (steps 1-3 were #3, #4, #6). RenderFrame was the last function above the static analysis threshold at CCN 21 / 203 NLOC / 289 lines and touching 37 other class members. Split into an orchestrator plus ten private section methods: UpdateFrameTiming - tick + FPS + rotation RenderCube - D3D11: clear, viewport, MVP, draw RenderTextHud - description, cli-args header, args, status RenderLoadedDataPanel - logs/saved-data panel (show_logs_) RenderPathsPanel - path items (show_paths_) RenderInputPrompt - exit prompt + user_input_ RenderQrBitmap - QR code blit RenderVolumeMeter - mic level bars + device name (or no-mic text) EndOverlay - D2D EndDraw + device-lost recreate PresentFrame - FPS log + Present + device-removed recreate RenderFrame itself is now ~20 lines of sequencing. No behaviour change intended — each helper receives the same D2D1_SIZE_F and draws into the same render target, in the same order as before. EndOverlay's bool return lets the orchestrator skip Present if the D2D device was lost, mirroring the original early `return`. Numbers: - Warnings (CCN > 15 or length > 1000) in cli_args_debugger.cpp: was 2 (PollMicrophone=50 already extracted, RenderFrame=21) → now 0. - cli_args_debugger.cpp stays within a line of the previous total — structural split, not a net-line reduction. - Highest-CCN function in the file drops to WindowProc at CCN 7.
4 tasks
kryuchenko
added a commit
that referenced
this pull request
Apr 18, 2026
Step 4 (final) of the cli_args_debugger.cpp decomposition (steps 1-3 were #3, #4, #6). RenderFrame was the last function above the static analysis threshold at CCN 21 / 203 NLOC / 289 lines and touching 37 other class members. Split into an orchestrator plus ten private section methods: UpdateFrameTiming - tick + FPS + rotation RenderCube - D3D11: clear, viewport, MVP, draw RenderTextHud - description, cli-args header, args, status RenderLoadedDataPanel - logs/saved-data panel (show_logs_) RenderPathsPanel - path items (show_paths_) RenderInputPrompt - exit prompt + user_input_ RenderQrBitmap - QR code blit RenderVolumeMeter - mic level bars + device name (or no-mic text) EndOverlay - D2D EndDraw + device-lost recreate PresentFrame - FPS log + Present + device-removed recreate RenderFrame itself is now ~20 lines of sequencing. No behaviour change intended — each helper receives the same D2D1_SIZE_F and draws into the same render target, in the same order as before. EndOverlay's bool return lets the orchestrator skip Present if the D2D device was lost, mirroring the original early `return`. Numbers: - Warnings (CCN > 15 or length > 1000) in cli_args_debugger.cpp: was 2 (PollMicrophone=50 already extracted, RenderFrame=21) → now 0. - cli_args_debugger.cpp stays within a line of the previous total — structural split, not a net-line reduction. - Highest-CCN function in the file drops to WindowProc at CCN 7. Co-authored-by: Andrey Kryuchenko <andrey@example.com>
3 tasks
kryuchenko
pushed a commit
that referenced
this pull request
Apr 18, 2026
…ests
Splits the 2101-line monolithic cli_args_debugger.cpp into a thin window
class plus four composable modules, and fills the test gap along the way.
End state: cli_args_debugger.cpp down to ~1270 lines, zero functions over
the static-analysis CCN threshold, and 33 new unit tests covering the
extracted pure logic.
Modules extracted
-----------------
log_manager.{hpp,cpp}
Owns g_log_file / g_logPath, InitLogger / Log / LogSEH / CloseLogger.
Collapses the duplicated cleanup that was inlined in both branches of
wWinMain. Keeps the extern globals so tests/logging_tests.cpp links
unchanged.
path_info.{hpp,cpp} (namespace path_info)
Hosts ExecutablePath / CurrentWorkingDirectory / TempDirectory /
WindowsDirectory / SystemDirectory / OsVersionString /
WineOrProtonVersion / SaveFilePath / Collect. While moving the code:
- MAX_PATH truncation fixed in five Win32 helpers via growing-retry
/ queried-size patterns.
- C-style casts on GetProcAddress → reinterpret_cast.
- exeDir = exeDir.substr(0, slash) → in-place erase.
- Wrapped under a namespace so Windows.h macros like
GetWindowsDirectory don't rewrite the function names.
audio_capture.{hpp,cpp} (class AudioCapture)
Pulls the entire WASAPI pipeline out of ArgumentDebuggerWindow: the
11 audio data members, InitializeMicrophone, PollMicrophone (CCN 50,
the worst function in the codebase), the capture thread entry, and
the audio slice of OnDestroy/Cleanup. The seh_wrapper TU now casts
its LPVOID straight to AudioCapture* and calls ThreadMain, so the
main class no longer leaks into it. PollMicrophone is split into
ResolveFormat + four per-format peak helpers + a dispatch; highest
resulting CCN is 16.
RenderFrame split in place
The CCN-21, 289-line RenderFrame is replaced by a ~20-line orchestrator
plus ten private section methods (UpdateFrameTiming, RenderCube,
RenderTextHud, RenderLoadedDataPanel, RenderPathsPanel,
RenderInputPrompt, RenderQrBitmap, RenderVolumeMeter, EndOverlay,
PresentFrame). No behaviour change — each helper draws the same
region in the same order; EndOverlay's bool return mirrors the
original device-lost early return.
Build system
------------
CMAKE_CXX_STANDARD 20 (required, extensions off) is set in both
CMakeLists.txt files, aligning the CMake/CTest builds with the
/std:c++20 flag already used by the raw-cl path in build.yml. Prior to
this, CMakeLists.txt left the standard unset (MSVC default ≈ C++14)
and tests/CMakeLists.txt pinned C++17, which is why
std::wstring::data() appeared to work for the main exe but broke
CTest compilation.
Tests
-----
tests/audio_peak_tests.cpp — 22 GoogleTest cases covering PeakFloat32,
PeakPcm16, PeakPcm24, PeakPcm32, PeakForFormat dispatch, ResolveFormat
for plain WAVEFORMATEX and WAVEFORMATEXTENSIBLE unwrapping. Includes
the 24-bit sign-extension edge case that used to be buried in
PollMicrophone, the "unsupported format degrades to 0" contract, and
the zero-channel-count fallback.
tests/path_info_tests.cpp — 11 smoke tests covering every path_info::
helper plus Collect(). Validates the growing-buffer / queried-size
patterns don't truncate, and pins Collect()'s label order so a future
accidental reorder surfaces as a test failure.
To keep these helpers testable without a live microphone, the
anonymous-namespace peak helpers in audio_capture.cpp are promoted
into audio_capture::detail.
Numbers
-------
before after
cli_args_debugger.cpp 2101 ~1270 (-40 %)
functions with CCN > 15 2 0
average CCN (main file) 6.4 3.4
highest CCN (main file) 50 7 (WindowProc)
test suites 12 14
Squash of PRs #3, #4, #5, #6, #7, #8.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Step 3 of the
cli_args_debugger.cppdecomposition (steps 1 and 2 were #3 and #4). Lifts the microphone pipeline out ofArgumentDebuggerWindowinto its own class inaudio_capture.{hpp,cpp}.What moves:
device_enumerator_,capture_device_,audio_client_,capture_client_,mix_format_,audio_event_,audio_thread_,mic_level_,mic_available_,audio_thread_running_,mic_name_.AudioCaptureThread(static entry),AudioCaptureThreadImpl,InitializeMicrophone,PollMicrophone.OnDestroyandCleanup.What consumers see:
ArgumentDebuggerWindownow holdsAudioCapture audio_capture_as a regular data member.Initialize()brings the pipeline up;Stop()takes it down cooperatively;IsAvailable()/Level()/Name()feed the level meter inRenderFrame.seh_wrapper.cppnow casts theLPVOIDparameter toAudioCapture*and callsThreadMain(). TheArgumentDebuggerWindowforward declaration in that file is gone.Split of the CCN-50 monster:
PollMicrophonewas the worst function in the codebase (CCN 50, 206 NLOC). It is split into:ResolveFormat— handlesWAVEFORMATEXTENSIBLEvs plainWAVEFORMATEX(CCN 6).PeakFloat32,PeakPcm16,PeakPcm24,PeakPcm32(all CCN 5).PeakForFormat— dispatch (CCN 9).PollOnce— sequencesGetNextPacketSize→GetBuffer→ peak →ReleaseBuffer(CCN 16, still the highest in the new file, but 3× lower than before).Numbers:
cli_args_debugger.cpp: 1846 → 1382 lines (−464).RenderFrame(CCN 21) remains over the static-analysis warning threshold in the main file; step 4 will address it.Test plan
build/windows-build: raw-cl build with new compilation unit.test(CTest): all existing tests (includingaudio_tests.cppandseh_exception_tests.cpp) compile and pass against the new class.Memory Safety Tests(ASan Debug): confirms no new leaks or use-after-free on the audio thread's cooperative shutdown path.