feat(tci): add mic_level handler (bridge existing setMicLevel to TCI)#3234
Conversation
TransmitModel already has setMicLevel(int) wired into FlexLib via commandReady (line 517-522 — same shape as setRfPower) but until now no TCI client could reach it: TciProtocol::handleCommand has no mic_level entry in its dispatcher, so the command silently no-ops. This adds: - TciProtocol::cmdMicLevel — cloned from cmdDrive (the closest sibling), global verb (no per-trx, mic is whole-radio), accepts spec-shorter `mic_level:N;` and legacy TRX-prefixed `mic_level:0,N;` for forward compatibility with clients that send the spec form for everything - Dispatcher entry beside the drive/tune_drive ones - mic_level: line in the init burst so newly-connected clients learn the current value immediately ## Motivation Unblocks the [aethersdr-ulanzi-plugin](https://github.com/nigelfenton/aethersdr-ulanzi-plugin) Mic Gain ▲▼ keys, which have been sending `mic_level:0,N;` correctly since v0.1.0 but landing on a missing dispatcher entry. After this merges, the plugin's mic-gain action set works immediately with no plugin-side change required. ## Verified - Compilation: TciProtocol.cpp.obj rebuilt clean (369/369 .obj files) - Live smoke test pending — local link blocked by an unrelated Windows SDK / mt.exe environment issue (LNK1158). Will validate by pressing the Ulanzi plugin's Mic Gain keys against this build + watching AE's mic-level slider move, before flipping the PR from draft to ready. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Live smoke test — passed ✅Verified on linux-aether (Ubuntu 24.04, Qt 6.4.2, built from Test method: Python websocket client sent three discrete (Excerpted from After each command, AE's MIC slider in the TX panel settled at the sent value. Result: handler works end-to-end — TCI parse → dispatcher → Both inbound formats accepted as designed:
Init burst also confirmed emitting the current Ready for review. |
Pre-existing latent bug surfaced by PR aethersdr#3234's mic_level TCI handler: setMicLevel updated m_micLevel and fired commandReady toward FlexLib, but never emitted micStateChanged — the signal PhoneCwApplet's mic slider listens to (PhoneCwApplet.cpp:667 → :728 m_micLevelSlider-> setValue(m_model->micLevel())). Result: programmatic setMicLevel() calls (now possible via the new TCI handler) updated the internal state and the radio, but the UI slider stayed at its last-painted value. The bug was previously invisible because nothing called setMicLevel from a non-UI path — the slider's own onValueChanged was the sole entry point and the widget refreshes itself on its own value change. Fix mirrors setSpeechProcessorLevel (line 539) and setRfPower (line 366) — both bound m_micLevel-style fields wrap their state mutation in an if-different guard + emit the appropriate state-changed signal before commandReady goes out. Live-verified by sending mic_level:0,30 / 0,60 via TCI and watching the PhoneCwApplet slider follow each value. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Updated — additional fix in
|
Bonus fix in
|
Three-platform build verification — all green ✅
The CMakeLists.txt change is gated by Ready for review. |
There was a problem hiding this comment.
Thanks @nigelfenton — the TCI bridge itself is a clean clone of cmdDrive and the init-burst line is the right shape. Just a couple of scope/disclosure concerns since two of the four files changed aren't covered by the PR body (which says "32-line diff across 2 files"):
CMakeLists.txt — unrelated MSVC link-option fix
The /MANIFEST:EMBED + /MANIFESTINPUT change has nothing to do with mic_level and isn't mentioned in the PR description. It's a reasonable fix on its own merits (the comment about LNK1220 on newer MSVC toolsets is plausible), but it should land as a separate PR so the build-system change can be reviewed against the Windows toolchain matrix independently. It also looks like it may overlap with whatever's going on in #3225 ("Embed DFNR model and set Store identity") which just merged — worth confirming there's no conflict with the manifest handling there.
src/models/TransmitModel.cpp — setMicLevel now emits micStateChanged
This is actually a behavior change, not just a refactor:
- The new equality guard is fine and matches the pattern of
setActiveMicProfile/setMicSelectionetc. - Adding
emit micStateChanged()is necessary (otherwise the AE UI mic slider wouldn't reflect TCI-initiated changes —PhoneCwApplet.cpp:667andMainWindow.cpp:2420both bind to that signal), so it's the right call — but please call it out explicitly in the PR description. Reviewers shouldn't have to chase down why a model setter changed in a "dispatcher entry" PR.
cmdMicLevel body — looks good
Indexing is safe (early-return on empty, then [0] if size==1 else [1]). Stashing the unbounded lvl into m_pendingNotification means a client sending mic_level:0,200; will see a notification echoing 200 even though the radio got 100, but that exactly mirrors cmdDrive/cmdTuneDrive — same pre-existing quirk, not yours to fix here.
Nit on init-burst symmetry: drive/tune_drive use txTrx in the format string even though they're global verbs (legacy form). mic_level correctly uses the spec form with no trx. That's the right choice — just noting that strict-spec clients will now see one new global verb in an init burst otherwise dominated by the legacy TRX-prefixed form. Fine.
Recommend splitting out the CMake change and updating the description to mention the TransmitModel signal addition; the TCI work itself is good to go once those are sorted.
🤖 aethersdr-agent · cost: $7.3603 · model: claude-opus-4-7
2aa0347 to
19a95f5
Compare
|
Thanks for the thorough review @aethersdr-agent — both points actioned: 1. CMake fix split out → #3237. The On the #3225 overlap you flagged: this branch was cut from 2. PR description updated to call out the Good catch on both — agreed the build-system change didn't belong here, and the model-setter change deserved disclosure rather than making a reviewer chase it down. Also noted (and agreed, not changing here): the unbounded- |
|
Thanks @nigelfenton — both follow-ups look good. Re-reviewed against current Scope split (CMake → #3237): ✅ Confirmed. The right call. As a separate PR #3237 can be reviewed against the Windows toolchain matrix on its own merits (LNK1220 wording, MSVC toolset version gates, etc.) without coupling to a TCI behaviour change. And as you noted, the branches don't conflict — different parts of Description disclosure of the I re-read the current void TransmitModel::setMicLevel(int level)
{
level = qBound(0, level, 100);
m_micLevel = level;
emit commandReady(QString("transmit set miclevel=%1").arg(level));
}Confirmed — no equality guard, no On the unbounded- Scope now feels right: 32 lines TCI handler + 3 lines model signal fix, both intrinsically linked. LGTM from my end — handing back to a human maintainer for the merge call. 73, Jeremy KK7GWY & Claude (AI dev partner) 🤖 aethersdr-agent · cost: $2.6201 · model: claude-opus-4-7 |
jensenpat
left a comment
There was a problem hiding this comment.
Thanks Nigel for TCI protocol support for Mic gain
## Summary `CMakeLists.txt:1062` passes `/MANIFESTINPUT:<path>` to the MSVC linker but not the required `/MANIFEST:EMBED`. Per MSVC docs the former is only valid alongside the latter. Older toolsets accepted the lone flag with a warning; newer ones (VS 18.x / MSVC 14.50+) fail hard: ``` LINK : fatal error LNK1220: 'MANIFESTINPUT' requires '/MANIFEST:EMBED' specification ``` One-line fix (4 lines incl. comment): add `/MANIFEST:EMBED` to the `target_link_options` call so the manifest is embedded inline (the intended behaviour anyway). ## Why a separate PR Split out of #3234 (the `mic_level` TCI handler) on reviewer request — this is an unrelated build-system change and deserves independent review against the Windows toolchain matrix. ## Context - Hit on a fresh Windows build with **Visual Studio 18 Build Tools, toolset 14.50.35717**, Windows SDK 10.0.26100.0. - This branch is cut from `44eaef7c` (the #3225 "Embed DFNR model and set Store identity" merge), so it sits on top of the latest Windows/manifest changes — no conflict with #3225's Store-identity handling (different part of the file). - CI presumably uses an older MSVC, which is why this has been latent. ## Test plan - [x] Windows local build (MSVC 14.50) links clean with the fix; fails with LNK1220 without it - [x] No effect on Linux/macOS — the change is inside the existing `if(MSVC)` block - [ ] CI Windows matrix (auto) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Problem `TransmitModel::setMonGainSb()` updates `m_monGainSb` and emits `commandReady` but never emits `micStateChanged()`. A UI slider bound to monitor gain via the model signal won't repaint when the value is changed by MIDI controller, keyboard shortcut, or radio echo — until an unrelated event happens to trigger a redraw. Same class of gap as #2084 (VOX setters) and the `setMicLevel` part of #2180. **Note:** `setMicLevel` was fixed concurrently in PR #3234 (with a change guard). This PR addresses the remaining gap — `setMonGainSb`. ## Fix Add `emit micStateChanged()` after updating `m_monGainSb`, consistent with how all other mic-state setters behave. ## Setter audit (per #2180) | Setter | Signal emitted? | |---|---| | `setMicSelection` | ✅ `micStateChanged()` | | `setMicLevel` | ✅ `micStateChanged()` — fixed in #3234 | | `setSpeechProcessorEnable` | ✅ `micStateChanged()` | | `setSpeechProcessorLevel` | ✅ `micStateChanged()` | | `setMonGainSb` | ❌ **missing** — fixed here | | `setMonGainCw` | ✅ `phoneStateChanged()` | | `setMonPanCw` | ✅ `phoneStateChanged()` | Fixes #2180
Summary
Adds a
mic_levelcommand toTciProtocol, bridging the existingTransmitModel::setMicLevel(int)setter to TCI clients. Until nowmic_level:commands sent over TCI silently no-op'd —handleCommand's dispatcher had no entry for them.3 files changed (+36 / -1):
src/core/TciProtocol.hcmdMicLevel(const QStringList&, bool)src/core/TciProtocol.cppcmdMicLevelbody (cloned fromcmdDrive), init-burst linesrc/models/TransmitModel.cppsetMicLevelnow emitsmicStateChanged+ equality guard (see below)The
TransmitModel::setMicLevelchange (behaviour change — calling it out explicitly)setMicLevelpreviously updatedm_micLeveland emittedcommandReadytoward FlexLib, but never emittedmicStateChanged— the signal the mic-level slider binds to (PhoneCwApplet.cpp:667→:728, alsoMainWindow.cpp:2420). This was invisible until now because nothing calledsetMicLevelfrom a non-UI path; the slider's ownonValueChangedwas the only entry. OncecmdMicLevelstarted calling it programmatically over TCI, the gap surfaced: the radio + internal state updated, but the UI slider froze.Fix mirrors
setSpeechProcessorLevel/setRfPower: equality guard + emit the state-changed signal beforecommandReady. Without it the TCI handler is functionally half-broken (no UI feedback), so it belongs in this PR.cmdMicLevelNear-exact clone of
cmdDrive— global verb (no per-trx; mic is whole-radio), accepts spec formmic_level:N;and legacy TRX-prefixedmic_level:0,N;(trx ignored). Init burst gains amic_level:<value>;line so clients learn the current value on connect.Split out
The unrelated
/MANIFEST:EMBEDCMakeLists build fix that was previously bundled here is now #3237 (separate PR, reviewed against the Windows toolchain matrix independently). This PR is purely themic_levelTCI feature + its required UI-signal fix.Verified — three platforms
mic_level:0,30/60/90over TCI, AE mic slider tracked each value. Confirmed end-to-end: parse → dispatcher →setMicLevel→micStateChanged→ UI repaint.Test plan
🤖 Generated with Claude Code