Skip to content

feat(midi): register QSO Record and Playback as learnable MIDI params (#2844)#2845

Merged
jensenpat merged 2 commits into
aethersdr:mainfrom
M8WLO:feature/midi-qso-record-playback
May 22, 2026
Merged

feat(midi): register QSO Record and Playback as learnable MIDI params (#2844)#2845
jensenpat merged 2 commits into
aethersdr:mainfrom
M8WLO:feature/midi-qso-record-playback

Conversation

@M8WLO
Copy link
Copy Markdown
Contributor

@M8WLO M8WLO commented May 18, 2026

Summary

  • Registers qso.record and qso.play as MIDI-learnable parameters in MainWindow::registerMidiParams()
  • Both appear in the MIDI Mapping dialog under the Global category
  • Type P::Toggle — MIDI note-on (value > 0.5) starts, note-off stops; momentary pads work correctly in latch mode

What was missing

registerMidiParams() (src/gui/MainWindow.cpp) had ~80 entries covering RX, TX, Phone/CW, EQ, Mode, Band, and Global controls, but no entries for the QSO recorder. m_qsoRecorder->startRecording() etc. were only reachable via the VFO widget buttons — invisible to the MIDI dispatch path entirely.

Implementation

reg("qso.record", "QSO Record", "Global", P::Toggle, 0, 1,
    [this](float v) {
        if (!m_qsoRecorder) return;
        if (v > 0.5f) m_qsoRecorder->startRecording();
        else          m_qsoRecorder->stopRecording();
    },
    [this]() -> float {
        return (m_qsoRecorder && m_qsoRecorder->isRecording()) ? 1.0f : 0.0f;
    });

reg("qso.play", "QSO Playback", "Global", P::Toggle, 0, 1,
    [this](float v) {
        if (!m_qsoRecorder) return;
        if (v > 0.5f) m_qsoRecorder->startPlayback();
        else          m_qsoRecorder->stopPlayback();
    },
    [this]() -> float {
        return (m_qsoRecorder && m_qsoRecorder->isPlaying()) ? 1.0f : 0.0f;
    });

Test plan

  • Open MIDI Mapping dialog — confirm QSO Record and QSO Playback appear under Global category
  • Enter learn mode for QSO Record; press a MIDI pad — confirm binding saves
  • Trigger the bound control — confirm recording starts (VFO ⏺ button lights up)
  • Trigger again (note-off) or trigger the Playback binding — confirm correct stop/start transition
  • Restart AetherSDR — confirm bindings reload and still function
  • Confirm no regression to existing MIDI params

Closes #2844
Related: #2841 (companion TCI gap fixed in #2842)

🤖 Generated with Claude Code

@M8WLO M8WLO requested a review from ten9876 as a code owner May 18, 2026 10:16
…aethersdr#2844)

Adds two P::Toggle entries to registerMidiParams() that mirror the exact
dual-routing used by the VFO record/play buttons (MainWindow.cpp:11413-11443):
RecordingMode=="Client" routes through QsoRecorder; any other mode routes
through SliceModel::setRecordOn / setPlayOn (radio-side), so a MIDI button
behaves identically to the on-screen ⏺ / ▶ widgets in both recording modes.

  global.qsoRecord  — "QSO Record"   (Global category)
  global.qsoPlay    — "QSO Playback" (Global category)

Getter lambdas reflect live state so bidirectional MIDI controllers
(Akai APC, Behringer X-Touch, etc.) can keep LEDs in sync.

Closes aethersdr#2844
Related: aethersdr#2841 / aethersdr#2842

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@aethersdr-agent aethersdr-agent Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @M8WLO — nice, focused gap-fill. The two new reg(...) entries faithfully mirror the existing VFO ⏺/▶ routing at MainWindow.cpp:11413-11443, including the RecordingMode=="Client" switch between QsoRecorder and SliceModel::setRecordOn/setPlayOn. That's exactly the right shape — anything else would have created a second source of truth for record/play behaviour.

Spot checks:

  • Naming global.qsoRecord / global.qsoPlay and the "Global" category line up with the surrounding entries (global.masterMute, global.txButton, etc.) at MainWindow.cpp:14694-14702.
  • AppSettings::instance().value("RecordingMode", "Radio") matches the convention (no stray QSettings).
  • m_qsoRecorder is allocated in the ctor at MainWindow.cpp:1302 and parented to this, so the unguarded calls in the setters are consistent with the existing VFO handler. The getter's defensive m_qsoRecorder && check is fine — a slight asymmetry but it just defaults the read to 0.
  • activeSlice() returning nullptr is correctly handled by if (auto* s = activeSlice()) on the radio-side branch.

One small note (not blocking): the PR body has a template-substitution glitch (${PR_DIFF}${PR_DIFF} where && should be), and the snippet shown there isn't quite the code that was actually committed. The committed diff is the correct version — the body just looks confusing if anyone reads it standalone. Worth tidying the description when convenient.

LGTM otherwise.

Copy link
Copy Markdown
Collaborator

@jensenpat jensenpat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for new MIDI recording controls

@jensenpat jensenpat enabled auto-merge (squash) May 22, 2026 00:13
@jensenpat jensenpat merged commit 78af7cd into aethersdr:main May 22, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MIDI learn: QSO Record and Playback not registered as learnable parameters

2 participants