Skip to content

Qt6 migration and CI Actions#1

Open
naelolaiz wants to merge 7 commits into
mainfrom
qt6_migration_and_ci
Open

Qt6 migration and CI Actions#1
naelolaiz wants to merge 7 commits into
mainfrom
qt6_migration_and_ci

Conversation

@naelolaiz

@naelolaiz naelolaiz commented May 20, 2026

Copy link
Copy Markdown
Member

Summary

Brings the CLAM tree up to a modern build: top-level CMake (replacing SCons), C++17 across all libraries, and a Qt6 port of NetworkEditor and SMSTools. Adds a GitHub Actions workflow that builds everything on ubuntu-latest.

What's in here

  • CMake build for CLAM core/processing/audioio. Top-level CMakeLists.txt opts in via CLAM_BUILD_LIBRARIES=ON (default). Optional features (CLAM_WITH_LV2, WITH_ID3, WITH_JACK, WITH_MAD, WITH_PORTAUDIO, WITH_ALSA, …) hard-fail at configure time when ON but the dep is missing, with the symmetric pattern across all backends (LV2 used to slip through configure and explode at compile — fixed).

  • NetworkEditor ported to Qt6, built by the same CMake (CLAM_BUILD_GUI_APPS=ON). Targets produced: clam_qtmonitors (the reusable monitor widget library) and the NetworkEditor executable. The legacy <QtGui/QFoo> / <QtOpenGL/QGLWidget> / <QtSvg/QSvgWidget> includes were rewritten to their Qt6 paths across ~87 sources; the temporary compat-shim header tree (cmake/ClamQtCompat.cmake) introduced mid-port is gone now that the includes are direct.

  • SMSTools ported to Qt6 as a fresh tree (CLAM_BUILD_SMSTOOLS=ON). Replaces the Qt3Support-era SMSTools-qt4/. All Q3* widgets swapped for modern equivalents; Qt3-era helpers (QPalette shimmed via a small PaletteHelpers.hxx, QToolTip::addsetToolTip, setPixmapsetIcon, QFontMetrics::widthhorizontalAdvance, QGLWidgetQOpenGLWidget, QTimer::start(ms, true)setSingleShot + start(ms), etc.). UI signal/slot wiring moved into the dialog constructors so Qt6 uic's typed-pointer connect() resolves against the form subclass. Three executables produced: SMSConsole, SMSBatch (no Qt), and SMSTools (Qt GUI).

  • Graceful error handling. Engine calls in SMSTools's Qt slots (LoadConfiguration, LoadAnalysis, LoadTransformationScore, the Store* variants, Analyze, Transform, Synthesize, ExtractMelody) are wrapped in a small guard() helper that catches std::exception and surfaces the message via QMessageBox::critical, instead of letting an uncaught CLAM::Err terminate() the process.

  • CI workflow. .github/workflows/build.yml installs the project's apt build deps on ubuntu-latest (xerces-c, fftw3, sndfile, vorbis, mad, id3, lv2, jack, portaudio, alsa, GLU, LADSPA SDK, Qt6 base + svg + opengl + tools), runs cmake configure with all options ON, and builds. A trailing step lists the produced binaries so a green run visibly proves all four (NetworkEditor, SMSTools, SMSBatch, SMSConsole) linked.

  • Warning sweep. The Ubuntu CI build went from ~146 warnings to clean. Three real bugs were uncovered in the process:

    • Stats.hxx: if (mCenterOfGravities[order-1] = NULL) (assignment, always-false) → == nullptr. The per-order CenterOfGravity was never actually being lazily allocated.
    • Navigator.hxx: header guard typo #ifndef __NAVIGATOR__ / #define __NAVEGATOR__ corrected so the include guard actually guards.
    • ProcessingBox::mousePressEvent: precedence trap !event->modifiers() & Qt::ControlModifier (parsed as (!modifiers()) & Ctrl) replaced with the already-defined controlPressed flag.

    The cleanup intentionally preserves "suspicious-vestige" variables with [[maybe_unused]] + a brief comment rather than silently deleting them — the warning is gone but the signal for future readers stays.

Notes

  • The whole tree builds under C++17 with Qt6 only. Dynamic exception specifications (throw (X)) were removed because the language drops them in C++17; the only observable side-effect is that exceptions other than the declared one no longer trigger terminate() at the function boundary (they propagate normally and are caught by the SMSTools guard() wrapper described above).
  • register storage-class qualifiers in CLAM_Math.hxx were removed; modern compilers had been ignoring the keyword for decades and C++17 made the spelling illegal.
  • The old SMSTools-qt4/ tree has been removed and replaced by SMSTools/.
  • Annotator/, Voice2MIDI/ and a couple of other apps still rely on SCons and have not been touched here. See CMAKE-MIGRATION.md for the up-to-date status.

Test plan

  • CI: .github/workflows/build.yml passes on ubuntu-latest.
  • Local: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCLAM_BUILD_LIBRARIES=ON -DCLAM_BUILD_GUI_APPS=ON -DCLAM_BUILD_SMSTOOLS=ON && cmake --build build succeeds with zero compiler warnings.
  • ./build/NetworkEditor/NetworkEditor launches and lets you drop a Processing box; port-hover tooltips show the right type name (the prior dangling-pointer garbage is gone); the box drags from anywhere on its body, not only the title strip.
  • ./build/SMSTools/SMSTools launches; loads SMSTools/example-data/Elvis-config.xml, runs Analyze → Apply (any transformation score from clam_data/SMSTests/*-transf.xml) → Synthesize, and surfaces XML/load errors as QMessageBox dialogs instead of aborting.

naelolaiz added 5 commits May 21, 2026 01:11
Add a top-level CMake entrypoint for the imported CLAM tree, build the core/audio/processing libraries, and enable the NetworkEditor Qt targets with Qt6 compatibility headers. Remove C++ constructs rejected by C++17 and update the NetworkEditor/monitor sources for Qt6 APIs.
Replace SMSTools-qt4/ with a Qt6-only SMSTools/ tree built by CMake.
All Q3* widgets are swapped for their modern equivalents (Q3FileDialog,
Q3ListBox, Q3Frame, Q3Process, Q3WhatsThis, Q3PopupMenu, Q3ButtonGroup,
Q3TextBrowser, Q3MainWindow), removed-in-Qt6 helpers are replaced
(QGLColormap is shimmed in QtPalette.hxx, setPaletteBackgroundColor /
setPaletteForegroundColor become a small QPalette helper in
PaletteHelpers.hxx, QToolTip::add/remove become setToolTip,
QPushButton::setPixmap becomes setIcon, QFontMetrics::width becomes
horizontalAdvance, QString::sprintf becomes QString::asprintf,
QPainter::resetXForm becomes resetTransform, QGLWidget OpenGL widgets
inherit from QOpenGLWidget), and Qt3-style API calls are replaced
(ascii -> toStdString, setOn/isOn -> setChecked/isChecked,
setMargin -> setContentsMargins, currentItem/setCurrentItem ->
currentIndex/setCurrentIndex on QComboBox, setMaxValue/maxValue ->
setMaximum/maximum, setSteps -> setSingleStep + setPageStep,
QTimer::start(ms,true) -> setSingleShot + start(ms), reparent ->
setParent + move, QGridLayout addMultiCell* -> addLayout/addWidget,
new QHBoxLayout(parentLayout) -> new QHBoxLayout + parent->addLayout).

uic-generated connections in QtSMSTools.ui, SMSConfigDlg.ui and
ScoreEditorDlg.ui have been moved into the dialog constructors so the
typed-pointer connect() resolves against the QtSMS form class rather
than QMainWindow / QDialog.

SMSTools/CMakeLists.txt produces three executables (SMSConsole and
SMSBatch with no Qt dependency; SMSTools as the Qt GUI) plus three
static helper libraries. The top-level build opts in via
-DCLAM_BUILD_SMSTOOLS=ON. CMAKE-MIGRATION.md is updated to describe
the new target and drop SMSTools from the remaining-work list.

Assisted by Claude Opus 4.7
Wrap each engine call (LoadConfiguration / LoadAnalysis /
LoadTransformationScore / Store* / Analyze / Transform / Synthesize /
ExtractMelody) in a small guard() helper that catches std::exception
and surfaces the message via QMessageBox::critical instead of letting
an unhandled CLAM::Err abort the process.

The previous behaviour aborted the GUI with a terminate() backtrace
when, for example, a transformation-score XML used an unexpected
element.

Assisted by Claude Opus 4.7
Rewrite ~234 legacy Qt3-style includes across 87 NetworkEditor sources
to their Qt6 forms:
  <QtGui/QFoo>              -> <QFoo>
  <QtOpenGL/QGLWidget>      -> <QOpenGLWidget>
  <QtSvg/QSvgWidget>        -> <QSvgWidget>
  <QtDesigner/QDesignerExportWidget> -> <QtUiPlugin/QDesignerExportWidget>

Inheriting widgets renamed accordingly (QGLWidget -> QOpenGLWidget in
Spectrogram, Tonnetz, KeySpace, QFirstPerson).

With every consumer migrated, cmake/ClamQtCompat.cmake and the
clam_generate_qt_compat_headers() calls in the top-level, NetworkEditor
and SMSTools CMakeLists are removed; the build no longer generates the
fake QtGui/QtOpenGL/QtSvg/QtDesigner forward-header tree.

Assisted by Claude Opus 4.7
Add a GitHub Actions workflow that, on push / PR / manual dispatch,
installs the project's apt build dependencies on ubuntu-latest
(xerces-c, fftw3, sndfile, vorbis, mad, jack, portaudio, alsa, GLU,
LADSPA SDK, Qt6 base + svg + opengl + tools), then runs:

  cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release \
    -DCLAM_BUILD_LIBRARIES=ON -DCLAM_BUILD_GUI_APPS=ON \
    -DCLAM_BUILD_SMSTOOLS=ON
  cmake --build build

so both the GUI applications (NetworkEditor, SMSTools) and the
non-Qt console tools (SMSBatch, SMSConsole) are exercised by CI.

Assisted by Claude Opus 4.7
@naelolaiz
naelolaiz force-pushed the qt6_migration_and_ci branch 2 times, most recently from 4a3ef8d to a387d43 Compare May 21, 2026 00:21
naelolaiz added 2 commits May 21, 2026 03:54
…STools

Address the warning categories surfaced by the Ubuntu CI build.

Real bugs uncovered by warnings:
- Stats.hxx: `if (mCenterOfGravities[order-1] = NULL)` was an
  assignment (always-false truth value) so the body never ran and the
  per-order CenterOfGravity was never lazily allocated. Switched to
  `== nullptr` and use nullptr in the surrounding loop too.
- Navigator.hxx: header guard typo `#ifndef __NAVIGATOR__ / #define
  __NAVEGATOR__` corrected so the include guard actually works.
- ProcessingBox::mousePressEvent had the precedence trap
  `!event->modifiers() & Qt::ControlModifier`; now uses the existing
  controlPressed flag.

Unused / unused-but-set variables: marked [[maybe_unused]] with a
brief comment when they look like vestiges of an incomplete feature
(Vocoder bin-domain partners, SpectralCombDelta/Partializer outMag,
Deesser maxMag, SpectralNotch harmonic, CleanTracks contAt,
ChordSegmentator underMaxIndex, AudioBufferMixer portSize,
BufferedSDIFFileReader iterationsSinceLastExecution, SMSBase
"old segmentator" defaults, KeySpace::DrawLabels missing-render-call
hint, TextBox::itemChange dead local, etc.). CircularPeakPicking
restored `c = y0` and uses `c` in the ymax formula so code and comment
agree.

catch-value: every `catch (Foo e)` → `catch (const Foo&)` in CLAM core
(Flags, LadspaNetworkExporter, BinaryAudioOp, SegmentSMSMorph,
LV2NetworkPlayer) and SMSTools (SDIFSerializer, SMSBase, SMS, SMSBatch).

sign-compare: signed/unsigned loop variables converted to `auto` so
the type matches the size() / Size() expression they compare against
(AudioMixer, AudioBufferMixer, EnvelopeExtractor, FDCombFilter,
LoopingSDIFFileReader). ProcessingBox::getItemRegion uses
static_cast<int> on the unsigned member counts;
BufferedSDIFFileReader and MultiChannelAudioFileReader use std::size_t
casts.

unused-function: ProcessingBox debug-helper statics and
vmqt/Message.hxx tagged [[maybe_unused]].

reorder: SDIFOut constructor's initialiser list reordered to match the
header (and mpFile = nullptr).

parentheses: BufferedSDIFFileReader::Configure assignment wrapped in
explicit parens.

return-type: SDIFOut::Do() returns false explicitly with a TODO noting
the stream-mode path was never finished.

switch: QSynthKnob::mouseMoveEvent's switch handles QDialMode.

format-security: MIDIReader::Error uses printf("%s", str).

unused-result: NetworkEditor main checks QTranslator::load before
installing the translator.

extern-init: LadspaNetworkExporter's clam_library_marker wrapped in an
extern "C" block.

overloaded-virtual: added `using Processing::Do;` /
`using FrameTransformation::Do;` /
`using SMSBase::StoreOutputSound{,Residual,Sinusoidal};` so derived
classes do not silently hide their base-class overloads.

deprecated-declarations:
- QKeyCombination: Qt::CTRL + Qt::Key_X → Qt::CTRL | Qt::Key_X across
  RichTextEditor (8 shortcuts).
- QTextCharFormat::setFontFamily → setFontFamilies({...}).
- QMessageBox positional-int constructor and 8-arg static question()
  replaced with the StandardButtons overloads (Message.hxx,
  ShowMessage.cxx, MainWindow::askUserSaveChanges).
- QMouseEvent::x()/y() → position() (PlotController::GetXY,
  DisplaySurface).

AutoGen: removed the empty NetworkEditor/src/widgets/Configurator.cxx
placeholder. Configurator is a header-only Q_OBJECT class; AUTOMOC
picks the header up directly, so the empty .cxx had no purpose since
its 2011 SVN move.

Also dropped the C-style functional casts I introduced earlier in the
Qt6 port (BPFEditorDisplaySurface, DisplaySurface, ScoreEditorDlg,
Ruler) in favour of static_cast<T>(...), const auto and structured
bindings; factored the duplicated mouse-event-to-view-coords math in
BPFEditorDisplaySurface into a single helper.

Assisted by Claude Opus 4.7
Summarises what CLAM is, lists the libraries (clam_core / clam_processing /
clam_audioio) and the four applications produced by this branch's build
(NetworkEditor, SMSTools, SMSBatch, SMSConsole), and documents:

- the apt dependency list that mirrors the CI workflow
- cmake / cmake --build incantations for the three build profiles
  (libraries only / +NetworkEditor / +SMSTools)
- the CLAM_BUILD_* and CLAM_WITH_* CMake option matrix
- a manual quick-test recipe for the SMSTools GUI against the bundled
  example-data
- pointers to the CI workflow and to CMAKE-MIGRATION.md
@naelolaiz
naelolaiz force-pushed the qt6_migration_and_ci branch from a387d43 to 3c6ce9b Compare May 21, 2026 01:58
@vokimon

vokimon commented Jun 3, 2026

Copy link
Copy Markdown
Member

@naelolaiz, should we do a chat?

@naelolaiz

Copy link
Copy Markdown
Member Author

Hi @vokimon!,
Sure!

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.

2 participants