From 919f9a1c0aba57f278de70b5621292f607e4fa97 Mon Sep 17 00:00:00 2001 From: Fawn Date: Sun, 16 Mar 2025 18:04:53 -0600 Subject: [PATCH 01/62] Upgrade Amplifier plugin PNG assets with SVG (#7770) Replaces the .png raster assets for the native Amplifier plugin with .svg vector assets. --- plugins/Amplifier/AmplifierControlDialog.cpp | 16 ++++++++-------- plugins/Amplifier/CMakeLists.txt | 2 +- plugins/Amplifier/artwork.png | Bin 7739 -> 0 bytes plugins/Amplifier/artwork.svg | 13 +++++++++++++ plugins/Amplifier/logo.png | Bin 774 -> 0 bytes plugins/Amplifier/logo.svg | 3 +++ 6 files changed, 25 insertions(+), 9 deletions(-) delete mode 100644 plugins/Amplifier/artwork.png create mode 100644 plugins/Amplifier/artwork.svg delete mode 100644 plugins/Amplifier/logo.png create mode 100644 plugins/Amplifier/logo.svg diff --git a/plugins/Amplifier/AmplifierControlDialog.cpp b/plugins/Amplifier/AmplifierControlDialog.cpp index 1fbc3729a..7d303d0d1 100644 --- a/plugins/Amplifier/AmplifierControlDialog.cpp +++ b/plugins/Amplifier/AmplifierControlDialog.cpp @@ -42,14 +42,14 @@ AmplifierControlDialog::AmplifierControlDialog(AmplifierControls* controls) : auto makeKnob = [this](int x, int y, const QString& label, const QString& hintText, const QString& unit, FloatModel* model, bool isVolume) { - Knob* newKnob = new Knob(KnobType::Bright26, this); - newKnob->move(x, y); - newKnob->setModel(model); - newKnob->setLabel(label); - newKnob->setHintText(hintText, unit); - newKnob->setVolumeKnob(isVolume); - return newKnob; - }; + Knob* newKnob = new Knob(KnobType::Bright26, this); + newKnob->move(x, y); + newKnob->setModel(model); + newKnob->setLabel(label); + newKnob->setHintText(hintText, unit); + newKnob->setVolumeKnob(isVolume); + return newKnob; + }; makeKnob(16, 10, tr("VOL"), tr("Volume:"), "%", &controls->m_volumeModel, true); makeKnob(57, 10, tr("PAN"), tr("Panning:"), "%", &controls->m_panModel, false); diff --git a/plugins/Amplifier/CMakeLists.txt b/plugins/Amplifier/CMakeLists.txt index 3e57ba5ef..2d465fc55 100644 --- a/plugins/Amplifier/CMakeLists.txt +++ b/plugins/Amplifier/CMakeLists.txt @@ -1,3 +1,3 @@ INCLUDE(BuildPlugin) -BUILD_PLUGIN(amplifier Amplifier.cpp AmplifierControls.cpp AmplifierControlDialog.cpp MOCFILES AmplifierControls.h AmplifierControlDialog.h EMBEDDED_RESOURCES artwork.png logo.png) +BUILD_PLUGIN(amplifier Amplifier.cpp AmplifierControls.cpp AmplifierControlDialog.cpp MOCFILES AmplifierControls.h AmplifierControlDialog.h EMBEDDED_RESOURCES artwork.svg logo.svg) diff --git a/plugins/Amplifier/artwork.png b/plugins/Amplifier/artwork.png deleted file mode 100644 index 5598b32db47134819b686a2f2e282c297fe78c06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7739 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 Bz%u{< diff --git a/plugins/Amplifier/artwork.svg b/plugins/Amplifier/artwork.svg new file mode 100644 index 000000000..df574cfda --- /dev/null +++ b/plugins/Amplifier/artwork.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/plugins/Amplifier/logo.png b/plugins/Amplifier/logo.png deleted file mode 100644 index 9340da708dd79ed97111eb535f51b81a91d6a15b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 774 zcmV+h1Nr=kP)7WEc)VQ)zLm`B#lSD% z0Wg;#IH?7|3b0p&fj_`2;A{cm@zx+(Ge?s$@EN$6LtMjg>;+(boCbaXw;ja=b6mQ?gRp67Yf18(18niCN0v&eY^{Cr&#;#IcF{ks?!* z&o!_q>9xbSw`QytRI!M?zP_o#K-8ExJaV?vi znPt?~0KhTu23U+Gy8^Tw;^SzWSet9n + + From f72ae32fd3705848ef490db15db46d8f01b25fea Mon Sep 17 00:00:00 2001 From: szeli1 <143485814+szeli1@users.noreply.github.com> Date: Mon, 17 Mar 2025 20:35:28 +0100 Subject: [PATCH 02/62] Ensure hidden full-screen windows are restored in the correct position (#7752) Fix bug introduced with #7595 where drag-moving the MDI canvas background would break the show/hide behavior of a fullscreen window. Closes #7751 --- src/gui/MainWindow.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 275ef4d29..7ca1387bf 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -1642,7 +1642,16 @@ void MainWindow::MovableQMdiArea::mouseMoveEvent(QMouseEvent* event) scrollY = scrollY < 0 && minY >= minYBoundary ? 0 : scrollY; scrollY = scrollY > 0 && maxY <= maxYBoundary ? 0 : scrollY; - scrollContentsBy(-scrollX, -scrollY); + for (auto* curWindow : subWindows) + { + // if widgets are maximized, then they shouldn't be moved + // moving a maximized window's normalGeometry is not implemented because of difficulties + if (curWindow->isMaximized() == false) + { + curWindow->move(curWindow->x() - scrollX, curWindow->y() - scrollY); + } + } + m_lastX = event->x(); m_lastY = event->y(); } From 10c428b7a09512ac2c769fa62e92899b3d1ccc41 Mon Sep 17 00:00:00 2001 From: Fawn Date: Tue, 18 Mar 2025 21:04:10 -0600 Subject: [PATCH 03/62] Upgrade Bass Booster plugin PNG assets with SVG (#7771) --- plugins/BassBooster/CMakeLists.txt | 2 +- plugins/BassBooster/artwork.png | Bin 324 -> 0 bytes plugins/BassBooster/artwork.svg | 10 ++++++++++ plugins/BassBooster/logo.png | Bin 774 -> 0 bytes plugins/BassBooster/logo.svg | 3 +++ 5 files changed, 14 insertions(+), 1 deletion(-) delete mode 100644 plugins/BassBooster/artwork.png create mode 100644 plugins/BassBooster/artwork.svg delete mode 100644 plugins/BassBooster/logo.png create mode 100644 plugins/BassBooster/logo.svg diff --git a/plugins/BassBooster/CMakeLists.txt b/plugins/BassBooster/CMakeLists.txt index 35bfb5049..955a6584c 100644 --- a/plugins/BassBooster/CMakeLists.txt +++ b/plugins/BassBooster/CMakeLists.txt @@ -1,3 +1,3 @@ INCLUDE(BuildPlugin) -BUILD_PLUGIN(bassbooster BassBooster.cpp BassBoosterControls.cpp BassBoosterControlDialog.cpp MOCFILES BassBoosterControls.h BassBoosterControlDialog.h EMBEDDED_RESOURCES artwork.png logo.png) +BUILD_PLUGIN(bassbooster BassBooster.cpp BassBoosterControls.cpp BassBoosterControlDialog.cpp MOCFILES BassBoosterControls.h BassBoosterControlDialog.h EMBEDDED_RESOURCES artwork.svg logo.svg) diff --git a/plugins/BassBooster/artwork.png b/plugins/BassBooster/artwork.png deleted file mode 100644 index c16b8ca2bc537c0eec4bf3e41ba840d187e68ed7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 324 zcmeAS@N?(olHy`uVBq!ia0vp^6+mpm!3HGPuB}@Eq*#ibJVQ8upoSx*1IXtr@Q5r1 zs=p4xj7}P}K{_N$Tq8Eakt zG3V{|M$Q8YJPeM}G7cIg|9#(zxK_MV6PV{wwD$Sk<~6^+F#ni-ps+Z{zU1bV?PtYh z)eGhvC}cW*bJ1g;f;q;T=U9$&$^gZ5tzax`3m^+34N}M{^W3Lvv+MDniypJ|30wF* zuxNaobFuK<1B>_)ut}UU>D_(C2MfRKbLh*2~7au3wmk* diff --git a/plugins/BassBooster/artwork.svg b/plugins/BassBooster/artwork.svg new file mode 100644 index 000000000..f98447e18 --- /dev/null +++ b/plugins/BassBooster/artwork.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/plugins/BassBooster/logo.png b/plugins/BassBooster/logo.png deleted file mode 100644 index 9340da708dd79ed97111eb535f51b81a91d6a15b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 774 zcmV+h1Nr=kP)7WEc)VQ)zLm`B#lSD% z0Wg;#IH?7|3b0p&fj_`2;A{cm@zx+(Ge?s$@EN$6LtMjg>;+(boCbaXw;ja=b6mQ?gRp67Yf18(18niCN0v&eY^{Cr&#;#IcF{ks?!* z&o!_q>9xbSw`QytRI!M?zP_o#K-8ExJaV?vi znPt?~0KhTu23U+Gy8^Tw;^SzWSet9n + + From 953c6843cd8c28d614cb50a66da6da98c714c55c Mon Sep 17 00:00:00 2001 From: Fawn Date: Tue, 18 Mar 2025 21:07:02 -0600 Subject: [PATCH 04/62] Upgrade ReverbSC plugin PNG assets to SVG (#7777) --- plugins/ReverbSC/CMakeLists.txt | 2 +- plugins/ReverbSC/artwork.png | Bin 262 -> 0 bytes plugins/ReverbSC/artwork.svg | 10 ++++++++++ plugins/ReverbSC/logo.png | Bin 774 -> 0 bytes plugins/ReverbSC/logo.svg | 3 +++ 5 files changed, 14 insertions(+), 1 deletion(-) delete mode 100644 plugins/ReverbSC/artwork.png create mode 100644 plugins/ReverbSC/artwork.svg delete mode 100644 plugins/ReverbSC/logo.png create mode 100644 plugins/ReverbSC/logo.svg diff --git a/plugins/ReverbSC/CMakeLists.txt b/plugins/ReverbSC/CMakeLists.txt index f6f6c7ee7..4c0d7623c 100644 --- a/plugins/ReverbSC/CMakeLists.txt +++ b/plugins/ReverbSC/CMakeLists.txt @@ -15,5 +15,5 @@ BUILD_PLUGIN( MOCFILES ReverbSCControls.h ReverbSCControlDialog.h - EMBEDDED_RESOURCES artwork.png logo.png + EMBEDDED_RESOURCES artwork.svg logo.svg ) diff --git a/plugins/ReverbSC/artwork.png b/plugins/ReverbSC/artwork.png deleted file mode 100644 index 6fe5168fa51d636070ee886c308f2db3037060ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 262 zcmeAS@N?(olHy`uVBq!ia0vp^JAv4og9%99FRfV&q&N#aB8wRqxP?KOkzv*x37{Z* ziKnkC`$I-1VO|E^><2f1Lb9155hc#~xw)x%B|t6%gL6@8Vo7R>LV0FMhJw4NZ$OG( zDmzfT#?!?yq~g}wOB;C^3^)#KxFo3f@&EQc2M(pp7f{{w>)@g34C^0=?r94Dz!k@c wg>_IHCX39fV1?*7sEy28-vBZNBnD<3XG@P_nB4T;{};%Cp00i_>zopr0Ga?>YybcN diff --git a/plugins/ReverbSC/artwork.svg b/plugins/ReverbSC/artwork.svg new file mode 100644 index 000000000..04f0bb87e --- /dev/null +++ b/plugins/ReverbSC/artwork.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/plugins/ReverbSC/logo.png b/plugins/ReverbSC/logo.png deleted file mode 100644 index 9340da708dd79ed97111eb535f51b81a91d6a15b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 774 zcmV+h1Nr=kP)7WEc)VQ)zLm`B#lSD% z0Wg;#IH?7|3b0p&fj_`2;A{cm@zx+(Ge?s$@EN$6LtMjg>;+(boCbaXw;ja=b6mQ?gRp67Yf18(18niCN0v&eY^{Cr&#;#IcF{ks?!* z&o!_q>9xbSw`QytRI!M?zP_o#K-8ExJaV?vi znPt?~0KhTu23U+Gy8^Tw;^SzWSet9n + + From 0b709dc1afeec8d20358e510a3f5e50daec10eae Mon Sep 17 00:00:00 2001 From: Fawn Date: Tue, 18 Mar 2025 21:15:26 -0600 Subject: [PATCH 05/62] Upgrade Flanger plugin PNG assets to SVG (#7775) --- plugins/Flanger/CMakeLists.txt | 2 +- plugins/Flanger/artwork.png | Bin 352 -> 0 bytes plugins/Flanger/artwork.svg | 10 ++++++++++ plugins/Flanger/logo.png | Bin 774 -> 0 bytes plugins/Flanger/logo.svg | 3 +++ 5 files changed, 14 insertions(+), 1 deletion(-) delete mode 100644 plugins/Flanger/artwork.png create mode 100644 plugins/Flanger/artwork.svg delete mode 100644 plugins/Flanger/logo.png create mode 100644 plugins/Flanger/logo.svg diff --git a/plugins/Flanger/CMakeLists.txt b/plugins/Flanger/CMakeLists.txt index e74b2838f..54ecdd85e 100644 --- a/plugins/Flanger/CMakeLists.txt +++ b/plugins/Flanger/CMakeLists.txt @@ -3,5 +3,5 @@ INCLUDE(BuildPlugin) BUILD_PLUGIN( flanger FlangerEffect.cpp FlangerControls.cpp FlangerControlsDialog.cpp MonoDelay.cpp MOCFILES FlangerControls.h FlangerControlsDialog.h - EMBEDDED_RESOURCES artwork.png logo.png + EMBEDDED_RESOURCES artwork.svg logo.svg ) diff --git a/plugins/Flanger/artwork.png b/plugins/Flanger/artwork.png deleted file mode 100644 index bbe80e5988023dd4ea2d3bcf5c691cac2c680590..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 352 zcmeAS@N?(olHy`uVBq!ia0vp^hk@9egAGWYontHyq}Y|gW!U_%O?XxI14-? ziy0WWg+Z8+Vb&Z8pdfpRr>`sf15O@cO_OIwq~-vH-g>$?hE&{od&`ll#ev85;QB9& zEC+wz-x9lXqCnL3Pl=d#&Oyi;c|Y>-%BWVDuNf{HWOgPANzAOQ!S4QG~z z8b~_uD6nOevq>}005SxaORSr%5zJmjAE4Nj^D6Vh4yHhKffUs|W#aYcdB=k=3nco- zWEo6r2jdZjq=x=KtSt3YgeQO1TaYQqMH==mUX9e(;0r87(hX#!c2Fmti= k44x0OfHou;0i9mNY_wGNN_BI$5zyldp00i_>zopr0Hv^Mq5uE@ diff --git a/plugins/Flanger/artwork.svg b/plugins/Flanger/artwork.svg new file mode 100644 index 000000000..e2aaa2862 --- /dev/null +++ b/plugins/Flanger/artwork.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/plugins/Flanger/logo.png b/plugins/Flanger/logo.png deleted file mode 100644 index 9340da708dd79ed97111eb535f51b81a91d6a15b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 774 zcmV+h1Nr=kP)7WEc)VQ)zLm`B#lSD% z0Wg;#IH?7|3b0p&fj_`2;A{cm@zx+(Ge?s$@EN$6LtMjg>;+(boCbaXw;ja=b6mQ?gRp67Yf18(18niCN0v&eY^{Cr&#;#IcF{ks?!* z&o!_q>9xbSw`QytRI!M?zP_o#K-8ExJaV?vi znPt?~0KhTu23U+Gy8^Tw;^SzWSet9n + + From 5960a4e79237a0f2eaec8436c7d0129c639f10d6 Mon Sep 17 00:00:00 2001 From: Fawn Date: Tue, 18 Mar 2025 21:28:22 -0600 Subject: [PATCH 06/62] Upgrade Peak Controller plugin PNG assets to SVG (#7776) --- plugins/PeakControllerEffect/CMakeLists.txt | 2 +- plugins/PeakControllerEffect/artwork.png | Bin 10882 -> 0 bytes plugins/PeakControllerEffect/artwork.svg | 10 ++++++++++ plugins/PeakControllerEffect/logo.png | Bin 774 -> 0 bytes plugins/PeakControllerEffect/logo.svg | 3 +++ 5 files changed, 14 insertions(+), 1 deletion(-) delete mode 100644 plugins/PeakControllerEffect/artwork.png create mode 100644 plugins/PeakControllerEffect/artwork.svg delete mode 100644 plugins/PeakControllerEffect/logo.png create mode 100644 plugins/PeakControllerEffect/logo.svg diff --git a/plugins/PeakControllerEffect/CMakeLists.txt b/plugins/PeakControllerEffect/CMakeLists.txt index 0bdb73123..eca93f671 100644 --- a/plugins/PeakControllerEffect/CMakeLists.txt +++ b/plugins/PeakControllerEffect/CMakeLists.txt @@ -1,3 +1,3 @@ INCLUDE(BuildPlugin) -BUILD_PLUGIN(peakcontrollereffect PeakControllerEffect.cpp PeakControllerEffectControls.cpp PeakControllerEffectControlDialog.cpp PeakControllerEffect.h PeakControllerEffectControls.h PeakControllerEffectControlDialog.h MOCFILES PeakControllerEffectControls.h PeakControllerEffectControlDialog.h EMBEDDED_RESOURCES artwork.png logo.png) +BUILD_PLUGIN(peakcontrollereffect PeakControllerEffect.cpp PeakControllerEffectControls.cpp PeakControllerEffectControlDialog.cpp PeakControllerEffect.h PeakControllerEffectControls.h PeakControllerEffectControlDialog.h MOCFILES PeakControllerEffectControls.h PeakControllerEffectControlDialog.h EMBEDDED_RESOURCES artwork.svg logo.svg) diff --git a/plugins/PeakControllerEffect/artwork.png b/plugins/PeakControllerEffect/artwork.png deleted file mode 100644 index 5977f83cc27e9dd371cd6a8f59f21e1b46872bc9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10882 zcmV-|Dt*<7P)AU=aWQAO9Ku_w#>0|66`O>|Y%I z_D4UwUV^-T=Kew2e|fzUgZ`%RuE^oD)SGzs{2YG8*N=C%6%QNaKiJI4cNcgwDt|hP z?_S~MC64#TZeHFl^7gj*>fd}En(Qw#0`D%xi-nP+1G{&C7f(V&1c+GfMwDr=-uHYM z`Tjl$kbe+zZ_hip9)`ek7dB0v_ph6HtjxRzu8ZvPK}4XL>-i|2$M<1##PwxLlQQ?1R%F`5-|@n)oM9Cszz$n;SJ`X4>^&>ruFFocnp;?zCPc z)+9o$1!(h^j5YLOjvwpw+9DyQdmbQUNmygI?`nO=p21koQwdNsA8+R4G1wN$IY?Qr@i^9URgeUp2U_=yvMh6j4gz~&+PAq(h3D>S z&XcuvF8?5~O@N$2a)N=32U*u>ix`$o)3?R6+$IHS8y(b-*F@f{I%KT{1K_iXa5-JA zugLWVYtpTCkjM>aujj$Xa3qLYOR!u&DBDna-S%uoZC1<9B%cX`<&l(~3&fgN%hG$% zdj^cp+{0cAo^|o@l5K%pE}$y_Dds}%Pdd>1NuK$}2;eWS$&<56EWl+38Si*ZzWsGO zEe1Uk|hXvIZZv&EOff$HoN89RaWw8i33#rWTexTgpx$4zRrK z`G)=M;?j7o_L${yV60E=QxxhU_Z#;VX3pC>-_$PkygQ#i?{lqudu8F{-TPAeGqu$0 zKbGYnLuCH3*N-*$&w%7ow5XeH)BysrWIr1NaU3CFH@3Wk`y;hI?Z*Y$*%5mX z_d2;Z&)PSbcEh>^^N{fQ>-WFq%9AsnYj2UwCDv|(`ntXSU{9i4bM{ig{kgqk;MpJc zxi#+*a`?@IOPuG{=bHOQo(E&(B9*tZ?DDL0&%;noa`gqsRdii&KN|4-A&w}s1v&k4 z_RD)}uBS!wH`^*vhT}dY;5Z7^zmY*mzIR(-J0sS3t-D@}_&P8Ju>Qw7ctpOiuU|L2 zet}#%xSyM~fk%BW((UqV2|v9c7^ zTwXwq94p9Ln4zI=`Q6YRjy*HS9dq-69-$xAOj=DGxxJOSS@7k`-9S@iI<3t!7cNuYg!mJOy9xSTD0BKBR0g*9rS_mTOK zXVLil_51G&3WqCKZXn!0<2h91mpX>%or5pahsvQNu+cIZOyoCv?g|kK=|)LH@LCQipWS z6z=^>xPi?&h2G$oIuOzZN}lKnB#Zf6^Ycp_T3vw?&Nsf&KGi&Xz&tG}(_hDMGnnA8 z>ld9+<@qf-a^a&g!8b7tXOD7mxhqW8dktpe`lP^s*mk_1ZDqN@x;Wl8E>96NLX7(j zqX(gH@M$B3-F+tB8LfgsI1tbQvUBC*fHKx;+UY+Kx}ox*SnSao?7QO*i^!?>z=KZ6 zVCRVbqmg1M0LRHT-Ej!7P^yNv5D0%qaJJ+R(4((38~_@zo8bJiSCAuq-Y#AkKRK;F z^Z?%=kQQ4upfBFwa#S|X9_48EH-LxPzia$il zDerAeC}1IxP90JlM{%y>b5f$N9`x}lCTZvyD$y|5wk^I*AKC>CBvCPM4VyYf{cg(~ zB@s~lY=_Tw*KqGhh#YKTJ1Y&$N+EEEQX3YcP;}CVI;yNb$_@c0v66}sW=My`Q%L21 zLi~0iv;_$%v8ko$pQCY|xLN!|gCQ$VWI2_M2(qP{)xnX$`^S+uV^P{Ge+aLqz*%C# zy+m{285v4L7{T0QE{shO&J`uFXLZKuNuEa9UitLm;dH9DK>cOT{XmImuq}2_TzHei zHig&@So?JdGZC8t2-)a#C0~<{mQKQUQQNtkx~Q895Vf)ZEhn-(28KDm4YZ^O7gO%FZ|4D{?*oieJL+l z*fF*oLi;Wibu#Px7n^~0E&~iuieE@|>}c1iJK;(R3B6Y~ zC-h9SEigSq-1f;bw+meg0zdJH?-H$t03wpdb%L8g#e0-D${~Dv!robH>p*@=Li_1M zFfL{{!O4Y=!jBb?L>|wLeco{x2oQ&wp_NROU<%6ObY3*gwGNJ^!CX5pDl}dKZ+Uiv za+({4mrsiBVaBT#XR|$9!kB@H%7J+5Aga_^h$p}%BuBQYVrV$yMN2Kv_9nyx8A&t& z$!QhJ!*Xy`T?+8|_4{ucaw!aFAVVhf^B0gPC&cpTv=I!rvYglQ@q}rc&btyXm6)){ zM&}N}2;Vm494SL5+w@6^{&hDJCg7Btwq3>mutD)*To}X$5{6oF^1x?CrY<4TXNteg zDEcF7wq+9Z8BSp1?G=5xo`7vGa=4l^Z8=s)Ba4nCZO!-up{BpBDv+9|Grz-An zatAATOGcc)g@p?UY(O54*l5Za366@hvf&6+#0QopW*pz!YoCkgQ{{!qFsy)%cDLkU z5XT(5k2d&PqZk~9B`?-K5oL+K!*FW_l+;khWmTUVR7O0Chq8EugpVrmr=eH}?h{hg z_@SvMDx8BP(E*ECMgvzSMy3;!AKDcZ%oXI66MkAs1i-|8MQ3Ufrdg5qyn!dhNR3Xa zTjypkNX326DoC)#Vq%3B(<VK~N<|bJhkesu9-O}L z{;2X*txgwS=7tv~f1cDk@uWqPgd~FsJlU+V&O=W)WYs@g>SIYF2pfx(abpc=GBAU| zzZOXF9Jn{AYO$Qnh!HuWqWY$yM3R*3I-9G4$jWYsTnGp8B#xkq zW@M#5q^5k}SC*S^<%fM#O>(NNk|;O}iO!X}iVTg1qhkGi?uDeS3K?8fGl(dHKyqZ( zfkS!Hbjl?Q_YeXgfH=tdwG$oQWk#c)2Www9Q+kh0>8abko( z4h1MgMA5wpJb}rdx2jU(%s>eG*l;3zpO$)M?%TAL7GBE^Gc_HF67*t`0Xdy8x15FI ztwJhRAQg`P2sIn10(atMY)F8(n1qGT)CPQ%G*+=atbB%mE;bMfA2Vxtl>u)IPzQ_*c&hzS6#^h~QRpS7S+l!VBpesl^m+%71()&R zhp`##><5=uu=y;Y=r&&hf-Y--P`WL2z36W35kCizM*m~6(#+RY{IyfCP?S4Fn$+se zmGp;B2^3`>9VzHy6Nc(RPOGpy13Kdg!jlLyTEQ7|egXt7G^*uuMreiOpI^U!8wayx z5RM{zLx)bl^F&OBxSwvL7M?h6j29*8Ft0v2FM+A5>SaMRo!r`Htf*~;)HBuFMIJ3IJR6{%Wc*^ z{~S4n@B9uR`VtbKErL4+e50!J9V#4mY@4(E!?!N@`sTk69>3-R$NgJIzBOOE?G1kB z1zn9xR)Lqy!0U^|WxV)Kz-06#zI4T#D9XoJh<=a{qeFBXrNtZQ87!7Q4*>t7M)VQMzyZlS(WHUvYd;FG%Hf@ zbdx_>2Hg1(m30-(iiEzg#W7XRA!)UA?z$zLP@<*5mV`=1z2z>b=WY3}k3AgFl5R!R zlT4O0{e-;@&s;WvS$U)LJ0PWVA9rynOj-Nk7eOP7Bo0gEz42MaPmm1U;-Ni5!!WLA zqHHK>111EAOmFOBejv2W;X#vhP+4WbCbSt+LoS~$3D1QH?7vYqHn3-|7Ccy*9>u_A zdQK6gMg8Yl3TQ6TE3&q@atYWn0CqYyMFo{2`IEK8WXpS|07FwDfoq~8vLrvvzpqRo zfuORztlSp)`SaJ`g8B&ytcZMjcs0Tyobw^lj7S<`k=jz{7WG-|5sd_yt=v3>mx8VJ zkjOidC|NjizL$E(sv-c#R}0od!mJRIRC4GY2i3}FB~(FtE7@v~3@RGRxLIV9I0q8E zqH@Ae%&`+D6avqq#R}9ynP4qXJVjMPg+_$6ny6Bj z?OkM4A=#fe3oqTMs+EJTTM%baG-6UWMYon0d>+ykG~C{}ypm+;S_9GKe+3K0D~xrn z?1$xN=!iLHHH#}2)Wq4G;SODc#DJeazkcU{qoZE{+nV)S`xb{*x8)&*N02l+@ah3C zlB@t<+5C#}4h0==aoqbT;F8E=917X@ufTEZ^F-)5QUz#mLgbUP-5(V_0jY@|Ot3P) zlU<0OMS2Yn9WvYVIx`K(=g%Xt@=yHyDG>WV=Z8}+;O)cy>+JE(E8S6ac8dT1r@P@; z+40~1^ARWfYEZ-oGj~l0D8M^5^SeU4C&oD7-Y0I%_igSXdT}Q?YYOu&qde#!qYGf3 z4F+9Xmwask4*!v!cYb{y5O+hfO_}i2zxr0w!u%4>Q)GA-Xb|Ku23K3ro+SO7O!$h-3bJ7UsNYHsZEMzaq>^$3P(tz&DuRx)= z?crn$b#Qb|nywo|kq6vNY7%|;u>0PyDc3rn8uYTqBhS-eiU&!fc0n^cHK^?}W; z=_UQ)a-V92*7&HB-)d77#CtglR_a0l^UCF*EhrDSw8rDmCKA#=0w2y$_M{nHf@)Wx zaEp#=W}RCoE}g72Wxzv|IC;CY^?f&YV>G}4QcNS`(6dsN;3%fPA!mde{OX+P@K& zway3n$Ti`4bV{nSF!LxFXpR3CWmOMIRgx4v50UhS_JL*gYg)w$T+pki_SjyMNr7ye z6s5mKOPo_eY$YvI`Ho4LN9r4Uvr#4FkctR!t68JcY}}g7Av!XJl$R+G+LAG|r3Uzw z^bI91H?=~pzJpv%?Zy*7xc6UbBT8f^(JhT5EdsGc*s?pJDEKhNM#LW>62qV+BUAJ# zB-^!Qrg!=XCgL5EhO-VJQ2Qk<77I;heE#_Qt-P}hxfbBrFrgv^pq~%~x6IT*p{Ppg z7JXXl=OuJy>tBb$VCfyIqA9dO(-SD@hj7Ud7@#P11UtFFYAl;CQ5twP0ZGMr;UER8 zeOo0mG83V-5fn(f5Hv{o4V6xW>b6rT=ewYv3TUFy|3oNrYjmMizZ)8XEDtE@76)cq zfmNo$as%5%8rtwyq2;KjLP)PV*E|cUA23nnb%D>HU%yno6isTVcb^-0tS#}RXP?vx z9)Tk5YIwAMYd{Yj)`#HBaGtVte4}Ssv{dFiRy8E-pqQQH|1R~bU{}N0?O&LhPc70r zC(X~!-B+WH`u5lrWH?YjK*EU$E%r{7;uW$OaDosevFKt`LdhQP#tPgF?7@D&a zQ>XOW=hxS-w*Vh*NbcD$u6-7lz+mJocf}ff1K{jcN}ia%V6LOaf-08OYVi^TAFl_O z+&FtK4v^~m270*Q*k^lmv~1N3vQMo?>96ipvXe{EZ3ge%IP=iENLic$?2T2zcZr2o zO!F))pFh5S6<6?E*deEz8{TH?I^SkxzL!b)SL3+<#4X`&@t8zKjysXm79*~Ca=vx+ z7x!?C4sj4UN|hLY0J7rS1NTC_WllYdiO}>*IA`R}v5-E67&HYA2-aflh!+jAU@hh} zs39>1FAa<48!Y=9*$uvW6AP1j2aFrmWi8+)Y&G>;FT`i4&Ep0D_&{rr42O!)Ek8or zLytI>J!p47fAaxRyafMSvqbxs_P=4ADj}*8tRkyCXTX?YZ*KQ~|5v&J1EY}1puXldA6vPF95owKfuk6Qpuk=gma zjxn2nG+IW)WinW}I3Brp}^Q4r&{_O`RnpWgX<9Gd-;aliJVP{P`?{!m z0i`D%R3OCRcnV`%r%BD?jYBQUGo$)Cxh0Wm@k|`eKqj(m2vtu~QQZ<#1-&_>x6tfl z@K&q47em!3mXLyn+7Wt++)z&e_pM_d7x$V{2d|yOXv!dGewOswBPj#Y6kIOQx?MA2 z;0H*N)>skoAxwTqJ}`?txao-;!VpjVO5*`tia=>=Nxi5q+gv00*l7CZZRPfk zbYdA~wMS7MS)$#cNa~r_7cG|-9-!oKSjzX3a)#euRRip{PMKi@dm@iu_OeQ-7?Op; z>4??g4aPa9t^MVM*-B39Olb>K3XWHo11$aX^{|9>*MxVOg_IvjRnO+ETE#81ky%vs zPm+j2!be^YnaMx|SJ}UZAN1HB_&5>xz1Se>rbxC81jJQHE^(V8m z0FDf1YfrM;?-hWyq*mZ`&FDP{jH3&yQoyBBwIv@)@@2beW)HI;gn;)6H1CcY*$Hm9 z^5aJMrDYjvQ>4@$Z1yYAR3@5&IS79K{P`8pwI8gjS9vIV1uINwgo-Z9Fe)&th-gdJ z2x+xsv$VNeRG9}PPZ2n@JgDnSq;QI;_7*k1*9*A7Ob^{E86m|u=`kYY1eYK|lQoE; zf*N`}1*Q9oHLz(?E}=wuF>Dqd^p>1Q-Fqf}mprxA_mr%9AFqv>yVeF$-mk%Cl}K_( z)Dw;>zO?(~)+)%U=G?j43$`m)@Kt1}TnA>|TTH3|c%?Tm_Qt#E=bjlaOz_PNU|}dK zHfp$}reYFoh1L{xDE83x&x)TzHi()wo1S#iAg(NALyCRfn7getbc_?KtJ>T#Xck-F z!BI~+t<)rOrjBe43`e_%v3lYx!7w+FiTe5D=dV8kcG3ISL|dLlfC}hNeCc&(h{*_8 z;@cSKi-~b-Js2;0D~5GC7!3XTu6Ti~UjWa?eX7r~b%Z768yLU+&amg%bmK5ouOvlp zSjV#lr%S3TJ#e(=%hHW$p(Fwy1y-D6;Ni7MFIy@f);R1$?Z7hCc+|pQ*f*954@-z@90Uh=@%ZajwOtqou=0*dOs|)RbY@ibhaYDCz3Ap0` z0~;5seMK~W1vVHN7m58B|qZR@M zNEbS0xQfk5w0@W(vKrd_QlJFzK+c}%gs2*K56QVj4O&XUI{FZbf1J7(nvp0hCvQAf*5V-<`=@+D>^W3`onkhtFJZl&x)f-7wjBhlw23Cf&APb2JIcvWA(gK(w z#9o|eO%C%=Pr(yK3wkx{g3UrGzGG^BDTg9ybC&B#DVsBV;HOc!>H(;h7H#U zf;P!RO^{^rvV1OuBNE(c9#VkuzPAzrWMOqFG(2ZJ6!1L+*1EFzKG~i`XD!H zJz7$VLM`roYS_6DjWnn1LSsNRfC_7h$6|=*f20Xay}!JMi+yliA1xYTur$tL>XzG;EaW$4C(|}&1wZZf@pA;Cn<$8!X#FneofPuv6Kn)#irgQFKE#JwL zlLim$Lj#DL9Xld>Pf5ba# zEjovrs|?1N3P*E23?q)2B8dwjMM)*ZpaXtr0T=4G9D1U$QW{JE_KLk>D()EZBf_14 zY>L%G$JskdPj8kyByaKh6}A#Aqq<2M#4J;Nwm&0!c2kCy(o^SJKf%i^YP{-WeVqx+ zBV%~H&s#?3#6BP%@aU!R>-b~+!x#ho6H2yawwa`&DeQX-t^70&wF+jT zI7R=pCFVB#2QPjwjiS~JWI&Tc77Dj6w^9=FR#v&!G3I)GwTqK3!1 zxG4teTe9>X$ok`uno=fNvWShDXTfg7x3+--;k8L1vhi8TM4xph@&`B>xmfH4#6zu%MgMUf1^=$0x z=u6bGnK}G1bzx1hb5rFwTh~q0EF9R`ApMHgffS+3k+#tU@3Z&w$B$oKIe*|X%1UWU z4}4i+T3t~JB=0dU3pzo2$Sraunz0H_<1+? z_2)&8Gu^sJXX61XIg%DPp^us1_#HLPX*PEl7!CiC!z5y4v>r)24;JS~`y-{0v`(Gah`*Rzb_7KpBk&)9oxTPlMf1w(yd03~BXwVh>bLAv|6n=7uwnarv^@ znuA0ZQD2)VuV5!Sd&#)gOTfIK;ee!SYuPp~S4 z#_ml(`lcv-`d?W8L7x1|>Q)uX7ixJX@}QT(MQ1ZnA4&tGxXyHF&{ePHXu^A|(U{Xb z5nFGNa;`FLoZx7MutlrYwN>b)vswwOy0voDb7r8%`?xbS$(ESZl26z(A?z}c!MXyg z(-$B%rqf4}Uy1S(NZ+Rg$QN9szP+!Wjcqp9P^q!omh4qF#kZxy`AOkRvf{LyHk17e_us?Rk zM0MMRni8wg9h2j=QU5TKw}_Is`miv61;}f}^=jwn^QPq|N^(_2H_%wTeQy;RVTtxY z8sV22lnKqBARPFvPO{Q+?jgRefnrOSWo7>EM7Or|1&gopd{RWd!DMHH?qiFfwae1A z5R%C_t$aoJI+@ifg`74HlPLqED#A7q*E!TrEj*DznyEIsDMo4HRX|T^)d?lYXHFBD z`pG7Zbzgg9VR24X6qs84yv@Iprd;|ETd9<(t`5l=DclSxCoM|#l3@UpoKDF;80opy zU}zbNbh}I^Z32I54zq~jRpk&eMD!G?O2zCfA<;njHKKoMH}YLIzx%do$G(!4h7o*| zKjsP@t4$ZR4W4KrvV4Tr`esv-N_HFd57CAoTdinP+F7_#t1aFS9=j#s9{(&vW(g$c z%L+0RCKpu9NE$7X61l5bE-Ir|txPK=B*&eqn_oFdO=_u#^`4Z)R-&XOYQ@nE5+OmJ z7ggxdmRcIT{hXy|K_Mch#6B6?AW-v^(t8UOfP42QE}0TkiF#K9yqBG%=p;FTBTzi$05p-=G>t=~`n_+Pvm&a88?6y3)S$?&xRMMI zv0`yCI447ge~>=K1vR9RD%RG+m|<;!qUiu*#OIH{zElF*@&GeB0?%x~sf#_?_l{5~ z!V|9W3P!v|h$iVyw46mW#egG=tjYsZ4>g{I+1~W@yC@vabP{AjkB?`GER0Oe0}0oL zvk^C0SzM@a7$9OqH6`|5L^8!Sn{-HxH#r1ft$e6@626J=%y|Cu+t0rghyE|)qu=^S z%k<*Cr~dIxy?Y<<9mm7J<{SSxVPMQu&NmkW4sFr@`p>1cWPGdT;-47f<+^oLKa4;B Y4|$cuvTwJ^MF0Q*07*qoM6N<$g6PPjE&u=k diff --git a/plugins/PeakControllerEffect/artwork.svg b/plugins/PeakControllerEffect/artwork.svg new file mode 100644 index 000000000..b3e7ba076 --- /dev/null +++ b/plugins/PeakControllerEffect/artwork.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/plugins/PeakControllerEffect/logo.png b/plugins/PeakControllerEffect/logo.png deleted file mode 100644 index 9340da708dd79ed97111eb535f51b81a91d6a15b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 774 zcmV+h1Nr=kP)7WEc)VQ)zLm`B#lSD% z0Wg;#IH?7|3b0p&fj_`2;A{cm@zx+(Ge?s$@EN$6LtMjg>;+(boCbaXw;ja=b6mQ?gRp67Yf18(18niCN0v&eY^{Cr&#;#IcF{ks?!* z&o!_q>9xbSw`QytRI!M?zP_o#K-8ExJaV?vi znPt?~0KhTu23U+Gy8^Tw;^SzWSet9n + + From fd321588613472dfb31659f56961ffb0cbf9eb27 Mon Sep 17 00:00:00 2001 From: cyberrumor <46626673+cyberrumor@users.noreply.github.com> Date: Wed, 19 Mar 2025 10:23:53 -0700 Subject: [PATCH 07/62] Fix crash when switching opened projects (#7797) * Fix crash when switching opened projects #7793 When the DAW is already running with an open project and you attempt to open another project via File > Open, a segfault used to occur. The crash seems to have been caused by model()->value(), which was called in Fader::getModelValueAsDbString(). The model() function looked like it should return a pointer to an AutomatableModel. I suspect that when a model was dropped in favor of loading models for a new project, the AutomatableModel pointer became a null pointer. Add a check to the AutomatableModel pointer so getModelValueAsDbString returns early (before accessing member functions) if the pointer is null. This fixes the crash. --- src/gui/widgets/Fader.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/gui/widgets/Fader.cpp b/src/gui/widgets/Fader.cpp index 153d8ca1a..61e6e753d 100644 --- a/src/gui/widgets/Fader.cpp +++ b/src/gui/widgets/Fader.cpp @@ -512,9 +512,16 @@ void Fader::modelValueChanged() QString Fader::getModelValueAsDbString() const { - const auto value = model()->value(); - QString label(tr("Volume: %1 dB")); + // Check that the pointer isn't dangling, which can happen if the + // model was dropped in order to load a new project from an existing one. + auto* newModel = model(); + if (!newModel) + { + // model() was a dangling pointer, so return a sane default value. + return label.arg(tr("-inf")); + } + const auto value = newModel->value(); if (modelIsLinear()) { From fc29682b902ced3eac0442d8c0c71ec4e72366b0 Mon Sep 17 00:00:00 2001 From: Fawn Date: Wed, 19 Mar 2025 13:12:00 -0600 Subject: [PATCH 08/62] Upgrade Bitcrusher plugin PNG assets with SVG (#7772) --- plugins/Bitcrush/CMakeLists.txt | 2 +- plugins/Bitcrush/artwork.png | Bin 1380 -> 0 bytes plugins/Bitcrush/artwork.svg | 21 +++++++++++++++++++++ plugins/Bitcrush/logo.png | Bin 774 -> 0 bytes plugins/Bitcrush/logo.svg | 3 +++ 5 files changed, 25 insertions(+), 1 deletion(-) delete mode 100755 plugins/Bitcrush/artwork.png create mode 100644 plugins/Bitcrush/artwork.svg delete mode 100644 plugins/Bitcrush/logo.png create mode 100644 plugins/Bitcrush/logo.svg diff --git a/plugins/Bitcrush/CMakeLists.txt b/plugins/Bitcrush/CMakeLists.txt index f4810183e..88507f860 100644 --- a/plugins/Bitcrush/CMakeLists.txt +++ b/plugins/Bitcrush/CMakeLists.txt @@ -1,3 +1,3 @@ INCLUDE(BuildPlugin) -BUILD_PLUGIN(bitcrush Bitcrush.cpp BitcrushControls.cpp BitcrushControlDialog.cpp MOCFILES BitcrushControls.h BitcrushControlDialog.h EMBEDDED_RESOURCES artwork.png logo.png) +BUILD_PLUGIN(bitcrush Bitcrush.cpp BitcrushControls.cpp BitcrushControlDialog.cpp MOCFILES BitcrushControls.h BitcrushControlDialog.h EMBEDDED_RESOURCES artwork.svg logo.svg) diff --git a/plugins/Bitcrush/artwork.png b/plugins/Bitcrush/artwork.png deleted file mode 100755 index 72d980c9164c9f83613c7a76e68a74f76d1e053c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1380 zcmX9;3sllq7-x|}55dmV(iEvv9%~)YS-wC`5zU+rLXV~^Bt*prZ$+$ZikYuQ<+6eX zDk74mY|eBUYMGkD^rX01D=TqhajXs_qZDg@*gfaF-?{hxzVG{ek8`UN>K{Km>^9JUjqZ|PLhgH6a@D>*Xm#$oV zDwoK};_}FBDu~D9p=k6x4u#C5q8RLK@f)lg0CKm2l4#s~D(IWII3hBENu?u+j6ybr zij1K!=p0G9-BkeRV#S3Qa&s7L&=KkhDuqHuMpC0_)cj1px2sA$6)f=wCo%% z2#51QCKjH^r%{lRi~=%^4!|>M6f&2_pd)j*TxKvDO<_}ka}kxoC@26zS!tn}Hk(YF z3A%*JW}tmhKIl=M`Xi9GUrc0pQt|cI&*JD`hC5ci%w0MNMef+a0`2mD`gz~J3w@FI zN$&86_J>9rTfAr*(rHs@9{NWAZ*oadLchAybPJNPIIaYPqGWC4bIp|=HEu=TxpR-B zvQ6OQ|7ca+@`T}s{-&l+oSHTI)?-ZNblUy;*GKbjWh-EE>=mq{reyHXk$$u1!or9q z8WQlNw~Mr3QV5-vK`8!Ypcw z68E8NH%neCqW8hdHLpgd@vnVSJ8*1in!0&b*B)^i!7<8f($ul-V<~4>uvxlvRqJSQ zRZ7u1c#TTOX&k4zD_TR(S7Y^@Vcm|(v#WK->tAsPE zR7iuTsy;a3=6p;4O7-|ot=@OLXL^ZhSg;eOWsoe*RY8LNdXoMUqJzsk+crgcUx->( z4DGwZdO@$ocFQ4{4yCWm^5nRS)u;jS_vYJM&80&yAm{o4Gw#>qZgD_mtmKteRsRW3 zFt2WJdU`C^_`t8<^Xg2e;Rnl_c0u3_hYL1`B*Z=Lz^`FBjT?HHjT`bJ3Bxdm$VaED z(apo%+oai6o_h_bdHMgv?lwMB+J*t)XhXNM(f=FaHv1#20wk0!wT z?DsHP9pHVoLl7=8^-2yCfwEp!%i2OxUyPQY4C5++ereR6cVtkE<-d1~4B`@3toJ>DNus7tlds(r6*90_t7pBtsW9k9w%PNu;?9GYO%n}>rItw|&p7Cn zBJA3@%%2#IR@mmW&kW|+^IM6-u&{w4*pF_#Z2^#Z!hLYyBL8lgm+_?=_C=1hSpoa2 zh0cO)i`Nbuur{>bGIfP=&KPJgCO3r&hu^L-i+K)a&I3OUd~TfNxM$6-0oLg##G?D8 zX<|(vGhmWPxm+B*1!B-&i+8}d=iIBqNld^5M7Mv^ggYB%0^d}6HNp^1tj1DF&}!MB zt#zUN)$aMcxyNPZ zm#{+b&vW^=9-S0s>w)R;Pu3pX?XOpsh0RS1Jfwry!KE^wKzIj?lP!51VR#blReq5q U{CUq$z!wFI!N*5-Mr4%#2aoKCBme*a diff --git a/plugins/Bitcrush/artwork.svg b/plugins/Bitcrush/artwork.svg new file mode 100644 index 000000000..dfc583974 --- /dev/null +++ b/plugins/Bitcrush/artwork.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/Bitcrush/logo.png b/plugins/Bitcrush/logo.png deleted file mode 100644 index 9340da708dd79ed97111eb535f51b81a91d6a15b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 774 zcmV+h1Nr=kP)7WEc)VQ)zLm`B#lSD% z0Wg;#IH?7|3b0p&fj_`2;A{cm@zx+(Ge?s$@EN$6LtMjg>;+(boCbaXw;ja=b6mQ?gRp67Yf18(18niCN0v&eY^{Cr&#;#IcF{ks?!* z&o!_q>9xbSw`QytRI!M?zP_o#K-8ExJaV?vi znPt?~0KhTu23U+Gy8^Tw;^SzWSet9n + + From f09d56cdd0b4067fabf2e5c744df719ab2d290ce Mon Sep 17 00:00:00 2001 From: Fawn Date: Wed, 19 Mar 2025 13:16:05 -0600 Subject: [PATCH 09/62] Upgrade Dual Filter plugin PNG assets to SVG (#7774) --- plugins/DualFilter/CMakeLists.txt | 2 +- plugins/DualFilter/artwork.png | Bin 934 -> 0 bytes plugins/DualFilter/artwork.svg | 22 ++++++++++++++++++++++ plugins/DualFilter/logo.png | Bin 774 -> 0 bytes plugins/DualFilter/logo.svg | 3 +++ 5 files changed, 26 insertions(+), 1 deletion(-) delete mode 100644 plugins/DualFilter/artwork.png create mode 100644 plugins/DualFilter/artwork.svg delete mode 100644 plugins/DualFilter/logo.png create mode 100644 plugins/DualFilter/logo.svg diff --git a/plugins/DualFilter/CMakeLists.txt b/plugins/DualFilter/CMakeLists.txt index 9aca1a7d5..a9839b4db 100644 --- a/plugins/DualFilter/CMakeLists.txt +++ b/plugins/DualFilter/CMakeLists.txt @@ -1,3 +1,3 @@ INCLUDE(BuildPlugin) -BUILD_PLUGIN(dualfilter DualFilter.cpp DualFilterControls.cpp DualFilterControlDialog.cpp MOCFILES DualFilterControls.h DualFilterControlDialog.h EMBEDDED_RESOURCES artwork.png logo.png) +BUILD_PLUGIN(dualfilter DualFilter.cpp DualFilterControls.cpp DualFilterControlDialog.cpp MOCFILES DualFilterControls.h DualFilterControlDialog.h EMBEDDED_RESOURCES artwork.svg logo.svg) diff --git a/plugins/DualFilter/artwork.png b/plugins/DualFilter/artwork.png deleted file mode 100644 index 00458f1019a0c806d38a094e9d017f55ac624dff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 934 zcmeAS@N?(olHy`uVBq!ia0y~yU@Qf)b2-?6WE}s;E+EC0|H(?D8gCb z5m^k>aUFyioit`w00r4gJbhi+AF@a@DC?K$#64tSU^e!2aSW-r_4aOH-em^?wg;t? z*qk~;G#3>xsLb2(-?hT=5d#P3l4ZR?la4)O5sY@X%jBzqt46sB7>XG7e_sHhZW2L$6MoCQsSNV?*e)_<<*t1ehk+oUKz=XunBXNu1^*=(thjxB3L}m zj;YtG(?WyQ<49{9NJT?h`;VS@P`EkCGAslcw!`Y{b`z^-=`P!(zpUoI&LVK>+2P}x z=l}ipwRq3-9sf(dZC>7Pzb~+7&Z~8t4Euu$LeE@b6uP)l_|gpJX__|dFRn0_yn0^R z*3MArBzxhF!2FNg5ElY{wIaCQx8bwGu_wQ{UWAJO`^BoKW#sL!!SC)omoTS!y9|K7 za#_I)iN@xtqd*rf;VgC7>|Aq9ALzV>ju>GFpLLuKait%-X6{mAPIdg_Cp+QQlEh_v z6IKRaPharA)uwbIiXlY{#bX>|fr%mEfZttH#+IDMTnk_@+|y|7?m8nR(2e9cV33C& z*X@M_;)nVD2m0lO4C_EK;Sy5o+pv9`%xLKui8B1>muAlLv{}7NAt}~mb@}W6?~Yu4 zxfCe8OVEw;)a<9XjpiS>KO8RZvZi}g0DFRz%oHY|$x21u$_~o{zW=j#kOTW45 + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/DualFilter/logo.png b/plugins/DualFilter/logo.png deleted file mode 100644 index 9340da708dd79ed97111eb535f51b81a91d6a15b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 774 zcmV+h1Nr=kP)7WEc)VQ)zLm`B#lSD% z0Wg;#IH?7|3b0p&fj_`2;A{cm@zx+(Ge?s$@EN$6LtMjg>;+(boCbaXw;ja=b6mQ?gRp67Yf18(18niCN0v&eY^{Cr&#;#IcF{ks?!* z&o!_q>9xbSw`QytRI!M?zP_o#K-8ExJaV?vi znPt?~0KhTu23U+Gy8^Tw;^SzWSet9n + + From b9cb343d8b9f2c396c07f5654c61c10aa73f1518 Mon Sep 17 00:00:00 2001 From: regulus79 <117475203+regulus79@users.noreply.github.com> Date: Wed, 19 Mar 2025 21:29:44 -0400 Subject: [PATCH 10/62] Revert "Change tooltip window flags (#7613)" (#7761) This reverts commit b21a2696a9b06effa0f4edd9a22e38ff13240643. --- src/gui/widgets/TextFloat.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/widgets/TextFloat.cpp b/src/gui/widgets/TextFloat.cpp index bd3ed03bc..4eb14bd50 100644 --- a/src/gui/widgets/TextFloat.cpp +++ b/src/gui/widgets/TextFloat.cpp @@ -44,7 +44,7 @@ TextFloat::TextFloat() : } TextFloat::TextFloat(const QString & title, const QString & text, const QPixmap & pixmap) : - QWidget(getGUI()->mainWindow(), Qt::Tool | Qt::FramelessWindowHint) + QWidget(getGUI()->mainWindow(), Qt::ToolTip) { QHBoxLayout * mainLayout = new QHBoxLayout(); setLayout(mainLayout); From 953f6b7351589cb5191d6ddc8280763f3d2a7572 Mon Sep 17 00:00:00 2001 From: Fawn Date: Wed, 19 Mar 2025 23:30:29 -0600 Subject: [PATCH 11/62] Rework Crossover EQ plugin GUI (#7781) * Switches to SVG assets * Fixes layout issues * Refactors redundant code --------- Co-authored-by: Tres Finocchiaro --- data/themes/default/fader_knob.png | Bin 226 -> 0 bytes data/themes/default/fader_knob.svg | 18 +++ include/Fader.h | 1 + include/embed.h | 18 +++ plugins/CrossoverEQ/CMakeLists.txt | 2 +- .../CrossoverEQ/CrossoverEQControlDialog.cpp | 135 +++++++++--------- plugins/CrossoverEQ/CrossoverEQControls.cpp | 2 +- plugins/CrossoverEQ/artwork.png | Bin 876 -> 0 bytes plugins/CrossoverEQ/artwork.svg | 18 +++ plugins/CrossoverEQ/fader_knob2.png | Bin 351 -> 0 bytes plugins/CrossoverEQ/logo.png | Bin 774 -> 0 bytes plugins/CrossoverEQ/logo.svg | 3 + src/gui/widgets/Fader.cpp | 25 ++-- src/gui/widgets/PixmapButton.cpp | 15 +- 14 files changed, 145 insertions(+), 92 deletions(-) delete mode 100644 data/themes/default/fader_knob.png create mode 100644 data/themes/default/fader_knob.svg delete mode 100644 plugins/CrossoverEQ/artwork.png create mode 100644 plugins/CrossoverEQ/artwork.svg delete mode 100644 plugins/CrossoverEQ/fader_knob2.png delete mode 100644 plugins/CrossoverEQ/logo.png create mode 100644 plugins/CrossoverEQ/logo.svg diff --git a/data/themes/default/fader_knob.png b/data/themes/default/fader_knob.png deleted file mode 100644 index 2190451d8408e97c2526e9fd4e76dd64366127fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 226 zcmV<803H8{P)Ck;(4|2uj362w4t5DQ2E|BcLT|LYi95W*mJP#<6d7F?$P z{PhckPmVe!oKFAupIA(W;l#s{ + + + + + + + + + + + + + + + + + diff --git a/include/Fader.h b/include/Fader.h index 9d6e21590..27e5e07a1 100644 --- a/include/Fader.h +++ b/include/Fader.h @@ -171,6 +171,7 @@ private: QElapsedTimer m_lastPeakTimer_R; QPixmap m_knob {embed::getIconPixmap("fader_knob")}; + QSize m_knobSize; /** * @brief Stores the offset to the knob center when the user drags the fader knob diff --git a/include/embed.h b/include/embed.h index 40a3622c6..f5156b02e 100644 --- a/include/embed.h +++ b/include/embed.h @@ -51,6 +51,24 @@ auto LMMS_EXPORT getIconPixmap(std::string_view name, int width = -1, int height = -1, const char* const* xpm = nullptr) -> QPixmap; auto LMMS_EXPORT getText(std::string_view name) -> QString; +/** + * @brief Temporary shim for QPixmap::deviceIndependentSize. + * @param pixmap The pixmap to get the size of. + * @return The device-independent size of the pixmap. + */ +#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0) +[[deprecated("Use QPixmap::deviceIndependentSize() instead; See " + "https://doc.qt.io/qt-6/qpixmap.html#deviceIndependentSize")]] +#endif +inline auto logicalSize(const QPixmap &pixmap) noexcept +{ +#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0) + return pixmap.deviceIndependentSize().toSize(); +#else + return pixmap.isNull() ? QSize() : pixmap.size() / pixmap.devicePixelRatio(); +#endif +} + } // namespace embed class PixmapLoader diff --git a/plugins/CrossoverEQ/CMakeLists.txt b/plugins/CrossoverEQ/CMakeLists.txt index 447c92c11..32e700d6a 100644 --- a/plugins/CrossoverEQ/CMakeLists.txt +++ b/plugins/CrossoverEQ/CMakeLists.txt @@ -1,3 +1,3 @@ INCLUDE(BuildPlugin) -BUILD_PLUGIN(crossovereq CrossoverEQ.cpp CrossoverEQControls.cpp CrossoverEQControlDialog.cpp MOCFILES CrossoverEQControls.h CrossoverEQControlDialog.h EMBEDDED_RESOURCES artwork.png fader_bg.png fader_empty.png fader_knob2.png logo.png) +BUILD_PLUGIN(crossovereq CrossoverEQ.cpp CrossoverEQControls.cpp CrossoverEQControlDialog.cpp MOCFILES CrossoverEQControls.h CrossoverEQControlDialog.h EMBEDDED_RESOURCES artwork.svg logo.svg) diff --git a/plugins/CrossoverEQ/CrossoverEQControlDialog.cpp b/plugins/CrossoverEQ/CrossoverEQControlDialog.cpp index e7202556b..93aaf0cff 100644 --- a/plugins/CrossoverEQ/CrossoverEQControlDialog.cpp +++ b/plugins/CrossoverEQ/CrossoverEQControlDialog.cpp @@ -31,90 +31,89 @@ #include "LedCheckBox.h" #include "Knob.h" #include "Fader.h" +#include "PixmapButton.h" #include +#include +#include namespace lmms::gui { -CrossoverEQControlDialog::CrossoverEQControlDialog( CrossoverEQControls * controls ) : - EffectControlDialog( controls ) +CrossoverEQControlDialog::CrossoverEQControlDialog(CrossoverEQControls *controls) : + EffectControlDialog(controls) { - setAutoFillBackground( true ); + setAutoFillBackground(true); QPalette pal; - pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap( "artwork" ) ); - setPalette( pal ); - setFixedSize( 167, 178 ); + pal.setBrush(backgroundRole(), PLUGIN_NAME::getIconPixmap("artwork")); + setPalette(pal); + setFixedSize(167, 218); + auto layout = new QVBoxLayout(this); + + auto knobsLayout = new QHBoxLayout(); + layout->addLayout(knobsLayout); + + const auto makeKnob = [this, knobsLayout]( + FloatModel *model, + const QString &label, + const QString &txt_before + ) { + auto k = new Knob(KnobType::Bright26, this); + k->setModel(model); + k->setLabel(label); + k->setHintText(txt_before, "Hz"); + knobsLayout->addWidget(k, 0, Qt::AlignHCenter); + }; + + makeKnob(&controls->m_xover12, "1/2", tr("Band 1/2 crossover")); + makeKnob(&controls->m_xover23, "2/3", tr("Band 2/3 crossover")); + makeKnob(&controls->m_xover34, "3/4", tr("Band 3/4 crossover")); - // knobs - auto xover12 = new Knob(KnobType::Bright26, this); - xover12->move( 29, 11 ); - xover12->setModel( & controls->m_xover12 ); - xover12->setLabel( "1/2" ); - xover12->setHintText( tr( "Band 1/2 crossover:" ), " Hz" ); + auto bandsLayout = new QGridLayout(); + bandsLayout->setContentsMargins(4, 10, 4, 5); + layout->addLayout(bandsLayout); - auto xover23 = new Knob(KnobType::Bright26, this); - xover23->move( 69, 11 ); - xover23->setModel( & controls->m_xover23 ); - xover23->setLabel( "2/3" ); - xover23->setHintText( tr( "Band 2/3 crossover:" ), " Hz" ); + const auto makeFader = [this, bandsLayout]( + FloatModel *model, + const QString &label, + int column + ) { + auto f = new Fader(model, label, this, false); + f->setHintText(label, "dBFS"); + f->setDisplayConversion(false); + f->setRenderUnityLine(false); + bandsLayout->addWidget(f, 0, column, Qt::AlignHCenter); + }; - auto xover34 = new Knob(KnobType::Bright26, this); - xover34->move( 109, 11 ); - xover34->setModel( & controls->m_xover34 ); - xover34->setLabel( "3/4" ); - xover34->setHintText( tr( "Band 3/4 crossover:" ), " Hz" ); - - QPixmap const fader_knob(PLUGIN_NAME::getIconPixmap("fader_knob2")); - - // faders - auto gain1 = new Fader(&controls->m_gain1, tr("Band 1 gain"), this, fader_knob, false); - gain1->move( 7, 56 ); - gain1->setDisplayConversion( false ); - gain1->setHintText( tr( "Band 1 gain:" ), " dBFS" ); - gain1->setRenderUnityLine(false); + makeFader(&controls->m_gain1, tr("Band 1 gain"), 0); + makeFader(&controls->m_gain2, tr("Band 2 gain"), 1); + makeFader(&controls->m_gain3, tr("Band 3 gain"), 2); + makeFader(&controls->m_gain4, tr("Band 4 gain"), 3); - auto gain2 = new Fader(&controls->m_gain2, tr("Band 2 gain"), this, fader_knob, false); - gain2->move( 47, 56 ); - gain2->setDisplayConversion( false ); - gain2->setHintText( tr( "Band 2 gain:" ), " dBFS" ); - gain2->setRenderUnityLine(false); + const auto muteOn = embed::getIconPixmap("mute_active"); + const auto muteOff = embed::getIconPixmap("mute_inactive"); - auto gain3 = new Fader(&controls->m_gain3, tr("Band 3 gain"), this, fader_knob, false); - gain3->move( 87, 56 ); - gain3->setDisplayConversion( false ); - gain3->setHintText( tr( "Band 3 gain:" ), " dBFS" ); - gain3->setRenderUnityLine(false); + const auto makeMuteBtn = [this, bandsLayout, muteOn, muteOff]( + BoolModel *model, + const QString &label, + int column + ) { + auto b = new PixmapButton(this, label); + b->setActiveGraphic(muteOff); + b->setInactiveGraphic(muteOn); + b->setCheckable(true); + b->setModel(model); + b->setToolTip(label); + bandsLayout->addWidget(b, 1, column, Qt::AlignCenter); + }; - auto gain4 = new Fader(&controls->m_gain4, tr("Band 4 gain"), this, fader_knob, false); - gain4->move( 127, 56 ); - gain4->setDisplayConversion( false ); - gain4->setHintText( tr( "Band 4 gain:" ), " dBFS" ); - gain4->setRenderUnityLine(false); - - // leds - auto mute1 = new LedCheckBox("", this, tr("Band 1 mute"), LedCheckBox::LedColor::Green); - mute1->move( 15, 154 ); - mute1->setModel( & controls->m_mute1 ); - mute1->setToolTip(tr("Mute band 1")); - - auto mute2 = new LedCheckBox("", this, tr("Band 2 mute"), LedCheckBox::LedColor::Green); - mute2->move( 55, 154 ); - mute2->setModel( & controls->m_mute2 ); - mute2->setToolTip(tr("Mute band 2")); - - auto mute3 = new LedCheckBox("", this, tr("Band 3 mute"), LedCheckBox::LedColor::Green); - mute3->move( 95, 154 ); - mute3->setModel( & controls->m_mute3 ); - mute3->setToolTip(tr("Mute band 3")); - - auto mute4 = new LedCheckBox("", this, tr("Band 4 mute"), LedCheckBox::LedColor::Green); - mute4->move( 135, 154 ); - mute4->setModel( & controls->m_mute4 ); - mute4->setToolTip(tr("Mute band 4")); + makeMuteBtn(&controls->m_mute1, tr("Mute band 1"), 0); + makeMuteBtn(&controls->m_mute2, tr("Mute band 2"), 1); + makeMuteBtn(&controls->m_mute3, tr("Mute band 3"), 2); + makeMuteBtn(&controls->m_mute4, tr("Mute band 4"), 3); } -} // namespace lmms::gui \ No newline at end of file +} // namespace lmms::gui diff --git a/plugins/CrossoverEQ/CrossoverEQControls.cpp b/plugins/CrossoverEQ/CrossoverEQControls.cpp index 9758d804a..928797dc5 100644 --- a/plugins/CrossoverEQ/CrossoverEQControls.cpp +++ b/plugins/CrossoverEQ/CrossoverEQControls.cpp @@ -121,4 +121,4 @@ void CrossoverEQControls::sampleRateChanged() } -} // namespace lmms \ No newline at end of file +} // namespace lmms diff --git a/plugins/CrossoverEQ/artwork.png b/plugins/CrossoverEQ/artwork.png deleted file mode 100644 index ffd2788abed140675ede4a4beb1154ccd306a72d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 876 zcmeAS@N?(olHy`uVBq!ia0vp^%Yk?k2OE%7=e>UfNU;<$qHLLJyXgZhPq&?2_(r@OaeJoP5 ze?I%oy}kbp_&Ii2eP=x&RX9o2Z_^RaX%;7i)FU6Mc+W}kP5vfn0f&YipF>dDN4qCBumPv=9N^T;tSWhRQNElzHl>M{)+lbbmGo6`?%Rt1}F zxy%tFiWE2Q&7KjjE}W8y%zF*@xQ_JYgqt&TKPu=W`~IB3#MGimsoI?wG1 + + + + + + + + + + + + + + + + + diff --git a/plugins/CrossoverEQ/fader_knob2.png b/plugins/CrossoverEQ/fader_knob2.png deleted file mode 100644 index b5238ac06c68fe21520792d9f464550e3d75e453..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 351 zcmeAS@N?(olHy`uVBq!ia0vp^3P3Et!3HGD8EPYe6icy_X9x!n)NrJ90QsB+9+AaB z_18g|(Me-=1yE43#5JNMI6tkVJh3R1p}f3YFEcN@I61K(RWH9NefB#WDWIY|o-U3d z9-U{W_+}k85OBMHWYVS5%x{)XiVw;j-u}Y2*JoXbwAZGyyi!7&xGW-ymaS$x?KjOy zR^o?5%f99FB?Ci?j_ItnZdi3sXhnn61(!(Xs~3XX{1ueGzJ9g0rY=&&^3BVNgx`O! zu6T7j{%p^+DM1o--+rqw6hCJ;m6*%pa64|&4EtNvOa*(ton&}-lqKQc#v>8l4|$Hw zim>)hOy<4t+u?=H!S;yq#s|q~pR0X3eXnh@w8i@JO2$q98y_$$O*-=C-VJ$XliX^( sxz%1w7fQt&(wx5LyA}4GxYxEnOlsvOp&fNRKz}lLy85}Sb4q9e02j`V4*&oF diff --git a/plugins/CrossoverEQ/logo.png b/plugins/CrossoverEQ/logo.png deleted file mode 100644 index 9340da708dd79ed97111eb535f51b81a91d6a15b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 774 zcmV+h1Nr=kP)7WEc)VQ)zLm`B#lSD% z0Wg;#IH?7|3b0p&fj_`2;A{cm@zx+(Ge?s$@EN$6LtMjg>;+(boCbaXw;ja=b6mQ?gRp67Yf18(18niCN0v&eY^{Cr&#;#IcF{ks?!* z&o!_q>9xbSw`QytRI!M?zP_o#K-8ExJaV?vi znPt?~0KhTu23U+Gy8^Tw;^SzWSet9n + + diff --git a/src/gui/widgets/Fader.cpp b/src/gui/widgets/Fader.cpp index 61e6e753d..7ce85963b 100644 --- a/src/gui/widgets/Fader.cpp +++ b/src/gui/widgets/Fader.cpp @@ -73,6 +73,7 @@ SimpleTextFloat* Fader::s_textFloat = nullptr; Fader::Fader(FloatModel* model, const QString& name, QWidget* parent, bool modelIsLinear) : QWidget(parent), FloatModelView(model, this), + m_knobSize(embed::logicalSize(m_knob)), m_modelIsLinear(modelIsLinear) { if (s_textFloat == nullptr) @@ -198,7 +199,7 @@ void Fader::mousePressEvent(QMouseEvent* mouseEvent) const int localY = mouseEvent->y(); const auto knobLowerPosY = calculateKnobPosYFromModel(); - const auto knobUpperPosY = knobLowerPosY - m_knob.height(); + const auto knobUpperPosY = knobLowerPosY - m_knobSize.height(); const auto clickedOnKnob = localY >= knobUpperPosY && localY <= knobLowerPosY; @@ -207,7 +208,7 @@ void Fader::mousePressEvent(QMouseEvent* mouseEvent) // If the users clicked on the knob we want to compensate for the offset to the center line // of the knob when dealing with mouse move events. // This will make it feel like the users have grabbed the knob where they clicked. - const auto knobCenterPos = knobLowerPosY - (m_knob.height() / 2); + const auto knobCenterPos = knobLowerPosY - (m_knobSize.height() / 2); m_knobCenterOffset = localY - knobCenterPos; // In this case we also will not call setVolumeByLocalPixelValue, i.e. we do not make any immediate @@ -361,9 +362,9 @@ int Fader::calculateKnobPosYFromModel() const const auto scaledRatio = computeScaledRatio(actualDb); // This returns results between: - // * m_knob.height() for a ratio of 1 + // * m_knobSize.height() for a ratio of 1 // * height() for a ratio of 0 - return height() - (height() - m_knob.height()) * scaledRatio; + return height() - (height() - m_knobSize.height()) * scaledRatio; } } else @@ -375,9 +376,9 @@ int Fader::calculateKnobPosYFromModel() const auto const ratio = (clampedValue - minV) / (maxV - minV); // This returns results between: - // * m_knob.height() for a ratio of 1 + // * m_knobSize.height() for a ratio of 1 // * height() for a ratio of 0 - return height() - (height() - m_knob.height()) * ratio; + return height() - (height() - m_knobSize.height()) * ratio; } } @@ -391,11 +392,11 @@ void Fader::setVolumeByLocalPixelValue(int y) // The y parameter gives us where the mouse click went. // Assume that the middle of the fader should go there. - int const lowerFaderKnob = y + (m_knob.height() / 2); + int const lowerFaderKnob = y + (m_knobSize.height() / 2); // In some cases we need the clamped lower position of the fader knob so we can ensure // that we only set allowed values in the model range. - int const clampedLowerFaderKnob = std::clamp(lowerFaderKnob, m_knob.height(), height()); + int const clampedLowerFaderKnob = std::clamp(lowerFaderKnob, m_knobSize.height(), height()); if (modelIsLinear()) { @@ -411,7 +412,7 @@ void Fader::setVolumeByLocalPixelValue(int y) // First map the lower knob position to [0, 1] so that we can apply some curve mapping, e.g. // square, cube, etc. - LinearMap knobMap(float(m_knob.height()), 1., float(height()), 0.); + LinearMap knobMap(float(m_knobSize.height()), 1., float(height()), 0.); // Apply the inverse of what is done in calculateKnobPosYFromModel auto const knobPos = std::pow(knobMap.map(clampedLowerFaderKnob), 1./c_dBScalingExponent); @@ -433,7 +434,7 @@ void Fader::setVolumeByLocalPixelValue(int y) } else { - LinearMap valueMap(float(m_knob.height()), model()->maxValue(), float(height()), model()->minValue()); + LinearMap valueMap(float(m_knobSize.height()), model()->maxValue(), float(height()), model()->minValue()); model()->setValue(valueMap.map(clampedLowerFaderKnob)); } @@ -553,7 +554,7 @@ void Fader::paintEvent(QPaintEvent* ev) } // Draw the knob - painter.drawPixmap((width() - m_knob.width()) / 2, calculateKnobPosYFromModel() - m_knob.height(), m_knob); + painter.drawPixmap((width() - m_knobSize.width()) / 2, calculateKnobPosYFromModel() - m_knobSize.height(), m_knob); } void Fader::paintLevels(QPaintEvent* ev, QPainter& painter, bool linear) @@ -727,7 +728,7 @@ void Fader::paintFaderTicks(QPainter& painter) for (float i = startValue; i >= c_faderMinDb; i-= stepSize) { const auto scaledRatio = computeScaledRatio(i); - const auto maxHeight = height() - (height() - m_knob.height()) * scaledRatio - (m_knob.height() / 2); + const auto maxHeight = height() - (height() - m_knobSize.height()) * scaledRatio - (m_knobSize.height() / 2); if (approximatelyEqual(i, 0.)) { diff --git a/src/gui/widgets/PixmapButton.cpp b/src/gui/widgets/PixmapButton.cpp index bd683ed71..b456926ad 100644 --- a/src/gui/widgets/PixmapButton.cpp +++ b/src/gui/widgets/PixmapButton.cpp @@ -102,17 +102,13 @@ void PixmapButton::mouseDoubleClickEvent( QMouseEvent * _me ) } - - -void PixmapButton::setActiveGraphic( const QPixmap & _pm ) +void PixmapButton::setActiveGraphic(const QPixmap &pm) { - m_activePixmap = _pm; - resize(m_activePixmap.size() / m_activePixmap.devicePixelRatio()); + m_activePixmap = pm; + resize(embed::logicalSize(m_activePixmap)); } - - void PixmapButton::setInactiveGraphic( const QPixmap & _pm, bool _update ) { m_inactivePixmap = _pm; @@ -129,9 +125,8 @@ QSize PixmapButton::sizeHint() const QSize PixmapButton::minimumSizeHint() const { - const auto activeSize = m_activePixmap.size() / m_activePixmap.devicePixelRatio(); - const auto inactiveSize = m_inactivePixmap.size() / m_inactivePixmap.devicePixelRatio(); - return activeSize.expandedTo(inactiveSize); + return embed::logicalSize(m_activePixmap) + .expandedTo(embed::logicalSize(m_inactivePixmap)); } bool PixmapButton::isActive() const From 23efcf456b783b06ddde0340f509bb1b2be5ad19 Mon Sep 17 00:00:00 2001 From: StakeoutPunch Date: Thu, 20 Mar 2025 11:20:03 -0500 Subject: [PATCH 12/62] Update Classic Theme (#7799) Created and replaced missing/mismatched assets, fixed mixer send arrows causing mixer height to be wrong, tweaked CSS stylesheet to fix active mixer channel being black --- data/themes/classic/automation_ghost_note.png | Bin 247 -> 2551 bytes data/themes/classic/clear_ghost_note.png | Bin 833 -> 2608 bytes data/themes/classic/cursor_knife.png | Bin 243 -> 456 bytes data/themes/classic/cut_overlaps.png | Bin 0 -> 1925 bytes data/themes/classic/edit_knife.png | Bin 205 -> 1998 bytes data/themes/classic/file.png | Bin 0 -> 286 bytes data/themes/classic/fill.png | Bin 0 -> 2454 bytes data/themes/classic/ghost_note.png | Bin 287 -> 2411 bytes data/themes/classic/glue.png | Bin 0 -> 2446 bytes data/themes/classic/gridmode.png | Bin 0 -> 2060 bytes data/themes/classic/insert_bar.png | Bin 0 -> 540 bytes data/themes/classic/max_length.png | Bin 0 -> 1957 bytes data/themes/classic/min_length.png | Bin 0 -> 2047 bytes data/themes/classic/proportional_snap.png | Bin 242 -> 2128 bytes data/themes/classic/receive_bg_arrow.png | Bin 189 -> 1827 bytes data/themes/classic/remove_bar.png | Bin 0 -> 386 bytes data/themes/classic/send_bg_arrow.png | Bin 157 -> 1762 bytes data/themes/classic/style.css | 91 +++++++++--------- data/themes/classic/tool.png | Bin 0 -> 2495 bytes 19 files changed, 46 insertions(+), 45 deletions(-) create mode 100644 data/themes/classic/cut_overlaps.png create mode 100644 data/themes/classic/file.png create mode 100644 data/themes/classic/fill.png create mode 100644 data/themes/classic/glue.png create mode 100644 data/themes/classic/gridmode.png create mode 100644 data/themes/classic/insert_bar.png create mode 100644 data/themes/classic/max_length.png create mode 100644 data/themes/classic/min_length.png create mode 100644 data/themes/classic/remove_bar.png create mode 100644 data/themes/classic/tool.png diff --git a/data/themes/classic/automation_ghost_note.png b/data/themes/classic/automation_ghost_note.png index d4b1416f5e4b68e1ad2ff0cb253452f682bde891..4b06b4073679a80535d1c92e51fc3931a1f52da0 100644 GIT binary patch literal 2551 zcmb7G4LqB79)EDs4k^YtOxG-I(@kDAQ6XMKOGUi|;i?tMlNa*VJV6rcU8MV%t)0!N z9<8!fXuDEYN~;!A8)~<(TdUV@rY}2&7Nh9QxhILzR(JQg&nHjv{QuwI>-YbCetF)F ziQa*T4h*pJS5V*p zB2dmkDMUgsL{U)D#<&#t-mr~Bql_lWFU%!C1LUC04tQgjKihG(ZW}p z;T09lm&>IT94;p(2b<%KmB@HFg0HVH4o}1pi5S=d11ZIFmI5P&+zla2F=!yfmIx z7?ur+b69c~jU|UGCE|%B4Bi_< z@MRE*6g-hafR`i+9&hZa;ml}R{RY>;D2ZPdR3g(O^g4oe0G^WX!fqJz0y0nCFE6=M)Oiije@ z52er3E;)SAaI%kE+r}VzQ%Jt>XQegVbBi%b?stnZq5oGgCYNW5S;zBAG5$Ei^Z%GA zf807MO$JX447dr?9z2NPiH2JpY4BooD1u^m63XC_SK*as2LNVy0$8T&bBupe-3va*cbrOW@-Is;jL zu*y@zuWvr3y_0pqD)QdN*o0vB^@jxOpnyxmO}g}x4sT-j z5WP=im2FT%~bW&Y)UXmxuZ3{x9i1DUZLFhbrAnnKe%1>BtZFc!ePq=Ynn7Hrn`+VOnvK=^-P&W^_9h3eg z;o6-xX`^N82N_X^j(8kvB^}YU#Pbh@bR)OnN^=wcIdabo{n9T9gAFaVOR}NHS?kO) z-l)ZW-D~iF*9+2}$q)coI~XoAp!~Qq0GNLpK?`PVf4}0IF4KYK);&GHglA}9@fghf z73sOjUR#2Vwi>!uwZM+=W$dvo)$Drb>Z1*gmLIomGP9?=r|E?HztqS_Z(J;E+SW`K zl#k#2Ft@NeB4a#9^#JM}fBExye|@qI_N5+R=HebPp}3dh*4=1!XL~?KbILn@etr^l zOVDP%`Lx}em<_Gt%AvdhzP2P_Z{$`C`sU1Krzx8$HKZolH0R}hnNE0s7=NhRB7Ql> z%ZZ)lG-Z}kMe*@D`tX2G6r(LkjQly|jrK+Ift=gu^#m31+pS+2NWL!bP^^|eE zO*5F>R(SU}#NNWf!kR}nZZO;Y9PE*oE$TfcYg+vEnPti5Z5>3s#rJka?%I-Xev)nI zupa)Zr>AGjOs; z;WC%`jtipN!NR-DAeZex4W{oiz{$x83!KZ_&2s+NK@{whcI(2CswFCUiPZk}$P7k5 zlY;)_3VuSVjNIC;(^X>ItM$7_z1sJt(q^w;PgHFVi=}MRsPEqw0c0}y6yuGb+f(Z1 z@Gk!b=F~qp+^((#&eqi-uhvwAcE?)e46SLZ9ue^gfk delta 230 zcmV|g!axv3hn&F>!KCp9dIRwQ zvB{;h&JEJUTJ#=TDQGRIWrS2umW1HA*+m3jGk<5_FeC2}u%O^VMa7wdltB7n%7IVz zOvHvcPx`2siH0E$T-=GYyrM(I%1bSuyz)`YHHHl%#v)d(wO`y2cQ2iB$|Nc~HTObC#a5C}m)2!#+NSQG-8Bm*QfCWC~Bim*~W z=u(eCt8%a^LF)q_)kR2a5N#>aUGQ*xKNj1%fVKZ1?P6drl_FcYpW! zyZ5`vhP2cKcd8#1008%-M6s0k^d_I_&cyq<6UFZW0Hqg^$#J-!5Sp7Tn}fIr^*V-iwby3x-gm= zW#khMv@ouq8MVba1K%j1+x+s0@1&SXr`c5SA^}}4Nu$M~dYHxqxgd)kO{GQYl`6he zylC8*I0@()9LM-fW@%|DSjquWy_(79@pw!Y#DpLQp}{be>2QUSp)&-MF6=(Uu%SSY zU^s&6Xrxa?K3akc=yU?7jXQ^p$fS3jVZ3~T2quXz*&vJgqB^EQanztev5AacvYCLV z7~+WPcjA!96m^2W*GDiau-7R|B!8@~)xK1Q0gqov5HU{bOC>xHV33tzFjEQ}&=P$C z9KRCQ;ej?LNM87HXfdkKAVvcg(AksXG{guO%f*Nm))@%W1#}2{1BpB=$qarrEktvZ`Nszg*}|4o~mXj&AZFOl#Q5d)6u%WNY1m}kH%>?ixN7_rSa zIpL(fqJXSUK*#i`vZMf3+CtmrpF|C)3NKaY;aD}%U;#Z=r9ucE%4liipwl24L|n>< zmrY7FFxlDXrG?2L4xh^-UgM=P|BuOVWh@q-9m(fL{$Vn<`Tt#J?CAf*jNRlFW~MZK zoS7&lxs0B7Es8m%uXcqOwim&uT}Et0V!bja_A7Ci*wC9A_eHpgz}$XOtkn?R zF?hn7zjt;Bn8S|GQg_Xsb@$qkz?*Zc{OhHU`kodsHZ-?ZnUjnu=_jkln#Ue4jpddl zoXY4q_ePhxOD6$?E7f=JL)MQP!3S+UOM|?CBFj zmrL^(KWyF@dQbK2+2hRj+$(x7zW=oU=VIT{ReArY-L*$i`PQGl>|XC|mR5_f3pm7r}yA6f#}fQ*y5r*5w*tGrb|`s>Y6M4arBl6Ud=A?`q!q|r=63pbQDGn}p!=$iD0qSS?lzNj#vIePk?w=ugTQ=1>nn5d zhPH3?p={*Sw)7a$+Ycg)lyme?LPA0|^z`&B0UD&WxkA?1!e2f+P<^2K$xP-4nX9s< z?>0%#mn<_Q*IE_6R_%3NN2~vnPhnk4&d|y2MaP<=^`kQ5>;oHbfAN%F`7~Q=Odz8rz_|0e5uDy``-vQr_|LoAHY2+Whr1(^Et0=GfKN%*iIsgCw delta 811 zcmV+`1JwMm6u}0NBYy)3Nkl)n&nTRmHy$>m@5jpu7OsV!coPxp^CF>y=x@Q{=wCu=0d#ZZx*4Ozavs*sN#^YvG) zx^kP4PNboEL`W;|`}duxq^_uBGVr>4S9zy>`%6~{X<4WA27e)a+^$_K(P%W#&?Hz} z!_8K$g6O}sN`Ea~T1KUfjql^ffVIx#<+|qMi@^Md2w+hlJCXLt2}CV&6(H5XQk9nn z*PAz|@_@g(1#iy19zPzqTqj)c{tNf+D0}uF5?h>@2oHMpq*8%1gochR-jrJH*;CB@ zyj3K=g3zeB%71izHgu?$AbdG$R3V-Vs0-;xt`f49zDRa-^y0+nNJ}Q|i13B3zedO$ z{3io}t4*4K>*dR!lFRM!{sJp#3oY3MktnOH4S&Kq8ucSUQ{A_b&1Qfvf#;} zL0aLhcI_bO?OPCs6Z=vpy{M>FjB`5t+c#Y;-%XnabQWOh6IalSczPoi%PTA4>%4hd zA@(UaojC()g#s}3iPMFyFhNRjRP}%*%ikYvwr;JJKJ3#6Qe9l2zOcdTjc5=f-E>QF zaq-lsRDWQ9{J5^^dMj6gq^Jn$3n$)(06I787Rc9y3yYo(9;_A8+6KLO16*WKPjI?A z;J>Lz3DS|!UQsp{KoR=+Gk?SjFNSsKQ9%F zTMOOtbMaz>I~_X;bGQ`G&d68f+s(FZz42Uyz>QX|q_j_2^Xm@){8y2a^UY-A#@G0h pF(eX;R8sy7%3cG+3ESbbsKdu*m=b002ovPDHLkV1oUpoMQk0 diff --git a/data/themes/classic/cursor_knife.png b/data/themes/classic/cursor_knife.png index 591df2d62a0b09d2e88a8c3845d2c5c57e2003fc..8ddbd3ac4e784f15adebb129f707fdcd68672257 100644 GIT binary patch delta 441 zcmV;q0Y?7w0muW88Gi-<0047(dh`GQ0d+}4K~z|U#nnGJH8=K9BFn7Vl=Q0D!qA2D7hr^+DIKU&2fd-zbaT3=(_-$LDxD%rG(!e`>Uotvg>I(u_+zH2h{h|N>01jnX jNoGw=04e|g00;m8000000Mb*F00000NkvXXu0mjf457f% delta 226 zcmV<803H9x1M>lp8Gi%-007x@vVQ;o0J=#;K~#9!rO?3*gD@0>Vbeiq!de}KD@S(d zF5LtIlepsKNI(>vKWBeZ9x$UZwh|1D1a}Ti1j$4+5IoXHOh6y80g5mI5iTIY21M9^ z2pbS#10rldgawF*A}Ror5MUEb0xtRy#1|t1ZW;sAEwJ*<$Xk&gV#iP*P?&E#n#u&2 zO7j|`=W;q<&y{&|HIdH^BA-7e)_m>|QKvgqdCpFAi|AgYGCtB_pK6H>0T)GV4~WZp cEMVRH1FS0k!6UC|e*gdg07*qoM6N<$f&y4p1ONa4 diff --git a/data/themes/classic/cut_overlaps.png b/data/themes/classic/cut_overlaps.png new file mode 100644 index 0000000000000000000000000000000000000000..e79e4950a871b4fd9a6a911d0b46305af8fb6486 GIT binary patch literal 1925 zcmah~Yfuwc6b>UYG?t>MXj^U96$Hj6n*@kvn*~Dx1SAmNFtx~%Y?2j{jk_BXP;5&p zqWEI8S{R-BsC|INQYwta3NoOA4i!bFTCIxpH5C-8ZM9Wza$0XdlT=Yd&R~VX zCYH6L2x7O}h4x?}MH>;ZOeRA_5=0^q0EU1mu&}s8U}0u+5-trD!RTnx%94}?;xzFb z%En?a3~*>joFxq->K0}weNYI5LlCi0ggi|r9OMh=95RM(HBl_Zm?-OT0;9GVhQ~O9 z@;%?hfm@$P)@c5lZZ?lfgJC1{K_P~UIx40o8!#yaRsu;P7|KTLh{$}x!p?S9f~yM} zW5$g{9APoCCJdH{o&nNH{j+vXD9^@qBGp2IttBHa86y}5&yyhH5h(My5LaqcOFD?W z3>9f6EDTT`j$#DNd;zGW@+dkP9BTrDU3(4p0d-;;N)Mo5XBZ<;j8dtkDFc}YFeWKF z0*Z-LO2sm%R3H($WI)qU;7b|`R5%M*5|JcSAPN?ULsBHcXov_64iSh#QIV@$PByp; zdYr}okI$WGNDe$S8dOa(EJYVMO|D~}Oe}S+T+2MtdA=dY*<}>haaG4)D^2NbIzsPE z3&^=phBB~roF)`T&|nN!7z`xnPXVOmPA(*YB;Zoc8|`8@5zcbIC^=*(G=#fyPhyiJ zW1^Qk2~XWo;ODY|yAOs4GCW1VVt5V_7BGBhFlF|vDFD;V?E@0@WlTSKyufl>dE>%y ziixYjC%2{h75n=7D!OC+qdeDSm+wf_miZO$nXNVhND3LQ@S#w)N?dWipU!go{m zj#nhQ*Y>1!MP1#t@FRC|-HFbIrVFp;8xQ-&2UR8h<`KI6%!h8D=B~iIQXd!343I{V zYXeK9lPf)w&xG}VS=*~=O09ly;JvR4{0)KH(~$ggQ(>AT$x$e%&O9hMU*k7>Zp#-f zMc*&d{?&78xBrUz`H8(q**)t?@A@=6eOK}BseAowsTo=E4V4{1O@G#A^_K3r9^up2 z`nK59r+HOb(2;>F#KuV#Km2|LUyzp4Zl5-2Y**L7by06@PxbL@KNYYfzUrvok;o10 z>hgvMj~n(yEkBlA)qh8EVH0`i-hzj|%csQ+4xS8Y+R*&SbJ|UM#Wh~Z#YklH{{AAX zrTHw8S=Qfte-0}*O~R5k-|DS*%*aaELAaf(*-3uYU3al){q0!?g^O+D8<(A&dwu)y z@I@)c%Qw)<8{l*Cl2x6_@OTp@bB`OZa@%xZ?2Az;Qs%F_&@GN_T`=Xyz~U{XEz+#! zQ+>A%zF)-Om-F$Gg)6&%?(?s2pY@i!nE$Ps|9V$!MroTWv#s54V~2Oi%O|!zUPj%$ ztFCVGm^;J!PQd=4b5(6vXVSH^yzCy#Ta-}2?EI|lm$D{X2z0~y(k@f=@lSSkb{rKq zS2zrRE27;3&ivY(c=eJ8RbD69BCEHhg?X9>wuT*&M!xfID{Nb9Tkqe~i{-C7{iyY( zPvvz-Us~MlaoHCi7#APt>9v*?dSK3xQ$+5+ImBL`eB?Nj799$%gC#Aw@@_ zLMhl*P(;+Ko!VkS!BNzLgH8pk76r5_)z;72>9kd_YN17kz61mjZGUXC@11kcyZ7Ap z-j*n1m$E%(c|Z`vmPg9sz`u{Nu2~);jiE+EKy}0sC|PxjZDq0}i(8^jMrMVk8)h zB#41wEEtOB=H_y9g0{jFdGVY$`&6@SQOZ=Ps=nH8vkZ zm@}RS9GMxj9j#q%CP6KNF|EWgOo9Xm&}WuOdLcC02h5qBg3^KHgigURNG_Est(2ZL z0*sDZx&)SoOGSLKNW>9vhgSei!$2&l&y(Q{Uy19d>)7AkMV}B zaB9vW^6Ona~di zTCg#DW9K57%I8$*18M6A0v1bAQFO8FaZSmFwwPDfw{9H^#}LE*VSM_H)8`zpZpofBwDxYjjh8azg|im3pLK+M+!F&F;1DaYeo6 z{PrU6AZ~J2_=+$6ZmWK~GRT=OOdagdJnDRs$vM4g|Jprrn_AUa-uHgr_fc~Hyy%|2 zy4&)b-gy1|89puiN3!)hW6r4uF6cP3rWG`%^z8V%tt72Z*LbYqnD4TXBX#OtZA&g2DUz$endH71xIsyy6bSEOxO0QJamOb{U zbj`y}Wxlogp`pK)FY{z4d==!{)n}MlFX=k!o<^J;2;`O3OB*C7@^)9I{aAc6p!+-j zvj?Y`ez(|F7rd@o4!i8=UzJ=s6e-He#MtPmu33d2Ei$CmJgjT#?4BdL{BtcA&zNDq zlYjMAfW|Z9MonXJ75H4ZTOyNa2x56V9v7&3`%KUwkz5w04&1P>n;+{MHnZ*ikTBuX z6LGzeL}PQMR=%M;ElO~ES24d`m)?Aeci*F2d8MlA9g)g!I=2D2rP*~FIrz%d`s(zS zv};?I)h!?!?Loixvovp39=iHATYq3pZKLnNjOa?2l%C5g`$Bo1(Rfx@@oLx1(t?mb zR;#)+&9_gc|C0QJrkQP&V_VccwHLSduomkJE?tS=#dRkir+77QKI3!PGaRide$rBU z=g&z+)SaZN&m-zDvkKv(XU>T~7ECE((FJt6OvT(?D0HzmEG}$Q`AlxFnt~ekN!+H< z)o)#>GnajB!TV^R%)9Q&&4b>gfrkX|9&!_w|Z?Kj5!toiFhsVnHFIide EFLkcX{{R30 delta 188 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 literal 287 zcmV+)0pR|LP)423po0~`Bsvg(f>oHoB1?k76R?_G@BqZ72cW^=?3&e};F^YkRaq3vf@5wl z5p=jq`It;(!@Mg!@Z9l3&atwlrm})u{3fI5uWOT?+nkDCDrO_XhzI@L8xDE2ZAr_x z0NlCM0-NC$XOS@K1Bc0812^FXN0Bh3j^#*rMJ=>ZH5K1%fSqJ#fh0h~sbXh;I~Uu8pizL>P-O?hV_K lkTP$=fyL;OSCWbzz5r1rEtsK6s^I_t002ovPDHLkV1lD>eHZ`$ diff --git a/data/themes/classic/glue.png b/data/themes/classic/glue.png new file mode 100644 index 0000000000000000000000000000000000000000..c5d059a09883d23caaa4b3e6c2c76bcc200256e8 GIT binary patch literal 2446 zcmai0d0Z3M79M0#mV(HlBEk^BQZWk&AjAX=Bqb=JVUxgXkw_*aKr$g2OaK?)fr?05 zK@btDzf>zl*(yR6EX0jg5k>Uj22v;>ltqgz&j)HJfdW45dw)!ld%o{{_uO;tIg@O5 zpx*}uRt5k7eBjTd2czGX=wq=&5B>B3Z3+NDr$!jcK{%`cattiOae44I2&WK9P#gd# z-U&|tfTAFy z0wM_Ef(lWBSVmS*F&bSm`de+rV?YfBiKk*XEH>y1OCit==Y}I-ybM5!l*cCr(?6bd zMpsmf06`>VJYFuBMgAU1Jq(TWI zgvFp*le-O0LZ}!Fii5M_h>$<8E|$$6A58>ajo@8y1pGoeq!9iMU5&g$mk3}4mI+|V z+yUO&Vh(=C5l!FwT@+l&`^YTT|E7yXZyiI1Ft($Km`&*vQG{Mh9{=U9!Da;oppqMP!&j<8p_`17zy1QeExEUF=Xk^q^$WNwo5tKzF5Z$l@ z64u2vlt3VpT*yRMEWwRTAZU~|Y_u16T!j1o`05)CQcw>Ti_8?t5LlY5G0j}_5GZkG zHM2|*YVJ1(YN|};#;CJS#Ym(uFDVA%X*NgYW>6W-N90^7M2kfWOvTXnd?8w&WRR`C zxgZfFqD{H}Z5O!!Qm6ZG4~L%(4XA0N`{m>0rZVZQ9bk-e!)<=UJAPmNaFUdG6BM8s=@M zGR87h8B01~_erycRb?aRE6mN-2>{8OY;kM`zMMiR>J|~r4ckZ7m!q>DACurFhd#O)#p7mW-xtjg^8KJeP)kaT3a&nIQ#pKn% zt%C)hGWZ3HtWG;H^DaBaMU}TWTw{Fk6Elb~^`^cmWyh7z%EgmDm-D%ezo&}~f5_SM zJXx^fiC^^2%$xv6?^NLHapiL7Vwvqj_nwjOlni;#4zmlkclTMoEI89!sIZRu1B|jXJ_RjLsxE65I9QNU1E)B<_UZ`=^tNwqv$W z&I36=*4R%uV5djLaCpdrZ6zzSvm!}G=Ux?@d^6x`+RpKDj&hAF6L}8YZ!zX)dawi~ z5kX#+#wMXp&X)(=`jQtEBEs$wR2{Z<{%zw3P~Xh44ln_Z!`WX(7UzZ0vmDx&VLTg^ zm-AZ=0)uAJ0spj^(wCS~OdnX~7;p72ulcslX3h3Ns-0tp`^9U^f+Y4j*FV~Kk|ixk zOta-IO(+vBr|%sknu_u#N;0nb9={6+%rmc@VjJ+MKOX(H%5-RVG*ae&$okuY;Nk{r zdWq{1eSRjb!Xuio(L;A*^N>z$4bZe}^77&M^kXGGhS7p zPjJ>NT%pY!;BDroUEOu(=;W%qeILT5E2mwgyB-#ut+~}#`Qz3Q&XK&*T(2hEwI2Wa zAr)}UNhfFnS|mRgBGHPHqAWkHntW-L_WABy&!(f_ubZd?m#p^f>WVJj)0(|v+HtW; zN8dHN@hh^Ztnk7vz)I_k@e*FMFEi?~vX_t+*fG6@aog;!GFI!wbk*YnEfZ_6 z69L$R{fyZ;)t383)2;eP!x+mh&ey-D@4XPUnsieNsB~BFG`J}^)L-~;WJI;dKTfYY zH7>rOqfokd<5)(`P;IDn-MtN^-62Cmb{p*O0bADOwzdiSh8nBi4BQ*tKjD%V+&U4G z7g$|6lGJlGurljWCtlyfBK^d9_vU?I_l{S+cP=5lFA}iO%>Fks_`x literal 0 HcmV?d00001 diff --git a/data/themes/classic/gridmode.png b/data/themes/classic/gridmode.png new file mode 100644 index 0000000000000000000000000000000000000000..20b65fa47c32b2a3965e4c003a8b44ad0256b459 GIT binary patch literal 2060 zcmah~Yg7|Q6b=;-vA(c?wYF!wK}0>tCV?Q?CTJjupavs^0IjcuEXfMVhU_9s1g-ck zEsD}wIigrj>jOm*h1wvXRs?IQ5476WBZ5_1735G9?NK|MP!J#OkA2L1-~H~Ld+*Gw z(?m@l<~i1r!{H2zj8H^_?-=&+a0l;}3|S9{<62H=hWx zbSh)Pl2ULQO2iF>Sq695Yhj4c%iuV%TA;Rs<3=LFX2oM{QQ9P1N|HnmPYv~yGAIx* z;WP#@rc^V9GBVhqi-Lc48i64Pg-(&d@oEhePFitD%ops1F zk)|yuf@EZ5@G}DWq}6~3B@ziD5FsKF4={LCrkTbVo|)pZ5>5>TP9<3h3r&z_h}Far z$#hx zdU_ZXsSFPmN`ixVBEC}w6b%KoL~^DAqXA1K5Q%w$K%Ov2D+)wKVl+_56NpiPz@hA5 zgT0`~Xzc&^Y)3;rW6oLbO(k7tPQU5 zd@}Tq_g**o9M|$ox^t@Ex6WMYDy%ruR8@QR?Zt*;K2a0*%(>$(F1onFWqZm}>|E@# zoOdS#tB6knRtJwR^oY3_+P=>^e17ZN zuWyBU*}wll=;2kjCU;_eXB)m@)Rv=vwPDkA+QtmuE<5U0r8`YS-62_3-_}y7VSz^))3@ zaJW6&Vy?S_C*-!*J@Tie#!<59g1fDi%((fpi*c7<%61UDn=7tm=XJba&0mo2W?yo4 z@~xsDrq0zGuHQxrZ-eV%h$SL{;&6tJW*-+$;Z|_hxX_Ubxi(_my^FQx(eja}I@dot z=@Rp?V%!mVKoG(0bdM;I4pPowTnjeeFVSqAVwv8S=MKvY{5|WR>33e1Wrc3PCvKRh z`tWLdcE`=Cz``r|PrAk*Q;fyyCi#!S#{O(S74XGZ6AP0$*ib{|7LkdKZ-i%^ zm@W0PZacow?P<#zjcs4fCUt7#o;#eIVJBv_Qrj9fa*ZCJ1&#c(`iq8*ZcV&JN8zAE z&tLiXw`U)i*Ro^8r6F(4G(pXox;rI`${Q09K`yuB#_-xUTVaE0Rri7=d9%~vws#Jn zz~q!HTNq!n>7(QKnn&^T-?<+0t@qf{qg*@Rvw2n78I&3mBk;*9n0MuLQMIpq@bWXe UnQlpJ6MKnADx(x-VGDEq0p&{eKL7v# literal 0 HcmV?d00001 diff --git a/data/themes/classic/insert_bar.png b/data/themes/classic/insert_bar.png new file mode 100644 index 0000000000000000000000000000000000000000..fbc9f4775aea549b355290b620e9cccee64183c8 GIT binary patch literal 540 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 IA*%}i1w^Rn;Q#;t literal 0 HcmV?d00001 diff --git a/data/themes/classic/proportional_snap.png b/data/themes/classic/proportional_snap.png index 485ced02b994c6a0cea44c6361e7de10da2aaf99..be687c9dfdff5b0475b86a6107fe0f68678362a3 100644 GIT binary patch literal 2128 zcmai0c~BEq7>`x4f)o)%1&_F(P;0VbF+g?_A|zZwID-TfF(jL0A!K8+NI2>sI3NOb z7)J#~pcTC3Xb};#45-DUf)`%36s^^cRt}|j4W%y$f_SulY?AkWzwh_H<9**IHzdf{ z+|<^TLZO)Z`|-oTx3&KH+ys0UF`g`^~eWwwSpiCt%6XG1~Bf3QTPKx zkiL(b=)~%ekp+VPrY9zj3qwnKuLp@3N$NPC-X5S8rl>JE4AT-x8VTmT9#fI_hD_-5 zg8Ca`+6Z|lD-lVDjXb*jG2hWFb?E~z|Lb()JGwGs?iW! zhbcvTJP}iAf$FFiV_?)LKpvqaG~wV{V;snM)@U72112D(0CF+7F$Vebcp(}>hARQ4 z4fFM)`g`+SomsA~G=y%H0YyWBEiOypi%Gyj7zmTbaG^P~gb0EnER^X=V=z&MFSc7@WL4i4tr%Z-}`lL`p^f#A^P!Z6SZsSdo6__F2p9&5haShhD z+*{w+@CV+r4TN`k6xbQ}K<|Sg0*_7+uo#^~m<2#QyHZEPMKS%%FF_b{dt*FYV^w$N39?R^j=o^UkWTyBwgr7e% zqgCFj640H}<&XQ3=Ash%^W*2E9jqw{O*Jktr~7SJBGEyjH9-Wu>5#BT@?!xFT9C$o@Jiye_7y{i3uKikX%EnLyc9G8oQAF* z>+icRyi!=)w6t%{3#(jTUsHE-`m^XuKE$cU)QmSec-^fwReVch(cPxZJ*iFC;E4Db zrKCxCFv&{gcF%qLROS3M`>P#s3*J2G?5zGHy)Mh~rMb(Txb*xi!d=O~Ed+hqo8x%-mVsbyC#s%VD?LnJnr3rpCR7d8E96mUYLbcINfI z=v~j^PW@VJSNKp}U9$CI-{Gzve(s@;a^b-}0Zyl{UYfMiXNx8KhDE$Pn_${;=H5?h zoK-Kdxp#YuXA|i%IQ7NV{CyE~P+h)R@z6wNcU$Zi+pha`&X8^E-_ufk-eVcReR<0& z2_(_{U@<)8zMYrP32yAB-OpVT?B!Z?#Ob-?z^se>1t^<1c_DnEFvZ6ApMhrgyMbF? lb9Kc*n)4!dwr<9mzA0yC9V8eo0-gSkfWLPTzshTE=D)$~5&ZxF delta 225 zcmV<703QF)5b^<#8Gi%-001X|)rJ570J%v-K~#9!wU8kXK~Wfn&lQa+3dJw5VS*%> zB#TCq1V2E$iAb^ux=j#GP)w2(Q*6Rb{(*UHcbuIseB*}uq;uZ$obCNSqd}Sqc^O%3 zlECM|fg4*#{tAvbWko9dFyKT|5y=tQN5hi7Cbn1`3mxW4M_Q`v9dXD+o7dw){d3N6 zrED}Pac4w>E@yljgCxufjM=atU?$h2-FiGJ{v&!E*=eGH8ZVkkM+t4RCLZ{SsIL=; b7^&kMy&Ccl%Qj#;00000NkvXXu0mjf#m!&7 diff --git a/data/themes/classic/receive_bg_arrow.png b/data/themes/classic/receive_bg_arrow.png index 8aaeae606a627201fa2ddc141e60a5759e14d8c5..8e01e9df8fa34c3e533946e694116af5a22a0a9c 100644 GIT binary patch literal 1827 zcmah~eNYr-7+)(Z!IKe8&9B*69LIF-c3C{&7ET6s!8>A=gX2R7!R79~yOq1!b$8)D zbQ(YM1EnTBGc6svKP*Z{X$)=fvyz+&1vS&rhWyAGQ%jsQY_#azI|LD}KlX0l=l6Sl z@AEz%@75K}oui4F5Q89y#%y8=;s0dydHF^7TM}}{A;=4#@zye>jLoNPf=9>Mg({%) zdqjvMNJge#!oTy4fH5eeduXrH*kI|kIfa^LY`B5)PKlXL|BI34);XV6t-2wijuiXgiLvHt*rw&;h4 zIKuQj-Nny|Pb0JJf73mlLC27loLZQOzN8L{>CpzVH6Q}K5XeG}WCJ<1z^f!iGNI;$ zGJ7~D$Om4h;xb~S;R&FWw?AoDh1@l2C5pW~+?pJ4DI36ySSpDp4M6js3aP243V1(o zmodBtcx9;CAH@JT{}~`HxCN;Qwly$f(Y^ZXKt(WCutO+062<^zrs)DnaPV%3$%S*W zQF9KRl4M9p(UQ8T3@jQ2eR)TK;S|Us2~w{mlC>mZB?wASP_xpsgq|XZh;oDtXTi=X z-2d^_jz%+}2g_0>URDGt5HUsDya+6buA)mfAL(xtjZ~T9Y--kxm?#PM8XK@jHivRi zR2CeHkCQ-_6BgKrWjP!?tWN+fP&*eTQ4$Vi>R=b23#jRS=Hc+Z&~SC+9>tb{4~ae_ zLU?ROpgT|7yb7T@WRVSg15}(kjNs)uoXP4%aD2?$n=JqhK`wxWz6x+ zKj>rY$ku>O}hom5bjv*fr6-@4MRd@e_}XeK_3xZc8DP z_oKT`6Fiw*l(Y;whe_67Gk{;t-3yJ`3Goo2t)(z3Dp&F&xa%=*CV=RdJuHD6BP_DmZ$ z=5W$IX7!Hzlh*rZ?Ai&VR<Vd%faS@ljhCd-(usSDzq zml|8vZh@bR5u$0nj3BWI>N5;^f7^Iy++b$1tfso(PM;XDoESO$5~IZ4{4%U)&PlJu zT^U<^r7iR!JLMnitpl5~H^ybJE3B%QchgH>GZ{Hhg_s=K)LFukXy7zU}ia kmnCgSc4hk1wBD@JEj7vK$7pV(s^2zq&Rk}9_M(;l0v4KtZU6uP delta 174 zcmZ3?x0i8(c)bM&8v_Fadmpd;Uf*wh%Yoy^3M;!aN3QvQUVq_@-?!$5@9AAk6Am48+ghN57(8A5T-G@yGywn)vreS| diff --git a/data/themes/classic/remove_bar.png b/data/themes/classic/remove_bar.png new file mode 100644 index 0000000000000000000000000000000000000000..143766a37558bacfa3eeb907c85bc65fde780f0a GIT binary patch literal 386 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE-VORs=nf?vsGNUR06E57d|xn zY(Ia}1(UNEqh3#(P<)Q(@Q0r#k{K4{M&B>J8_sp(`(MSUh71D9E#FTzbQr4HvN5Qn zGO9jhbmoY-Zkoxb_=NF)-P7snHQWa2n*WpArnGF*=?@K6?YwLVx~<$eCkSsBcB9_U^td~Z*2 zx<`=wlSAAsEGiuPr-rYbaAdmv)0d|-|1v0bEa*9Ut~*|HgY@5=ZXoBna_JFNsWkG=RX2okWT>X3Wg#XcuD$@^nEk*J=w^+fwaF zdRnB`PXGZAEmE-G2z zy|AQ7Rk5`qi68)nXT?RvF|Y0wW|t2dfl3IfR$=HuI_+ofL6?vvbk4=sxO@WXxGiwJ>(jAadr_1A6 z2jd2qke|H;HfHLQ)v3wJN?a9|0V5+oEaM31DG{)64A&^JBqfenF^tft3CYwD7$z&r zY;Y3nlt}#_U)s;G7DSlMgn63K8l7%-jyoG0yD7>99iBvTfL_*^uQyZ7SI+0szyz}>D* zi!P~ND-#x+P!JdPfs+re5OnSi0mj@dM0>&2!-G2}bfw%6K@l%7U@lAM0YU3qYgMbizrC-qvzJy0bncJGaX}Q(e{PK|Lp`Am+>l@Duzge;UxxVvjjmN*J+a0@hVD+v0 z-TQm;^*P_Vo1=qglL{0Eh0W9Te@vejdZ){}vxJ=T6Vtl>ystiOZ+KRfJn60ftSN4@ zs;nln=pDr+%WvP`RIW}czd2lbZRDS7<>&j3HFOyLR!h&TQ!h?^onzDlo}PHue#v+t zj_OKWv$9`(O~3Pq>8$noxLuj>@Q$8mCJx^iYOV;`d-}Tj)@@BY5wcF&`UhJ#3}-!F zx5w8Wm#BwQ+FBkN9Dx-0%|F$KBcaZ=pJv+9brycr!NP50#fa`eY`NvNj$lqz>D7HL z>yA2RX8tbR8WX+kz0LQHPPyW{$k7j1RM01{Z^Bx-blv2~f%XFxKkT}YGWMnB%m)v8 ze&>Rg@z`_R@rc9I+spRM7?NwMiD>lHXhP#F8(rm{S3|ucV~^>-yL?ndIoAY#sXuoy zr8LI*b7#-4*TKhP1!pJ`ASfzMdLp1V4#fkf$*9k;#_zXPz71hGhGC~qKjHag!;P8g zfti+&Y2xx*+TLGJF0<{eQkclNUg-S<%ad_7s8`$cJojjWd7ZOz??oi^R8}stDlGv0 Xn4QR%A8J%cpC)5wuKw7T${qg#DDYu- literal 157 zcmeAS@N?(olHy`uVBq!ia0vp^vOsLX!VDxo-+8waNbv{wgt!7}895~&k_Cfy9-GrZ zA=Z*0zhH)#*;Tb^in0W^cb)78&qol`;+0Ezo6e*gdg diff --git a/data/themes/classic/style.css b/data/themes/classic/style.css index 13047dc5d..8d8854028 100644 --- a/data/themes/classic/style.css +++ b/data/themes/classic/style.css @@ -30,9 +30,8 @@ lmms--gui--AutomationEditor { qproperty-barLineColor: #808080; qproperty-graphColor: rgba(153, 175, 255, 200); - qproperty-scaleColor: qlineargradient(spread:reflect, - x1:0, y1:0.5, x2:1, y2:0.5, - stop:0 #333, stop:1 #202020); + qproperty-scaleColor: qlineargradient(spread:reflect, x1:0, y1:0.5, x2:1, y2:0.5, + stop:0 #333, stop:1 #202020); qproperty-ghostNoteColor: rgba(248, 248, 255, 125); qproperty-detuningNoteColor: rgba(248, 11, 11, 125); @@ -69,7 +68,8 @@ QTextEdit, QLineEdit:focus, QComboBox:focus, QSpinBox:focus, QDoubleSpinBox:focu QToolTip { border-radius: 4px; - background: qlineargradient(spread:reflect, x1:0.5, y1:0.5, x2:0.5, y2:0, stop:0 rgba(0, 0, 0, 255), stop:1 rgba(50, 50, 50, 220)); + background: qlineargradient(spread:reflect, x1:0.5, y1:0.5, x2:0.5, y2:0, + stop:0 rgba(0, 0, 0, 255), stop:1 rgba(50, 50, 50, 220)); opacity: 175; border: 1.0px solid rgba(0,0,0,255); color: #4afd85; @@ -77,7 +77,8 @@ QToolTip { lmms--gui--TextFloat, lmms--gui--SimpleTextFloat { border-radius: 4px; - background: qlineargradient(spread:reflect, x1:0.5, y1:0.5, x2:0.5, y2:0, stop:0 rgba(0, 0, 0, 255), stop:1 rgba(50, 50, 50, 220)); + background: qlineargradient(spread:reflect, x1:0.5, y1:0.5, x2:0.5, y2:0, + stop:0 rgba(0, 0, 0, 255), stop:1 rgba(50, 50, 50, 220)); opacity: 175; border: 1.0px solid rgba(0,0,0,255); color: #4afd85; @@ -248,45 +249,39 @@ QScrollBar::add-page:vertical:pressed, QScrollBar::sub-page:vertical:pressed { /* scrollbar: handles (sliders) */ QScrollBar::handle:horizontal { - background: qlineargradient(spread:reflect, - x1:0.5, y1:0, x2:0.5, y2:1, - stop:0 #969696, stop:0.5 #c9c9c9, stop:1 #aaa); + background: qlineargradient(spread:reflect, x1:0.5, y1:0, x2:0.5, y2:1, + stop:0 #969696, stop:0.5 #c9c9c9, stop:1 #aaa); border: 1px outset #888; border-radius: 2px; min-width: 24px; } QScrollBar::handle:horizontal:hover { - background: qlineargradient(spread:reflect, - x1:0.5, y1:0, x2:0.5, y2:1, - stop:0 #969696, stop:0.5 #f0f0f0, stop:1 #aaa); + background: qlineargradient(spread:reflect, x1:0.5, y1:0, x2:0.5, y2:1, + stop:0 #969696, stop:0.5 #f0f0f0, stop:1 #aaa); } QScrollBar::handle:horizontal:pressed { - background: qlineargradient(spread:reflect, - x1:0.5, y1:0, x2:0.5, y2:1, - stop:0 #747474, stop:1 #c9c9c9); + background: qlineargradient(spread:reflect, x1:0.5, y1:0, x2:0.5, y2:1, + stop:0 #747474, stop:1 #c9c9c9); } QScrollBar::handle:vertical { - background: qlineargradient(spread:reflect, - x1:0, y1:0.5, x2:1, y2:0.5, - stop:0 #969696, stop:0.5 #c9c9c9, stop:1 #aaa); + background: qlineargradient(spread:reflect, x1:0, y1:0.5, x2:1, y2:0.5, + stop:0 #969696, stop:0.5 #c9c9c9, stop:1 #aaa); border: 1px outset #888; border-radius: 2px; min-height: 24px; } QScrollBar::handle:vertical:hover { - background: qlineargradient(spread:reflect, - x1:0, y1:0.5, x2:1, y2:0.5, - stop:0 #969696, stop:0.5 #f0f0f0, stop:1 #aaa); + background: qlineargradient(spread:reflect, x1:0, y1:0.5, x2:1, y2:0.5, + stop:0 #969696, stop:0.5 #f0f0f0, stop:1 #aaa); } QScrollBar::handle:vertical:pressed { - background: qlineargradient(spread:reflect, - x1:0, y1:0.5, x2:1, y2:0.5, - stop:0 #747474, stop:1 #c9c9c9); + background: qlineargradient(spread:reflect, x1:0, y1:0.5, x2:1, y2:0.5, + stop:0 #747474, stop:1 #c9c9c9); } QScrollBar::handle:horizontal:disabled, QScrollBar::handle:vertical:disabled { @@ -298,7 +293,8 @@ QScrollBar::handle:horizontal:disabled, QScrollBar::handle:vertical:disabled { /* arrow buttons */ QScrollBar::add-line, QScrollBar::sub-line { - background: qradialgradient(cx:0.3, cy:0.3, radius:0.8, fx:0.3, fy:0.3, stop:0 #c9c9c9, stop:1 #969696 ); + background: qradialgradient(cx:0.3, cy:0.3, radius:0.8, fx:0.3, fy:0.3, + stop:0 #c9c9c9, stop:1 #969696 ); border-radius: 1px; border: 1px solid #131313; subcontrol-origin: margin; @@ -310,11 +306,13 @@ QScrollBar::add-line:vertical { subcontrol-position: bottom; height: 12px;} QScrollBar::sub-line:vertical { subcontrol-position: top; height: 12px;} QScrollBar::add-line:hover, QScrollBar::sub-line:hover { - background: qradialgradient(cx:0.3, cy:0.3, radius:0.8, fx:0.3, fy:0.3, stop:0 #e0e0e0, stop:0.5 #c9c9c9, stop:1 #969696 ); + background: qradialgradient(cx:0.3, cy:0.3, radius:0.8, fx:0.3, fy:0.3, + stop:0 #e0e0e0, stop:0.5 #c9c9c9, stop:1 #969696 ); } QScrollBar::add-line:pressed, QScrollBar::sub-line:pressed { - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #969696, stop:0.5 #c9c9c9, stop:1 #969696 ); + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #969696, + stop:0.5 #c9c9c9, stop:1 #969696 ); } QScrollBar::add-line:disabled, QScrollBar::sub-line:disabled { @@ -356,8 +354,10 @@ lmms--gui--TrackView > QWidget { /* track background config */ lmms--gui--TrackContentWidget { /* colors */ - qproperty-darkerColor: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(50, 50, 50), stop:0.33 rgb(20, 20, 20), stop:1 rgb(15, 15, 15)); - qproperty-lighterColor: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(50, 50, 50), stop:0.33 rgb(40, 40, 40), stop:1 rgb(30, 30, 30)); + qproperty-darkerColor: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 rgb(50, 50, 50), stop:0.33 rgb(20, 20, 20), stop:1 rgb(15, 15, 15)); + qproperty-lighterColor: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 rgb(50, 50, 50), stop:0.33 rgb(40, 40, 40), stop:1 rgb(30, 30, 30)); qproperty-coarseGridColor: rgba(0, 0, 0, 160); qproperty-fineGridColor: rgba(0, 0, 0, 80); qproperty-horizontalColor: rgba(0, 0, 0, 160); @@ -544,7 +544,8 @@ QToolButton:pressed { } QToolButton:checked { - background: qradialgradient(cx:0.3, cy:0.3, radius:0.8, fx:0.3, fy:0.3, stop:0 #e0e0e0, stop:0.8 #c9c9c9, stop:1 #c0c0c0 ); + background: qradialgradient(cx:0.3, cy:0.3, radius:0.8, fx:0.3, fy:0.3, + stop:0 #e0e0e0, stop:0.8 #c9c9c9, stop:1 #c0c0c0 ); padding: 2px 1px 0px 1px; color: black; } @@ -578,7 +579,8 @@ lmms--gui--TrackLabelButton { } lmms--gui--TrackLabelButton:hover { - background-color: qlineargradient(spread:reflect, x1:0, y1:0, x2:0, y2:0.5, stop:0 #5b6571, stop:0.75 #7b838d, stop:1 #7b838d ); + background-color: qlineargradient(spread:reflect, x1:0, y1:0, x2:0, y2:0.5, + stop:0 #5b6571, stop:0.75 #7b838d, stop:1 #7b838d ); color: white; border: 1px solid rgba(0,0,0,64); padding: 1px 0px; @@ -586,7 +588,8 @@ lmms--gui--TrackLabelButton:hover { } lmms--gui--TrackLabelButton:pressed { - background: qlineargradient(spread:reflect, x1:0.5, y1:0, x2:0.5, y2:1, stop:0 #49515b, stop:0.3 #5b6571, stop:1 #6b7581 ); + background: qlineargradient(spread:reflect, x1:0.5, y1:0, x2:0.5, y2:1, + stop:0 #49515b, stop:0.3 #5b6571, stop:1 #6b7581 ); color: white; border: 1px solid rgba(0,0,0,64); padding: 2px 0px 0px; @@ -594,7 +597,8 @@ lmms--gui--TrackLabelButton:pressed { } lmms--gui--TrackLabelButton:checked { - background: qlineargradient(spread:reflect, x1:0.5, y1:0, x2:0.5, y2:1, stop:0 #49515b, stop:0.3 #5b6571, stop:1 #6b7581 ); + background: qlineargradient(spread:reflect, x1:0.5, y1:0, x2:0.5, y2:1, + stop:0 #49515b, stop:0.3 #5b6571, stop:1 #6b7581 ); color: white; border: 1px solid rgba(0,0,0,128); padding: 2px 0px 0px; @@ -602,17 +606,19 @@ lmms--gui--TrackLabelButton:checked { } lmms--gui--TrackLabelButton:checked:hover { - background-color: qlineargradient(spread:reflect, x1:0, y1:0, x2:0, y2:0.5, stop:0 #5b6571, stop:0.75 #7b838d, stop:1 #7b838d ); + background-color: qlineargradient(spread:reflect, x1:0, y1:0, x2:0, y2:0.5, + stop:0 #5b6571, stop:0.75 #7b838d, stop:1 #7b838d ); } lmms--gui--TrackLabelButton:checked:pressed { - background: qlineargradient(spread:reflect, x1:0.5, y1:0, x2:0.5, y2:1, stop:0 #49515b, stop:0.3 #5b6571, stop:1 #6b7581 ); + background: qlineargradient(spread:reflect, x1:0.5, y1:0, x2:0.5, y2:1, + stop:0 #49515b, stop:0.3 #5b6571, stop:1 #6b7581 ); } /* sidebar, sidebar buttons */ lmms--gui--SideBar { - background: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop: 0 #98a2a7, stop: 1.0 #5b646f); + background: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #98a2a7, stop:1 #5b646f); } lmms--gui--SideBar QToolButton { @@ -649,8 +655,7 @@ lmms--gui--ControllerRackView QPushButton { lmms--gui--MixerChannelView { background: #5b6571; color: #e0e0e0; - qproperty-backgroundActive: qlineargradient(spread:reflect, x1:0, y1:0, x2:1, y2:0, - stop:0 #7b838d, stop:1 #6b7581 ); + qproperty-backgroundActive: #7d8691; qproperty-strokeOuterActive: rgb( 0, 0, 0 ); qproperty-strokeOuterInactive: rgba( 0, 0, 0, 50 ); qproperty-strokeInnerActive: rgba( 255, 255, 255, 100 ); @@ -684,16 +689,14 @@ lmms--gui--TimeLineWidget { min-height: 1.5em; max-height: 1.5em; - background-color: qlineargradient( x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #8796a7, stop: 1.0 #3e454e ); + background-color: qlineargradient( x1:0, y1:0, x2:0, y2:1, stop:0 #8796a7, stop:1 #3e454e ); qproperty-inactiveLoopColor: rgba( 52, 63, 53, 64 ); qproperty-inactiveLoopBrush: rgba( 255, 255, 255, 32 ); qproperty-inactiveLoopInnerColor: rgba( 255, 255, 255, 32 ); qproperty-inactiveLoopHandleColor: rgba( 192, 192, 192, 100 ); qproperty-activeLoopColor: rgba( 52, 63, 53, 255 ); - qproperty-activeLoopBrush: qlineargradient( x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #378d59, stop: 1.0 #297e36 ); + qproperty-activeLoopBrush: qlineargradient( x1:0, y1:0, x2:0, y2:1, stop:0 #378d59, stop:1 #297e36 ); qproperty-activeLoopInnerColor: rgba( 74, 155, 100, 255 ); qproperty-activeLoopHandleColor: rgba( 192, 192, 192, 200 ); @@ -766,10 +769,8 @@ lmms--gui--PatternClipView { /* Subwindows in MDI-Area */ lmms--gui--SubWindow { - color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #4b525c, stop: 1.0 #31363d); - qproperty-activeColor: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #33383e, stop: 1.0 #1a1c20); + color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #4b525c, stop:1 #31363d); + qproperty-activeColor: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #33383e, stop:1 #1a1c20); qproperty-textShadowColor: rgb( 0, 0, 0 ); qproperty-borderColor: rgb( 0, 0, 0 ); } diff --git a/data/themes/classic/tool.png b/data/themes/classic/tool.png new file mode 100644 index 0000000000000000000000000000000000000000..183470438869fcdab5a36bd4225dae01178d6e45 GIT binary patch literal 2495 zcmai0c~}!?9v+Fh2)I>5N&#gEV55*^l7K=sfd)toM=&5+4i5-0AyddfG9d|KD?TbJ zUerY@_1LnDxVCc0rHCRT@W`=15vf#d5D*{ad=OYb%T5xDTJ7#1ljQro@9+J-?{`fO z@q#ulH?cMW0ARTv$2SD~w#1&*#!InJZ*XlE04y#>Lc`H;ZXi8YDkcfyq%kl_EtX+8 z05H7NGC^ztjDj(6JR)J@N3U1oK_rfe4|C%}T$v9nLO3aMcxy^fXlzPCtVbN)%hQCR zregwP7!`nOaiT;)S2OW?T{`w#XC~u8Jq1l*;={Q-&_^nVK{t{c3Br4tfDCz@kRIau z`D%ijN)!${34?|&tcaB( zG8B+(@L73^G{K=$J1e)c6Wej75d0 zZbXPiq_~Dc5S`*mhc*!*H#!9AmGx|_7vcn{;Q#S;6%8^l4=$I^K@_M|uF;zeWxf?o zGOP^CL_}ZTAgIqWT@b5_IukFGOXHNWaGZW~OwNERq(W3Bki)EaEWu1XODIIJ{AfU) zu5v*tNX43R)4MJz5v&XMA6}09`e0Yaxa>w^uT3WJl`4&f4looT^ z2p8GrH5a+^r}lkG51lwgs}pf1xkn4V=Z(Zm5?DM7)(>U;(qNB)K#AVRUDh~V zaOQOp7zGz2gVg?NAu&Iviuf?k(ZTt4{%sPML`Y2-Y&ZA%_6%V>`)(vbiwn439=0;|cTG;<4Vk8rN0-8H1X?X&V;+u6b_BkS)SIbYX0eX;XWgJV7W_+3sgG&f&Sn38&D$0fi-fzbYjqh-UdPLakVwsE+p#T8WRk~*pkaNZg z0C1oB`LaUYkLKNza%|#l|1>t*Pr47;cPutP*xZxxYIg_Ap4}J??%!KVo~mzroLG3j z3l4pnku@uXHZAp!vgp3KzLT=-^V;UKbnB9#6Im8@>T`DjoU(D_47bfB)#DM(Y%Vvm zpsap()EY+hK<(%ZQ`MmT+e&t^=`C)TlKgY|qXHySlaew!;fpU`0z5?8wv(n;?x|+9 znj$#|CMT6gjvt>VHpwps*Sco>yQPKlWwlr9m7xeepW@|p9HsBB4*A!1oYr+PXXGd4 zBlkTeWs%DR0s=VePF5&)RDWsF(l580%h|s^?>x`Hy}SFcn(^zXR;wigL-mz}!%?%T zH|s74&bL-yBZ&HKkVFqy=8{m zsi%zAxper!==!`9ux8>8)H*Q=nN3`OUYVV&QmxFWy9JrKAHy{;t2g{y9=st7qSG7C z8$Z=}Hba4p%17$5NH%2Un-@|uHMOz3ySuHueGw48ZJQ7EbSmig<7CC{yFe(E|pFtcURKZ#9)7`S+ zq&fN#EHm0%hl4g`%}$A*eHG1GA+cS%_SdTw73myDqa9t}5lqZ#_c;1ZUwJU(W_!9f zNdSN+?-uR%?m0d+HI-5D9B+Q;Gd`bi*r>y`xT@-<&$^S>l;&e_LoT8GR8xOdE@8CT z<=f%mVP=?%CCEQe2T;2yyN5Zqk=7{6iU)~smP(Gr>7^qq@)B*Ol*EW zI$Bs(TKeLZT~Q_t@U*eEo-HXXlv>{61Kjzbs21AQHD}6WPTx4B``_iq4)QJVj^6uU DFE-E5 literal 0 HcmV?d00001 From 6af3ab587571aeb037f3ff6d71cc68fefc74160c Mon Sep 17 00:00:00 2001 From: Fawn Date: Fri, 21 Mar 2025 04:07:15 -0600 Subject: [PATCH 13/62] Fix missing update to mixer channel name when created (#7795) Fixes a regression from 07baf9e27a4990b2e1cc88b9cbe1347c70366d9d, in which a channel that was created through a tracks "Assign track to new mixer channel" context menu would display an incorrect name until it was renamed by a user. --- src/gui/MixerView.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/MixerView.cpp b/src/gui/MixerView.cpp index d05ff097d..5cb99295b 100644 --- a/src/gui/MixerView.cpp +++ b/src/gui/MixerView.cpp @@ -376,6 +376,7 @@ void MixerView::updateMixerChannel(int index) } thisLine->m_sendButton->updateLightStatus(); + thisLine->m_renameLineEdit->setText(thisLine->elideName(thisLine->mixerChannel()->m_name)); thisLine->update(); } From 91233e6a73e67d5532ce04e5df548997cd69f3cc Mon Sep 17 00:00:00 2001 From: Fawn Date: Fri, 21 Mar 2025 21:32:49 -0600 Subject: [PATCH 14/62] Enable CMAKE_EXPORT_COMPILE_COMMANDS (#7804) This setting instructs CMake to generate compile_commands.json for use with clangd. --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c01bebf0..a93d7383a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,5 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.13) +SET(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Set the given policy to NEW. If it does not exist, it will not be set. If it # is already set to NEW (most likely due to predating the minimum required CMake From db9ccbeb562b1482f54c4a5afe101f77a7e836a3 Mon Sep 17 00:00:00 2001 From: regulus79 <117475203+regulus79@users.noreply.github.com> Date: Sat, 22 Mar 2025 05:54:40 -0400 Subject: [PATCH 15/62] Add Strum Tool to Piano Roll (#7725) Adds a complex strum tool to the Piano Roll, allowing the user to take a selection of chords, and drag around the notes to shape the strum exactly how they want it. --------- Co-authored-by: szeli1 <143485814+szeli1@users.noreply.github.com> Co-authored-by: Sotonye Atemie --- include/PianoRoll.h | 26 ++++- src/gui/editors/PianoRoll.cpp | 194 ++++++++++++++++++++++++++++++++-- 2 files changed, 212 insertions(+), 8 deletions(-) diff --git a/include/PianoRoll.h b/include/PianoRoll.h index a1d045ff4..8ac59fe0a 100644 --- a/include/PianoRoll.h +++ b/include/PianoRoll.h @@ -112,7 +112,8 @@ public: Erase, Select, Detuning, - Knife + Knife, + Strum }; /*! \brief Resets settings to default when e.g. creating a new project */ @@ -268,7 +269,8 @@ private: SelectNotes, ChangeNoteProperty, ResizeNoteEditArea, - Knife + Knife, + Strum }; enum class NoteEditMode @@ -324,6 +326,9 @@ private: void setKnifeAction(); void cancelKnifeAction(); + void setStrumAction(); + void cancelStrumAction(); + void updateScrollbars(); void updatePositionLineHeight(); @@ -347,6 +352,7 @@ private: QPixmap m_toolMove = embed::getIconPixmap("edit_move"); QPixmap m_toolOpen = embed::getIconPixmap("automation"); QPixmap m_toolKnife = embed::getIconPixmap("edit_knife"); + QPixmap m_toolStrum = embed::getIconPixmap("arp_free"); static std::array prKeyOrder; @@ -437,6 +443,7 @@ private: EditMode m_editMode; EditMode m_ctrlMode; // mode they were in before they hit ctrl EditMode m_knifeMode; // mode they where in before entering knife mode + EditMode m_strumMode; //< mode they where in before entering strum mode bool m_mouseDownRight; //true if right click is being held down @@ -465,6 +472,21 @@ private: void updateKnifePos(QMouseEvent* me, bool initial); + //! Stores the chords for the strum tool + std::vector m_selectedChords; + //! Computes which notes belong to which chords from the selection + void setupSelectedChords(); + + TimePos m_strumStartTime; + TimePos m_strumCurrentTime; + int m_strumStartVertical = 0; + int m_strumCurrentVertical = 0; + float m_strumHeightRatio = 0.0f; + bool m_strumEnabled = false; + //! Handles updating all of the note positions when performing a strum + void updateStrumPos(QMouseEvent* me, bool initial, bool warp); + + friend class PianoRollWindow; StepRecorderWidget m_stepRecorderWidget; diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index f0f54f0ba..d075a70b8 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -58,6 +58,7 @@ #include "FontHelper.h" #include "InstrumentTrack.h" #include "KeyboardShortcuts.h" +#include "lmms_math.h" #include "MainWindow.h" #include "MidiClip.h" #include "PatternStore.h" @@ -1396,7 +1397,7 @@ void PianoRoll::keyPressEvent(QKeyEvent* ke) } case Qt::Key_A: - if( ke->modifiers() & Qt::ControlModifier ) + if (ke->modifiers() & Qt::ControlModifier && m_editMode != EditMode::Strum && m_editMode != EditMode::Knife) { ke->accept(); if (ke->modifiers() & Qt::ShiftModifier) @@ -1414,11 +1415,15 @@ void PianoRoll::keyPressEvent(QKeyEvent* ke) break; case Qt::Key_Escape: - // On the Knife mode, ESC cancels it + // On the Knife mode or Strum mode, ESC cancels it if (m_editMode == EditMode::Knife) { cancelKnifeAction(); } + else if (m_editMode == EditMode::Strum) + { + cancelStrumAction(); + } else { // Same as Ctrl + Shift + A @@ -1519,6 +1524,7 @@ void PianoRoll::keyReleaseEvent(QKeyEvent* ke ) } computeSelectedNotes( ke->modifiers() & Qt::ShiftModifier); m_editMode = m_ctrlMode; + if (m_editMode == EditMode::Strum) { setupSelectedChords(); } update(); break; @@ -1614,6 +1620,19 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) return; } + if (m_editMode == EditMode::Strum && me->button() == Qt::LeftButton) + { + // Only strum if the user is dragging a selected note + const auto& selectedNotes = getSelectedNotes(); + if (std::find(selectedNotes.begin(), selectedNotes.end(), noteUnderMouse()) != selectedNotes.end()) + { + updateStrumPos(me, true, me->modifiers() & Qt::ShiftModifier); + m_strumEnabled = true; + update(); + } + return; + } + if( m_editMode == EditMode::Detuning && noteUnderMouse() ) { static QPointer detuningClip = nullptr; @@ -2141,7 +2160,27 @@ void PianoRoll::cancelKnifeAction() update(); } +void PianoRoll::setStrumAction() +{ + if (m_editMode != EditMode::Strum) + { + m_strumMode = m_editMode; + m_editMode = EditMode::Strum; + m_action = Action::Strum; + m_strumEnabled = false; + setupSelectedChords(); + setCursor(Qt::ArrowCursor); + update(); + } +} +void PianoRoll::cancelStrumAction() +{ + m_editMode = m_strumMode; + m_action = Action::None; + m_strumEnabled = false; + update(); +} void PianoRoll::testPlayKey( int key, int velocity, int pan ) { @@ -2241,11 +2280,15 @@ void PianoRoll::mouseReleaseEvent( QMouseEvent * me ) s_textFloat->hide(); - // Quit knife mode if we pressed and released the right mouse button + // Quit knife mode or strum mode if we pressed and released the right mouse button if (m_editMode == EditMode::Knife && me->button() == Qt::RightButton) { cancelKnifeAction(); } + else if (m_editMode == EditMode::Strum && me->button() == Qt::RightButton) + { + cancelStrumAction(); + } if( me->button() & Qt::LeftButton ) { @@ -2266,6 +2309,10 @@ void PianoRoll::mouseReleaseEvent( QMouseEvent * me ) m_midiClip->rearrangeAllNotes(); } + else if (m_action == Action::Strum || m_strumEnabled) + { + m_strumEnabled = false; + } else if (m_action == Action::Knife && hasValidMidiClip()) { bool deleteShortEnds = me->modifiers() & Qt::ShiftModifier; @@ -2313,7 +2360,7 @@ void PianoRoll::mouseReleaseEvent( QMouseEvent * me ) m_currentNote = nullptr; - if (m_action != Action::Knife) + if (m_action != Action::Knife && m_action != Action::Strum) { m_action = Action::None; } @@ -2379,6 +2426,12 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) updateKnifePos(me, false); } + // Update Strum position if we are on knife mode + if (m_editMode == EditMode::Strum && m_strumEnabled) + { + updateStrumPos(me, false, me->modifiers() & Qt::ShiftModifier); + } + if( me->y() > PR_TOP_MARGIN || m_action != Action::None ) { bool edit_note = ( me->y() > noteEditTop() ) @@ -2661,7 +2714,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) } } } - else if (me->buttons() == Qt::NoButton && m_editMode != EditMode::Draw && m_editMode != EditMode::Knife) + else if (me->buttons() == Qt::NoButton && m_editMode != EditMode::Draw && m_editMode != EditMode::Knife && m_editMode != EditMode::Strum) { // Is needed to restore cursor when it previously was set to // Qt::SizeVerCursor (between keyAreaBottom and noteEditTop) @@ -2780,6 +2833,120 @@ void PianoRoll::updateKnifePos(QMouseEvent* me, bool initial) m_knifeEndKey = mouseKey; } +/* + * Setup chords + * + * A chord is an island of notes--as the loop goes over the notes, if the notes overlap, + * they are part of the same chord. Else, they are part of a new chord. +*/ +void PianoRoll::setupSelectedChords() +{ + if (!hasValidMidiClip()) { return; } + m_selectedChords.clear(); + m_midiClip->rearrangeAllNotes(); + + const NoteVector& selectedNotes = getSelectedNotes(); + if (selectedNotes.empty()) { return; } + + int maxTime = -1; + NoteVector currentChord; + for (Note* note: selectedNotes) + { + // If the note is not in the current chord range (and this isn't the first chord), start a new chord. + if (note->pos() >= maxTime && maxTime != -1) + { + // Sort the notes by key before adding the chord to the vector + std::sort(currentChord.begin(), currentChord.end(), [](Note* a, Note* b){ return a->key() < b->key(); }); + m_selectedChords.push_back(currentChord); + currentChord.clear(); + maxTime = note->endPos(); + } + maxTime = std::max(maxTime, static_cast(note->endPos())); + currentChord.push_back(note); + } + // Add final chord + std::sort(currentChord.begin(), currentChord.end(), [](Note* a, Note* b){ return a->key() < b->key(); }); + m_selectedChords.push_back(currentChord); +} + +/* + * Perform the Strum + * + * Notes above the clicked note (relative to each chord) will be strummed down, notes below will be strummed up. + * Holding shift raises the amount of movement to a power, causing the strum to be curved/warped. +*/ +void PianoRoll::updateStrumPos(QMouseEvent* me, bool initial, bool warp) +{ + if (!hasValidMidiClip()) { return; } + // Calculate the TimePos from the mouse + int mouseViewportPos = me->x() - m_whiteKeyWidth; + int mouseTickPos = mouseViewportPos * TimePos::ticksPerBar() / m_ppb + m_currentPosition; + // Should we add quantization? probably not? + if (initial) + { + m_strumStartTime = mouseTickPos; + m_strumStartVertical = me->y(); + } + m_strumCurrentTime = mouseTickPos; + m_strumCurrentVertical = me->y(); + int strumTicksHorizontal = m_strumCurrentTime - m_strumStartTime; + float strumPower = fastPow10f(0.01f * (m_strumCurrentVertical - m_strumStartVertical)); + + if (initial) + { + m_midiClip->addJournalCheckPoint(); + + Note* clickedNote = noteUnderMouse(); + if (clickedNote == nullptr) { return; } + + for (NoteVector chord: m_selectedChords) + { + for (Note* note: chord) + { + // Save the current note position + note->setOldPos(note->pos()); + // if this is the clicked note, calculate it's ratio up the chord + if (note == clickedNote && chord.size() > 1) + { + m_strumHeightRatio = 1.f * std::distance(chord.begin(), std::find(chord.begin(), chord.end(), clickedNote)) / (chord.size() - 1); + } + } + } + } + + for (NoteVector chord: m_selectedChords) + { + // Don't strum a chord with only one note + if (chord.size() <= 1) { continue; } + for (size_t i = 0; i < chord.size(); ++i) + { + float heightRatio = 1.f * i / (chord.size() - 1); + float ratio = 0.0f; + + if (heightRatio == m_strumHeightRatio) + { + ratio = 1.f; + } + else if (heightRatio < m_strumHeightRatio) + { + ratio = heightRatio / m_strumHeightRatio; + } + else + { + ratio = (1.f - heightRatio) / (1.f - m_strumHeightRatio); + } + + if (warp) + { + ratio = std::pow(ratio, strumPower); + } + chord.at(i)->setPos(std::max(0, static_cast(chord.at(i)->oldPos() + ratio * strumTicksHorizontal))); + } + } + m_midiClip->rearrangeAllNotes(); + m_midiClip->updateLength(); + m_midiClip->dataChanged(); +} @@ -3682,6 +3849,9 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) case EditMode::Knife: cursor = &m_toolKnife; break; + case EditMode::Strum: + cursor = &m_toolStrum; + break; } QPoint mousePosition = mapFromGlobal( QCursor::pos() ); if( cursor != nullptr && mousePosition.y() > keyAreaTop() && mousePosition.x() > noteEditLeft()) @@ -3896,7 +4066,14 @@ void PianoRoll::focusOutEvent( QFocusEvent * ) if (m_editMode == EditMode::Knife) { m_editMode = m_knifeMode; m_action = Action::None; - } else { + } + else if (m_editMode == EditMode::Strum) + { + m_editMode = m_strumMode; + m_action = Action::None; + } + else + { m_editMode = m_ctrlMode; } update(); @@ -4841,6 +5018,10 @@ PianoRollWindow::PianoRollWindow() : connect(knifeAction, &QAction::triggered, m_editor, &PianoRoll::setKnifeAction); knifeAction->setShortcut(combine(Qt::SHIFT, Qt::Key_K)); + auto strumAction = new QAction(embed::getIconPixmap("arp_free"), tr("Strum"), noteToolsButton); + connect(strumAction, &QAction::triggered, m_editor, &PianoRoll::setStrumAction); + strumAction->setShortcut(combine(Qt::SHIFT, Qt::Key_J)); + auto fillAction = new QAction(embed::getIconPixmap("fill"), tr("Fill"), noteToolsButton); connect(fillAction, &QAction::triggered, [this](){ m_editor->fitNoteLengths(true); }); fillAction->setShortcut(combine(Qt::SHIFT, Qt::Key_F)); @@ -4857,6 +5038,7 @@ PianoRollWindow::PianoRollWindow() : noteToolsButton->addAction(glueAction); noteToolsButton->addAction(knifeAction); + noteToolsButton->addAction(strumAction); noteToolsButton->addAction(fillAction); noteToolsButton->addAction(cutOverlapsAction); noteToolsButton->addAction(minLengthAction); From 736775082358286705d649576741afef4931c5f9 Mon Sep 17 00:00:00 2001 From: Rossmaxx <74815851+Rossmaxx@users.noreply.github.com> Date: Mon, 24 Mar 2025 19:07:46 +0530 Subject: [PATCH 16/62] [Code Cleanup] Cleaned up some header files and move a bit of debugging logic to cmake (#7677) * Renamed lmms_basics.h to LmmsTypes.h and scoped it down for that purpose. * Shifted the LMMS_STRINGIFY macro to it's own header. * Removed the debug.h header file and use cmake to handle the macro logic. * Remove some unused headers and include directives --- CMakeLists.txt | 10 +++ include/AudioDevice.h | 2 +- include/AudioEngine.h | 2 +- include/AudioEngineProfiler.h | 2 +- include/AutomationEditor.h | 2 +- include/BandLimitedWave.h | 2 +- include/BasicFilters.h | 8 ++- include/BufferManager.h | 2 +- include/CPULoadWidget.h | 2 +- include/ControllerRackView.h | 1 - include/Delay.h | 2 +- include/DrumSynth.h | 2 +- include/DspEffectLibrary.h | 2 +- include/EffectRackView.h | 1 - include/Engine.h | 2 +- include/EnvelopeAndLfoParameters.h | 2 +- include/Graph.h | 2 +- include/Instrument.h | 2 +- include/JournallingObject.h | 2 +- include/LadspaManager.h | 2 +- include/LfoController.h | 2 +- include/{debug.h => LmmsCommonMacros.h} | 27 +++---- include/{lmms_basics.h => LmmsTypes.h} | 20 +----- include/LocklessRingBuffer.h | 2 +- include/Lv2Ports.h | 2 +- include/Midi.h | 2 - include/MixHelpers.h | 2 +- include/Oscillator.h | 1 - include/OscillatorConstants.h | 2 +- include/Oscilloscope.h | 2 +- include/OutputSettings.h | 2 +- include/PianoRoll.h | 2 +- include/PlayHandle.h | 2 +- include/ProjectJournal.h | 2 +- include/ProjectRenderer.h | 1 - include/RemotePluginBase.h | 2 + include/RingBuffer.h | 2 +- include/SampleBuffer.h | 2 +- include/SampleDecoder.h | 1 - include/SampleFrame.h | 3 +- include/SetupDialog.h | 1 - include/SweepOscillator.h | 72 ------------------- include/ThreadableJob.h | 2 +- include/TimePos.h | 2 +- include/Track.h | 2 +- include/embed.h | 3 +- include/endian_handling.h | 3 +- include/lmms_constants.h | 17 ++++- include/lmms_math.h | 1 - include/panning.h | 2 +- include/versioninfo.h | 2 +- include/volume.h | 2 +- .../AudioFileProcessor/AudioFileProcessor.cpp | 2 +- .../AudioFileProcessor/AudioFileProcessor.h | 2 +- plugins/Delay/StereoDelay.cpp | 2 +- plugins/Delay/StereoDelay.h | 2 +- plugins/Eq/EqSpectrumView.h | 2 +- plugins/Flanger/MonoDelay.h | 2 +- .../LadspaEffect/LadspaMatrixControlDialog.h | 2 +- plugins/MidiImport/MidiImport.cpp | 1 - plugins/MultitapEcho/MultitapEcho.cpp | 2 +- plugins/OpulenZ/OpulenZ.cpp | 1 - plugins/SlicerT/SlicerT.h | 2 +- plugins/SpectrumAnalyzer/Analyzer.cpp | 2 +- plugins/SpectrumAnalyzer/SaProcessor.cpp | 4 +- plugins/SpectrumAnalyzer/SaProcessor.h | 2 +- plugins/TripleOscillator/TripleOscillator.cpp | 1 - plugins/Vibed/VibratingString.cpp | 2 +- plugins/Vibed/VibratingString.h | 2 +- plugins/VstBase/RemoteVstPlugin.cpp | 6 +- plugins/VstBase/vst_base.cpp | 1 + src/core/AudioEngine.cpp | 1 - src/core/BufferManager.cpp | 2 - src/core/FileSearch.cpp | 2 - src/core/Instrument.cpp | 2 +- src/core/LadspaManager.cpp | 1 + src/core/LfoController.cpp | 1 + src/core/Mixer.cpp | 1 - src/core/Oscillator.cpp | 1 - src/core/RemotePlugin.cpp | 1 - src/core/SampleBuffer.cpp | 1 - src/core/SampleDecoder.cpp | 2 +- src/core/SampleRecordHandle.cpp | 1 - src/core/audio/AudioDevice.cpp | 1 - src/core/audio/AudioSampleRecorder.cpp | 1 - src/core/audio/AudioSdl.cpp | 2 +- src/core/audio/AudioSoundIo.cpp | 1 - src/core/lv2/Lv2ControlBase.cpp | 1 + src/gui/AudioAlsaSetupWidget.cpp | 1 + src/gui/MixerView.cpp | 4 +- src/gui/editors/AutomationEditor.cpp | 1 - src/gui/editors/PianoRoll.cpp | 1 - src/gui/instrument/EnvelopeGraph.cpp | 1 - src/gui/modals/SetupDialog.cpp | 1 - src/gui/widgets/Oscilloscope.cpp | 1 - 95 files changed, 108 insertions(+), 206 deletions(-) rename include/{debug.h => LmmsCommonMacros.h} (64%) rename include/{lmms_basics.h => LmmsTypes.h} (84%) delete mode 100644 include/SweepOscillator.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a93d7383a..f439815cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -634,11 +634,20 @@ ENDIF() SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DLMMS_DEBUG") SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DLMMS_DEBUG") +if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + add_compile_definitions(NDEBUG) + SET(STATUS_ASSERTIONS "Disabled") +else() + remove_definitions(-DNDEBUG) + SET(STATUS_ASSERTIONS "Enabled") +endif() + if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.16") set(NOOP_COMMAND "${CMAKE_COMMAND}" "-E" "true") else() set(NOOP_COMMAND "${CMAKE_COMMAND}" "-E" "echo") endif() + if(STRIP) # TODO CMake 3.19: Now that CONFIG generator expressions support testing for # multiple configurations, combine the OR into a single CONFIG expression. @@ -865,6 +874,7 @@ MESSAGE( "* Debug using UBSanitizer : ${STATUS_DEBUG_UBSAN}\n" "* Debug packaging commands : ${STATUS_DEBUG_CPACK}\n" "* Profile using GNU profiler : ${STATUS_GPROF}\n" +"* Debug assertions : ${STATUS_ASSERTIONS}\n" ) MESSAGE( diff --git a/include/AudioDevice.h b/include/AudioDevice.h index 228b1c1aa..0a649bbe0 100644 --- a/include/AudioDevice.h +++ b/include/AudioDevice.h @@ -28,7 +28,7 @@ #include #include -#include "lmms_basics.h" +#include "LmmsTypes.h" class QThread; diff --git a/include/AudioEngine.h b/include/AudioEngine.h index 13758284d..20579f54d 100644 --- a/include/AudioEngine.h +++ b/include/AudioEngine.h @@ -34,7 +34,7 @@ #include #include "AudioDevice.h" -#include "lmms_basics.h" +#include "LmmsTypes.h" #include "SampleFrame.h" #include "LocklessList.h" #include "FifoBuffer.h" diff --git a/include/AudioEngineProfiler.h b/include/AudioEngineProfiler.h index b0d62a1dc..cee1ea0dd 100644 --- a/include/AudioEngineProfiler.h +++ b/include/AudioEngineProfiler.h @@ -29,7 +29,7 @@ #include #include -#include "lmms_basics.h" +#include "LmmsTypes.h" #include "MicroTimer.h" namespace lmms diff --git a/include/AutomationEditor.h b/include/AutomationEditor.h index eb3d229a3..61f1cb791 100644 --- a/include/AutomationEditor.h +++ b/include/AutomationEditor.h @@ -37,7 +37,7 @@ #include "MidiClip.h" #include "SampleClip.h" #include "TimePos.h" -#include "lmms_basics.h" +#include "LmmsTypes.h" #include "SampleThumbnail.h" class QPainter; diff --git a/include/BandLimitedWave.h b/include/BandLimitedWave.h index c70b8f6eb..0c38413ef 100644 --- a/include/BandLimitedWave.h +++ b/include/BandLimitedWave.h @@ -31,7 +31,7 @@ class QString; #include "lmms_export.h" #include "interpolation.h" -#include "lmms_basics.h" +#include "LmmsTypes.h" #include "lmms_math.h" #include "Engine.h" #include "AudioEngine.h" diff --git a/include/BasicFilters.h b/include/BasicFilters.h index 51d617480..a0ab32871 100644 --- a/include/BasicFilters.h +++ b/include/BasicFilters.h @@ -36,7 +36,9 @@ #include #include -#include "lmms_basics.h" +#include "lmms_constants.h" +#include "LmmsTypes.h" + namespace lmms { @@ -207,7 +209,7 @@ public: inline float update( float s, ch_cnt_t ch ) { - if (std::abs(s) < 1.0e-10f && std::abs(m_z1[ch]) < 1.0e-10f) { return 0.0f; } + if (std::abs(s) < F_EPSILON && std::abs(m_z1[ch]) < F_EPSILON) { return 0.0f; } return m_z1[ch] = s * m_a0 + m_z1[ch] * m_b1; } @@ -593,7 +595,7 @@ public: case FilterType::Formantfilter: case FilterType::FastFormant: { - if (std::abs(_in0) < 1.0e-10f && std::abs(m_vflast[0][_chnl]) < 1.0e-10f) { return 0.0f; } // performance hack - skip processing when the numbers get too small + if (std::abs(_in0) < F_EPSILON && std::abs(m_vflast[0][_chnl]) < F_EPSILON) { return 0.0f; } // performance hack - skip processing when the numbers get too small const int os = m_type == FilterType::FastFormant ? 1 : 4; // no oversampling for fast formant for( int o = 0; o < os; ++o ) diff --git a/include/BufferManager.h b/include/BufferManager.h index 84602f121..0a39114ca 100644 --- a/include/BufferManager.h +++ b/include/BufferManager.h @@ -27,7 +27,7 @@ #define LMMS_BUFFER_MANAGER_H #include "lmms_export.h" -#include "lmms_basics.h" +#include "LmmsTypes.h" namespace lmms { diff --git a/include/CPULoadWidget.h b/include/CPULoadWidget.h index bed10b05e..17a046e37 100644 --- a/include/CPULoadWidget.h +++ b/include/CPULoadWidget.h @@ -31,7 +31,7 @@ #include #include -#include "lmms_basics.h" +#include "LmmsTypes.h" namespace lmms::gui diff --git a/include/ControllerRackView.h b/include/ControllerRackView.h index 93d1e8438..436618303 100644 --- a/include/ControllerRackView.h +++ b/include/ControllerRackView.h @@ -29,7 +29,6 @@ #include #include "SerializingObject.h" -#include "lmms_basics.h" class QPushButton; diff --git a/include/Delay.h b/include/Delay.h index 8ead1c37b..199c592ce 100644 --- a/include/Delay.h +++ b/include/Delay.h @@ -28,7 +28,7 @@ #include -#include "lmms_basics.h" +#include "LmmsTypes.h" namespace lmms { diff --git a/include/DrumSynth.h b/include/DrumSynth.h index 3b418abd6..2894817fe 100644 --- a/include/DrumSynth.h +++ b/include/DrumSynth.h @@ -28,7 +28,7 @@ #include -#include "lmms_basics.h" +#include "LmmsTypes.h" class QString; diff --git a/include/DspEffectLibrary.h b/include/DspEffectLibrary.h index 656d5b1dd..8b3060334 100644 --- a/include/DspEffectLibrary.h +++ b/include/DspEffectLibrary.h @@ -28,7 +28,7 @@ #include #include "lmms_math.h" -#include "lmms_basics.h" +#include "LmmsTypes.h" #include "SampleFrame.h" namespace lmms::DspEffectLibrary diff --git a/include/EffectRackView.h b/include/EffectRackView.h index fec627a56..f166612e2 100644 --- a/include/EffectRackView.h +++ b/include/EffectRackView.h @@ -30,7 +30,6 @@ #include "EffectChain.h" #include "ModelView.h" -#include "lmms_basics.h" class QScrollArea; class QVBoxLayout; diff --git a/include/Engine.h b/include/Engine.h index 7e19e2e84..cc37ecee8 100644 --- a/include/Engine.h +++ b/include/Engine.h @@ -30,7 +30,7 @@ #include "lmmsconfig.h" #include "lmms_export.h" -#include "lmms_basics.h" +#include "LmmsTypes.h" namespace lmms { diff --git a/include/EnvelopeAndLfoParameters.h b/include/EnvelopeAndLfoParameters.h index 50bfdf787..34cbae5ec 100644 --- a/include/EnvelopeAndLfoParameters.h +++ b/include/EnvelopeAndLfoParameters.h @@ -32,7 +32,7 @@ #include "AutomatableModel.h" #include "SampleBuffer.h" #include "TempoSyncKnobModel.h" -#include "lmms_basics.h" +#include "LmmsTypes.h" namespace lmms { diff --git a/include/Graph.h b/include/Graph.h index cc87b913e..4cb3af512 100644 --- a/include/Graph.h +++ b/include/Graph.h @@ -32,7 +32,7 @@ #include "Model.h" #include "ModelView.h" -#include "lmms_basics.h" +#include "LmmsTypes.h" namespace lmms { diff --git a/include/Instrument.h b/include/Instrument.h index 3f701f12e..21353f725 100644 --- a/include/Instrument.h +++ b/include/Instrument.h @@ -30,7 +30,7 @@ #include "Flags.h" #include "lmms_export.h" -#include "lmms_basics.h" +#include "LmmsTypes.h" #include "Plugin.h" #include "TimePos.h" diff --git a/include/JournallingObject.h b/include/JournallingObject.h index f3e134f34..bc6a89076 100644 --- a/include/JournallingObject.h +++ b/include/JournallingObject.h @@ -27,7 +27,7 @@ #include -#include "lmms_basics.h" +#include "LmmsTypes.h" #include "SerializingObject.h" namespace lmms diff --git a/include/LadspaManager.h b/include/LadspaManager.h index 1a3360231..0d2defafd 100644 --- a/include/LadspaManager.h +++ b/include/LadspaManager.h @@ -36,7 +36,7 @@ #include "lmms_export.h" -#include "lmms_basics.h" +#include "LmmsTypes.h" namespace lmms diff --git a/include/LfoController.h b/include/LfoController.h index 01b4b1862..e884cd5e4 100644 --- a/include/LfoController.h +++ b/include/LfoController.h @@ -31,8 +31,8 @@ #include "AutomatableModel.h" #include "Controller.h" #include "ControllerDialog.h" +#include "SampleBuffer.h" #include "TempoSyncKnobModel.h" -#include "Oscillator.h" namespace lmms { diff --git a/include/debug.h b/include/LmmsCommonMacros.h similarity index 64% rename from include/debug.h rename to include/LmmsCommonMacros.h index 7cf51acaa..9840240f9 100644 --- a/include/debug.h +++ b/include/LmmsCommonMacros.h @@ -1,8 +1,8 @@ /* - * debug.h - header file to be included for debugging purposes - * - * Copyright (c) 2004-2008 Tobias Doerffel + * LmmsCommonMacros.h - defines some common macros used in the codebase * + * Copyright (c) 2025 Roshan M R (Ross Maxx) + * * This file is part of LMMS - https://lmms.io * * This program is free software; you can redistribute it and/or @@ -22,20 +22,15 @@ * */ -#ifndef LMMS_DEBUG_H -#define LMMS_DEBUG_H +#ifndef LMMS_COMMON_MACROS_H +#define LMMS_COMMON_MACROS_H -#include "lmmsconfig.h" +namespace lmms +{ -// Define standard macro NDEBUG when building without debug flag to make sure asserts become no-ops. -#ifndef LMMS_DEBUG -#ifndef NDEBUG - #define NDEBUG -#endif -#endif // LMMS_DEBUG +#define LMMS_STRINGIFY(s) LMMS_STR(s) // a macro used to stringify the plugin name +#define LMMS_STR(PN) #PN -#include -#include +} // namespace lmms - -#endif // LMMS_DEBUG_H +#endif // LMMS_COMMON_MACROS_H diff --git a/include/lmms_basics.h b/include/LmmsTypes.h similarity index 84% rename from include/lmms_basics.h rename to include/LmmsTypes.h index ea9371603..cf759aef9 100644 --- a/include/lmms_basics.h +++ b/include/LmmsTypes.h @@ -1,5 +1,5 @@ /* - * lmms_basics.h - typedefs for common types that are used in the whole app + * LmmsTypes.h - typedefs for common types that are used in the whole app * * Copyright (c) 2004-2009 Tobias Doerffel * @@ -26,13 +26,9 @@ #define LMMS_TYPES_H #include - -#include "lmmsconfig.h" - #include - namespace lmms { @@ -55,20 +51,6 @@ using mix_ch_t = uint16_t; // Mixer-channel (0 to MAX_CHANNEL) using jo_id_t = uint32_t; // (unique) ID of a journalling object -constexpr ch_cnt_t DEFAULT_CHANNELS = 2; - -constexpr char LADSPA_PATH_SEPERATOR = -#ifdef LMMS_BUILD_WIN32 -';'; -#else -':'; -#endif - - - -#define LMMS_STRINGIFY(s) LMMS_STR(s) -#define LMMS_STR(PN) #PN - } // namespace lmms #endif // LMMS_TYPES_H diff --git a/include/LocklessRingBuffer.h b/include/LocklessRingBuffer.h index ec3f4fc52..45dab858f 100644 --- a/include/LocklessRingBuffer.h +++ b/include/LocklessRingBuffer.h @@ -30,7 +30,7 @@ #include -#include "lmms_basics.h" +#include "LmmsTypes.h" namespace lmms { diff --git a/include/Lv2Ports.h b/include/Lv2Ports.h index 1b2986938..8bbf7b4d2 100644 --- a/include/Lv2Ports.h +++ b/include/Lv2Ports.h @@ -34,7 +34,7 @@ #include #include "Flags.h" -#include "lmms_basics.h" +#include "LmmsTypes.h" #include "PluginIssue.h" diff --git a/include/Midi.h b/include/Midi.h index 21db021b3..d68dda318 100644 --- a/include/Midi.h +++ b/include/Midi.h @@ -25,8 +25,6 @@ #ifndef LMMS_MIDI_H #define LMMS_MIDI_H -#include "lmms_basics.h" - namespace lmms { diff --git a/include/MixHelpers.h b/include/MixHelpers.h index a55ad6058..3b0ecf968 100644 --- a/include/MixHelpers.h +++ b/include/MixHelpers.h @@ -25,7 +25,7 @@ #ifndef LMMS_MIX_HELPERS_H #define LMMS_MIX_HELPERS_H -#include "lmms_basics.h" +#include "LmmsTypes.h" namespace lmms { diff --git a/include/Oscillator.h b/include/Oscillator.h index 13d8264be..84e9264c9 100644 --- a/include/Oscillator.h +++ b/include/Oscillator.h @@ -34,7 +34,6 @@ #include "Engine.h" #include "lmms_math.h" -#include "lmmsconfig.h" #include "AudioEngine.h" #include "OscillatorConstants.h" #include "SampleBuffer.h" diff --git a/include/OscillatorConstants.h b/include/OscillatorConstants.h index 85ae38fe8..5aaab6f7a 100644 --- a/include/OscillatorConstants.h +++ b/include/OscillatorConstants.h @@ -28,7 +28,7 @@ #include -#include "lmms_basics.h" +#include "LmmsTypes.h" namespace lmms::OscillatorConstants { diff --git a/include/Oscilloscope.h b/include/Oscilloscope.h index 1e3c52b1e..8675cea33 100644 --- a/include/Oscilloscope.h +++ b/include/Oscilloscope.h @@ -28,7 +28,7 @@ #include #include -#include "lmms_basics.h" +#include "LmmsTypes.h" namespace lmms { diff --git a/include/OutputSettings.h b/include/OutputSettings.h index 8a7ebc993..283826543 100644 --- a/include/OutputSettings.h +++ b/include/OutputSettings.h @@ -26,7 +26,7 @@ #ifndef LMMS_OUTPUT_SETTINGS_H #define LMMS_OUTPUT_SETTINGS_H -#include "lmms_basics.h" +#include "LmmsTypes.h" namespace lmms { diff --git a/include/PianoRoll.h b/include/PianoRoll.h index 8ac59fe0a..fb175c374 100644 --- a/include/PianoRoll.h +++ b/include/PianoRoll.h @@ -35,7 +35,7 @@ #include "ComboBoxModel.h" #include "SerializingObject.h" #include "Note.h" -#include "lmms_basics.h" +#include "LmmsTypes.h" #include "Song.h" #include "StepRecorder.h" #include "StepRecorderWidget.h" diff --git a/include/PlayHandle.h b/include/PlayHandle.h index 5f0256a1a..fbe45471b 100644 --- a/include/PlayHandle.h +++ b/include/PlayHandle.h @@ -32,7 +32,7 @@ #include "Flags.h" #include "ThreadableJob.h" -#include "lmms_basics.h" +#include "LmmsTypes.h" class QThread; diff --git a/include/ProjectJournal.h b/include/ProjectJournal.h index 841bbf094..b5b8fc138 100644 --- a/include/ProjectJournal.h +++ b/include/ProjectJournal.h @@ -28,7 +28,7 @@ #include #include -#include "lmms_basics.h" +#include "LmmsTypes.h" #include "DataFile.h" diff --git a/include/ProjectRenderer.h b/include/ProjectRenderer.h index 14c584a2e..3183fc478 100644 --- a/include/ProjectRenderer.h +++ b/include/ProjectRenderer.h @@ -26,7 +26,6 @@ #define LMMS_PROJECT_RENDERER_H #include "AudioFileDevice.h" -#include "lmmsconfig.h" #include "AudioEngine.h" #include "OutputSettings.h" diff --git a/include/RemotePluginBase.h b/include/RemotePluginBase.h index 787742fc0..53ccce1c7 100644 --- a/include/RemotePluginBase.h +++ b/include/RemotePluginBase.h @@ -27,6 +27,8 @@ #include "MidiEvent.h" +#include "lmmsconfig.h" + #include #include #include diff --git a/include/RingBuffer.h b/include/RingBuffer.h index 41595be19..0a7bb550a 100644 --- a/include/RingBuffer.h +++ b/include/RingBuffer.h @@ -28,7 +28,7 @@ #include #include -#include "lmms_basics.h" +#include "LmmsTypes.h" #include "lmms_export.h" diff --git a/include/SampleBuffer.h b/include/SampleBuffer.h index 8ec6c5886..d21bacf84 100644 --- a/include/SampleBuffer.h +++ b/include/SampleBuffer.h @@ -34,7 +34,7 @@ #include "AudioEngine.h" #include "Engine.h" -#include "lmms_basics.h" +#include "LmmsTypes.h" #include "lmms_export.h" namespace lmms { diff --git a/include/SampleDecoder.h b/include/SampleDecoder.h index 2cfd00977..9109d4121 100644 --- a/include/SampleDecoder.h +++ b/include/SampleDecoder.h @@ -31,7 +31,6 @@ #include #include -#include "lmms_basics.h" #include "SampleFrame.h" namespace lmms { diff --git a/include/SampleFrame.h b/include/SampleFrame.h index 238a85dea..43db2f6e6 100644 --- a/include/SampleFrame.h +++ b/include/SampleFrame.h @@ -26,7 +26,8 @@ #ifndef LMMS_SAMPLEFRAME_H #define LMMS_SAMPLEFRAME_H -#include "lmms_basics.h" +#include "LmmsTypes.h" +#include "lmms_constants.h" #include #include diff --git a/include/SetupDialog.h b/include/SetupDialog.h index 23589f91a..f544b8977 100644 --- a/include/SetupDialog.h +++ b/include/SetupDialog.h @@ -30,7 +30,6 @@ #include "AudioDevice.h" #include "AudioDeviceSetupWidget.h" -#include "lmmsconfig.h" #include "MidiClient.h" #include "MidiSetupWidget.h" diff --git a/include/SweepOscillator.h b/include/SweepOscillator.h deleted file mode 100644 index c7c7938d1..000000000 --- a/include/SweepOscillator.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * SweepOscillator.h - sweeping oscillator - * - * Copyright (c) 2006-2014 Tobias Doerffel - * - * This file is part of LMMS - https://lmms.io - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program (see COPYING); if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - */ - -#ifndef LMMS_SWEEP_OSCILLATOR_H -#define LMMS_SWEEP_OSCILLATOR_H - -#include "Oscillator.h" -#include "DspEffectLibrary.h" - -namespace lmms -{ - -template -class SweepOscillator -{ -public: - SweepOscillator( const FX & _fx = FX() ) : - m_phase( 0.0f ), - m_FX( _fx ) - { - } - - virtual ~SweepOscillator() = default; - - void update( SampleFrame* buf, const fpp_t frames, const float freq1, const float freq2, const float sampleRate ) - { - const float df = freq2 - freq1; - for( fpp_t frame = 0; frame < frames; ++frame ) - { - const sample_t s = Oscillator::sinSample( m_phase ); - buf[frame][0] = s; - buf[frame][1] = s; - m_FX.nextSample( buf[frame][0], buf[frame][1] ); - m_phase += ( freq1 + ( frame * df / frames ) ) / sampleRate; - } - } - - -private: - float m_phase; - FX m_FX; - -// inline sample_t getSample( const float _sample ); -// inline void recalcPhase(); - -} ; - - -} // namespace lmms - -#endif // LMMS_SWEEP_OSCILLATOR_H diff --git a/include/ThreadableJob.h b/include/ThreadableJob.h index 9d5a0beee..2f65ed5d9 100644 --- a/include/ThreadableJob.h +++ b/include/ThreadableJob.h @@ -25,7 +25,7 @@ #ifndef LMMS_THREADABLE_JOB_H #define LMMS_THREADABLE_JOB_H -#include "lmms_basics.h" +#include "LmmsTypes.h" #include diff --git a/include/TimePos.h b/include/TimePos.h index 68f3bd01b..ab41832b3 100644 --- a/include/TimePos.h +++ b/include/TimePos.h @@ -29,7 +29,7 @@ #include #include #include "lmms_export.h" -#include "lmms_basics.h" +#include "LmmsTypes.h" namespace lmms { diff --git a/include/Track.h b/include/Track.h index a152640e1..274e7a1fc 100644 --- a/include/Track.h +++ b/include/Track.h @@ -31,7 +31,7 @@ #include "AutomatableModel.h" #include "JournallingObject.h" -#include "lmms_basics.h" +#include "LmmsTypes.h" #include diff --git a/include/embed.h b/include/embed.h index f5156b02e..489a06270 100644 --- a/include/embed.h +++ b/include/embed.h @@ -32,7 +32,8 @@ #include #include "lmms_export.h" -#include "lmms_basics.h" +#include "LmmsCommonMacros.h" + namespace lmms { diff --git a/include/endian_handling.h b/include/endian_handling.h index 7ddb22f22..793b22e46 100644 --- a/include/endian_handling.h +++ b/include/endian_handling.h @@ -25,10 +25,9 @@ #ifndef LMMS_ENDIAN_HANDLING_H #define LMMS_ENDIAN_HANDLING_H +#include #include -#include "lmms_basics.h" - namespace lmms { diff --git a/include/lmms_constants.h b/include/lmms_constants.h index 782e6849d..8be44a404 100644 --- a/include/lmms_constants.h +++ b/include/lmms_constants.h @@ -25,17 +25,23 @@ #ifndef LMMS_CONSTANTS_H #define LMMS_CONSTANTS_H +#include "lmmsconfig.h" +#include "LmmsTypes.h" + namespace lmms { - // Prefer using `approximatelyEqual()` from lmms_math.h rather than // using this directly inline constexpr float F_EPSILON = 1.0e-10f; // 10^-10 +inline constexpr ch_cnt_t DEFAULT_CHANNELS = 2; + // Microtuner inline constexpr unsigned MaxScaleCount = 10; //!< number of scales per project inline constexpr unsigned MaxKeymapCount = 10; //!< number of keyboard mappings per project + +// Note: All constants below are used only in spectrum analyser // Frequency ranges (in Hz). // Arbitrary low limit for logarithmic frequency scale; >1 Hz. inline constexpr auto LOWEST_LOG_FREQ = 5; @@ -79,6 +85,15 @@ inline constexpr auto ARANGE_LOUD_END = 0; inline constexpr auto ARANGE_SILENT_START = -60; inline constexpr auto ARANGE_SILENT_END = -10; + +// This macro is used to handle path seperation properly in windows +constexpr char LADSPA_PATH_SEPERATOR = +#ifdef LMMS_BUILD_WIN32 +';'; +#else +':'; +#endif + } // namespace lmms #endif // LMMS_CONSTANTS_H diff --git a/include/lmms_math.h b/include/lmms_math.h index 1d3de249d..5129743de 100644 --- a/include/lmms_math.h +++ b/include/lmms_math.h @@ -34,7 +34,6 @@ #include #include -#include "lmmsconfig.h" #include "lmms_constants.h" namespace lmms diff --git a/include/panning.h b/include/panning.h index 2945988ba..fa9cde9ee 100644 --- a/include/panning.h +++ b/include/panning.h @@ -26,7 +26,7 @@ #ifndef LMMS_PANNING_H #define LMMS_PANNING_H -#include "lmms_basics.h" +#include "LmmsTypes.h" #include "Midi.h" #include "volume.h" diff --git a/include/versioninfo.h b/include/versioninfo.h index 7495299c2..f2a535313 100644 --- a/include/versioninfo.h +++ b/include/versioninfo.h @@ -1,7 +1,7 @@ #ifndef LMMS_VERSION_INFO_H #define LMMS_VERSION_INFO_H -#include "lmms_basics.h" +#include "LmmsCommonMacros.h" #if defined(__GNUC__) constexpr const char* LMMS_BUILDCONF_COMPILER_VERSION = "GCC " __VERSION__; diff --git a/include/volume.h b/include/volume.h index 382f76780..76c6c37de 100644 --- a/include/volume.h +++ b/include/volume.h @@ -26,7 +26,7 @@ #ifndef LMMS_VOLUME_H #define LMMS_VOLUME_H -#include "lmms_basics.h" +#include "LmmsTypes.h" namespace lmms { diff --git a/plugins/AudioFileProcessor/AudioFileProcessor.cpp b/plugins/AudioFileProcessor/AudioFileProcessor.cpp index 4cc14ba9c..6754624df 100644 --- a/plugins/AudioFileProcessor/AudioFileProcessor.cpp +++ b/plugins/AudioFileProcessor/AudioFileProcessor.cpp @@ -30,7 +30,7 @@ #include "SampleLoader.h" #include "Song.h" -#include "lmms_basics.h" +#include "LmmsTypes.h" #include "plugin_export.h" #include diff --git a/plugins/AudioFileProcessor/AudioFileProcessor.h b/plugins/AudioFileProcessor/AudioFileProcessor.h index acdbc45f7..0548d5b45 100644 --- a/plugins/AudioFileProcessor/AudioFileProcessor.h +++ b/plugins/AudioFileProcessor/AudioFileProcessor.h @@ -32,7 +32,7 @@ #include "Instrument.h" #include "Sample.h" -#include "lmms_basics.h" +#include "LmmsTypes.h" namespace lmms diff --git a/plugins/Delay/StereoDelay.cpp b/plugins/Delay/StereoDelay.cpp index 3187b3f2a..7381dc3e3 100644 --- a/plugins/Delay/StereoDelay.cpp +++ b/plugins/Delay/StereoDelay.cpp @@ -24,7 +24,7 @@ #include "StereoDelay.h" -#include "lmms_basics.h" +#include "LmmsTypes.h" #include "SampleFrame.h" namespace lmms diff --git a/plugins/Delay/StereoDelay.h b/plugins/Delay/StereoDelay.h index ad0e020b5..72b49a9df 100644 --- a/plugins/Delay/StereoDelay.h +++ b/plugins/Delay/StereoDelay.h @@ -25,7 +25,7 @@ #ifndef STEREODELAY_H #define STEREODELAY_H -#include "lmms_basics.h" +#include "LmmsTypes.h" namespace lmms diff --git a/plugins/Eq/EqSpectrumView.h b/plugins/Eq/EqSpectrumView.h index 448c0e378..8246c89bb 100644 --- a/plugins/Eq/EqSpectrumView.h +++ b/plugins/Eq/EqSpectrumView.h @@ -27,7 +27,7 @@ #include #include "fft_helpers.h" -#include "lmms_basics.h" +#include "LmmsTypes.h" namespace lmms { diff --git a/plugins/Flanger/MonoDelay.h b/plugins/Flanger/MonoDelay.h index 2e9324a7d..95bf460c5 100644 --- a/plugins/Flanger/MonoDelay.h +++ b/plugins/Flanger/MonoDelay.h @@ -25,7 +25,7 @@ #ifndef MONODELAY_H #define MONODELAY_H -#include "lmms_basics.h" +#include "LmmsTypes.h" namespace lmms { diff --git a/plugins/LadspaEffect/LadspaMatrixControlDialog.h b/plugins/LadspaEffect/LadspaMatrixControlDialog.h index c5949fa15..fa9a6e1b3 100644 --- a/plugins/LadspaEffect/LadspaMatrixControlDialog.h +++ b/plugins/LadspaEffect/LadspaMatrixControlDialog.h @@ -28,7 +28,7 @@ #include "EffectControlDialog.h" -#include "lmms_basics.h" +#include "LmmsTypes.h" class QGridLayout; diff --git a/plugins/MidiImport/MidiImport.cpp b/plugins/MidiImport/MidiImport.cpp index 785cd079a..24fed85df 100644 --- a/plugins/MidiImport/MidiImport.cpp +++ b/plugins/MidiImport/MidiImport.cpp @@ -43,7 +43,6 @@ #include "GuiApplication.h" #include "MainWindow.h" #include "TimePos.h" -#include "debug.h" #include "Song.h" #include "plugin_export.h" diff --git a/plugins/MultitapEcho/MultitapEcho.cpp b/plugins/MultitapEcho/MultitapEcho.cpp index 3d92e5ae8..ab84138aa 100644 --- a/plugins/MultitapEcho/MultitapEcho.cpp +++ b/plugins/MultitapEcho/MultitapEcho.cpp @@ -25,7 +25,7 @@ #include "MultitapEcho.h" #include "embed.h" -#include "lmms_basics.h" +#include "LmmsTypes.h" #include "lmms_math.h" #include "plugin_export.h" diff --git a/plugins/OpulenZ/OpulenZ.cpp b/plugins/OpulenZ/OpulenZ.cpp index 48dfb6c8c..f5c0e1a8d 100644 --- a/plugins/OpulenZ/OpulenZ.cpp +++ b/plugins/OpulenZ/OpulenZ.cpp @@ -51,7 +51,6 @@ #include #include "embed.h" -#include "debug.h" #include "Knob.h" #include "PixmapButton.h" diff --git a/plugins/SlicerT/SlicerT.h b/plugins/SlicerT/SlicerT.h index 53b8bfb2a..6021f44a3 100644 --- a/plugins/SlicerT/SlicerT.h +++ b/plugins/SlicerT/SlicerT.h @@ -36,7 +36,7 @@ #include "Sample.h" #include "SampleBuffer.h" #include "SlicerTView.h" -#include "lmms_basics.h" +#include "LmmsTypes.h" namespace lmms { diff --git a/plugins/SpectrumAnalyzer/Analyzer.cpp b/plugins/SpectrumAnalyzer/Analyzer.cpp index 7b6086ed2..725c175a4 100644 --- a/plugins/SpectrumAnalyzer/Analyzer.cpp +++ b/plugins/SpectrumAnalyzer/Analyzer.cpp @@ -33,7 +33,7 @@ #endif #include "embed.h" -#include "lmms_basics.h" +#include "LmmsTypes.h" #include "plugin_export.h" namespace lmms diff --git a/plugins/SpectrumAnalyzer/SaProcessor.cpp b/plugins/SpectrumAnalyzer/SaProcessor.cpp index 1ea80f126..d4e5456dc 100644 --- a/plugins/SpectrumAnalyzer/SaProcessor.cpp +++ b/plugins/SpectrumAnalyzer/SaProcessor.cpp @@ -27,11 +27,9 @@ #include #include "lmms_math.h" -#ifdef SA_DEBUG - #include -#endif #include #ifdef SA_DEBUG + #include #include #include #endif diff --git a/plugins/SpectrumAnalyzer/SaProcessor.h b/plugins/SpectrumAnalyzer/SaProcessor.h index 3903bf9d6..12163f63b 100644 --- a/plugins/SpectrumAnalyzer/SaProcessor.h +++ b/plugins/SpectrumAnalyzer/SaProcessor.h @@ -33,7 +33,7 @@ #include #include -#include "lmms_basics.h" +#include "LmmsTypes.h" namespace lmms diff --git a/plugins/TripleOscillator/TripleOscillator.cpp b/plugins/TripleOscillator/TripleOscillator.cpp index 61a6c4919..55ad80082 100644 --- a/plugins/TripleOscillator/TripleOscillator.cpp +++ b/plugins/TripleOscillator/TripleOscillator.cpp @@ -29,7 +29,6 @@ #include "TripleOscillator.h" #include "AudioEngine.h" #include "AutomatableButton.h" -#include "debug.h" #include "Engine.h" #include "InstrumentTrack.h" #include "Knob.h" diff --git a/plugins/Vibed/VibratingString.cpp b/plugins/Vibed/VibratingString.cpp index 5a09a4549..00e178e24 100644 --- a/plugins/Vibed/VibratingString.cpp +++ b/plugins/Vibed/VibratingString.cpp @@ -26,7 +26,7 @@ #include "interpolation.h" #include "AudioEngine.h" #include "Engine.h" -#include "lmms_basics.h" +#include "LmmsTypes.h" #include #include diff --git a/plugins/Vibed/VibratingString.h b/plugins/Vibed/VibratingString.h index d1691415b..3f61e39f0 100644 --- a/plugins/Vibed/VibratingString.h +++ b/plugins/Vibed/VibratingString.h @@ -28,7 +28,7 @@ #include #include -#include "lmms_basics.h" +#include "LmmsTypes.h" namespace lmms { diff --git a/plugins/VstBase/RemoteVstPlugin.cpp b/plugins/VstBase/RemoteVstPlugin.cpp index f8bf39a37..966c16dc8 100644 --- a/plugins/VstBase/RemoteVstPlugin.cpp +++ b/plugins/VstBase/RemoteVstPlugin.cpp @@ -41,10 +41,6 @@ #ifdef LMMS_BUILD_LINUX -#ifndef NOMINMAX -#define NOMINMAX -#endif - #ifndef O_BINARY #define O_BINARY 0 #endif @@ -114,7 +110,7 @@ struct ERect #endif -#include "lmms_basics.h" +#include "LmmsTypes.h" #include "Midi.h" #include "communication.h" #include "IoHelper.h" diff --git a/plugins/VstBase/vst_base.cpp b/plugins/VstBase/vst_base.cpp index 154dca975..544d05e0b 100644 --- a/plugins/VstBase/vst_base.cpp +++ b/plugins/VstBase/vst_base.cpp @@ -24,6 +24,7 @@ */ +#include "LmmsCommonMacros.h" #include "Plugin.h" #include "vstbase_export.h" diff --git a/src/core/AudioEngine.cpp b/src/core/AudioEngine.cpp index 8dcc37434..3eb604fc8 100644 --- a/src/core/AudioEngine.cpp +++ b/src/core/AudioEngine.cpp @@ -36,7 +36,6 @@ #include "EnvelopeAndLfoParameters.h" #include "NotePlayHandle.h" #include "ConfigManager.h" -#include "SamplePlayHandle.h" // platform-specific audio-interface-classes #include "AudioAlsa.h" diff --git a/src/core/BufferManager.cpp b/src/core/BufferManager.cpp index 47598c633..9059064ae 100644 --- a/src/core/BufferManager.cpp +++ b/src/core/BufferManager.cpp @@ -28,8 +28,6 @@ #include "SampleFrame.h" -#include - namespace lmms { diff --git a/src/core/FileSearch.cpp b/src/core/FileSearch.cpp index 8a668360e..cc9d49af2 100644 --- a/src/core/FileSearch.cpp +++ b/src/core/FileSearch.cpp @@ -26,8 +26,6 @@ #include #include -#include - #include namespace lmms { diff --git a/src/core/Instrument.cpp b/src/core/Instrument.cpp index cae16dee8..07614c314 100644 --- a/src/core/Instrument.cpp +++ b/src/core/Instrument.cpp @@ -29,7 +29,7 @@ #include "DummyInstrument.h" #include "InstrumentTrack.h" -#include "lmms_basics.h" +#include "LmmsTypes.h" namespace lmms { diff --git a/src/core/LadspaManager.cpp b/src/core/LadspaManager.cpp index 5b94cac3f..4c982397b 100644 --- a/src/core/LadspaManager.cpp +++ b/src/core/LadspaManager.cpp @@ -36,6 +36,7 @@ #include "ConfigManager.h" #include "LadspaManager.h" #include "PluginFactory.h" +#include "lmms_constants.h" namespace lmms diff --git a/src/core/LfoController.cpp b/src/core/LfoController.cpp index 96ea71f7b..dcdb59b7f 100644 --- a/src/core/LfoController.cpp +++ b/src/core/LfoController.cpp @@ -29,6 +29,7 @@ #include #include "AudioEngine.h" +#include "Oscillator.h" #include "PathUtil.h" #include "SampleLoader.h" #include "Song.h" diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index 0add6008d..6007be466 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -26,7 +26,6 @@ #include "AudioEngine.h" #include "AudioEngineWorkerThread.h" -#include "BufferManager.h" #include "Mixer.h" #include "MixHelpers.h" #include "Song.h" diff --git a/src/core/Oscillator.cpp b/src/core/Oscillator.cpp index 5b48ddf3e..7aad9c58e 100644 --- a/src/core/Oscillator.cpp +++ b/src/core/Oscillator.cpp @@ -31,7 +31,6 @@ #endif #include -#include "BufferManager.h" #include "Engine.h" #include "AudioEngine.h" #include "AutomatableModel.h" diff --git a/src/core/RemotePlugin.cpp b/src/core/RemotePlugin.cpp index e0eeff524..25085e3b6 100644 --- a/src/core/RemotePlugin.cpp +++ b/src/core/RemotePlugin.cpp @@ -33,7 +33,6 @@ #include #endif -#include "BufferManager.h" #include "AudioEngine.h" #include "Engine.h" #include "Song.h" diff --git a/src/core/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index fda3f2f66..3283a7142 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -27,7 +27,6 @@ #include "PathUtil.h" #include "SampleDecoder.h" -#include "lmms_basics.h" namespace lmms { diff --git a/src/core/SampleDecoder.cpp b/src/core/SampleDecoder.cpp index d3ee091f4..eb640447c 100644 --- a/src/core/SampleDecoder.cpp +++ b/src/core/SampleDecoder.cpp @@ -37,7 +37,7 @@ #include "AudioEngine.h" #include "DrumSynth.h" #include "Engine.h" -#include "lmms_basics.h" +#include "LmmsTypes.h" namespace lmms { diff --git a/src/core/SampleRecordHandle.cpp b/src/core/SampleRecordHandle.cpp index f7003f3be..f62de7885 100644 --- a/src/core/SampleRecordHandle.cpp +++ b/src/core/SampleRecordHandle.cpp @@ -29,7 +29,6 @@ #include "PatternTrack.h" #include "SampleBuffer.h" #include "SampleClip.h" -#include "debug.h" namespace lmms diff --git a/src/core/audio/AudioDevice.cpp b/src/core/audio/AudioDevice.cpp index c02ce5f99..c5d56c997 100644 --- a/src/core/audio/AudioDevice.cpp +++ b/src/core/audio/AudioDevice.cpp @@ -27,7 +27,6 @@ #include "AudioDevice.h" #include "AudioEngine.h" #include "ConfigManager.h" -#include "debug.h" namespace lmms { diff --git a/src/core/audio/AudioSampleRecorder.cpp b/src/core/audio/AudioSampleRecorder.cpp index 0e51c9d2d..2e577d1c6 100644 --- a/src/core/audio/AudioSampleRecorder.cpp +++ b/src/core/audio/AudioSampleRecorder.cpp @@ -26,7 +26,6 @@ #include "AudioSampleRecorder.h" #include "SampleBuffer.h" -#include "debug.h" namespace lmms diff --git a/src/core/audio/AudioSdl.cpp b/src/core/audio/AudioSdl.cpp index 8f533119c..dfbcde737 100644 --- a/src/core/audio/AudioSdl.cpp +++ b/src/core/audio/AudioSdl.cpp @@ -23,7 +23,7 @@ */ #include "AudioSdl.h" -#include "lmms_basics.h" +#include "LmmsTypes.h" #ifdef LMMS_HAVE_SDL diff --git a/src/core/audio/AudioSoundIo.cpp b/src/core/audio/AudioSoundIo.cpp index 851592018..01f804935 100644 --- a/src/core/audio/AudioSoundIo.cpp +++ b/src/core/audio/AudioSoundIo.cpp @@ -30,7 +30,6 @@ #include #include "Engine.h" -#include "debug.h" #include "ConfigManager.h" #include "ComboBox.h" #include "AudioEngine.h" diff --git a/src/core/lv2/Lv2ControlBase.cpp b/src/core/lv2/Lv2ControlBase.cpp index 0147ebd6e..27e6348ae 100644 --- a/src/core/lv2/Lv2ControlBase.cpp +++ b/src/core/lv2/Lv2ControlBase.cpp @@ -31,6 +31,7 @@ #include #include "Engine.h" +#include "lmms_constants.h" #include "Lv2Manager.h" #include "Lv2Proc.h" diff --git a/src/gui/AudioAlsaSetupWidget.cpp b/src/gui/AudioAlsaSetupWidget.cpp index 43872a12f..9764bd863 100644 --- a/src/gui/AudioAlsaSetupWidget.cpp +++ b/src/gui/AudioAlsaSetupWidget.cpp @@ -31,6 +31,7 @@ #include "ConfigManager.h" #include "LcdSpinBox.h" +#include "lmms_constants.h" namespace lmms::gui { diff --git a/src/gui/MixerView.cpp b/src/gui/MixerView.cpp index 5cb99295b..e0b483bd6 100644 --- a/src/gui/MixerView.cpp +++ b/src/gui/MixerView.cpp @@ -22,6 +22,7 @@ * */ +#include "MixerView.h" #include #include @@ -29,10 +30,7 @@ #include #include -#include "lmms_math.h" - #include "MixerChannelView.h" -#include "MixerView.h" #include "Knob.h" #include "Mixer.h" #include "GuiApplication.h" diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index 78f5d112a..e1805f727 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -58,7 +58,6 @@ #include "StringPairDrag.h" #include "TextFloat.h" #include "TimeLineWidget.h" -#include "debug.h" #include "embed.h" #include "FontHelper.h" diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index d075a70b8..63d2a81a6 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -50,7 +50,6 @@ #include "ComboBox.h" #include "ConfigManager.h" #include "DataFile.h" -#include "debug.h" #include "DeprecationHelper.h" #include "DetuningHelper.h" #include "embed.h" diff --git a/src/gui/instrument/EnvelopeGraph.cpp b/src/gui/instrument/EnvelopeGraph.cpp index 3483f46a0..c9d60646e 100644 --- a/src/gui/instrument/EnvelopeGraph.cpp +++ b/src/gui/instrument/EnvelopeGraph.cpp @@ -30,7 +30,6 @@ #include #include "EnvelopeAndLfoParameters.h" -#include "lmms_math.h" #include "ColorHelper.h" #include diff --git a/src/gui/modals/SetupDialog.cpp b/src/gui/modals/SetupDialog.cpp index 06f228ab7..eb65e2a2c 100644 --- a/src/gui/modals/SetupDialog.cpp +++ b/src/gui/modals/SetupDialog.cpp @@ -33,7 +33,6 @@ #include #include "AudioEngine.h" -#include "debug.h" #include "embed.h" #include "Engine.h" #include "FileDialog.h" diff --git a/src/gui/widgets/Oscilloscope.cpp b/src/gui/widgets/Oscilloscope.cpp index bf66fa465..122a13ce9 100644 --- a/src/gui/widgets/Oscilloscope.cpp +++ b/src/gui/widgets/Oscilloscope.cpp @@ -34,7 +34,6 @@ #include "Engine.h" #include "Song.h" #include "embed.h" -#include "BufferManager.h" namespace lmms::gui { From 498315ef487456402d00f6a3a39c183c49358d16 Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Mon, 24 Mar 2025 13:34:28 -0400 Subject: [PATCH 17/62] Make buttons automatable on macOS (#7813) --- src/gui/widgets/AutomatableButton.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gui/widgets/AutomatableButton.cpp b/src/gui/widgets/AutomatableButton.cpp index 6e9cd23e4..c205b75cf 100644 --- a/src/gui/widgets/AutomatableButton.cpp +++ b/src/gui/widgets/AutomatableButton.cpp @@ -29,6 +29,7 @@ #include "CaptionMenu.h" #include "StringPairDrag.h" +#include "KeyboardShortcuts.h" namespace lmms::gui @@ -111,7 +112,7 @@ void AutomatableButton::contextMenuEvent( QContextMenuEvent * _me ) void AutomatableButton::mousePressEvent( QMouseEvent * _me ) { if( _me->button() == Qt::LeftButton && - ! ( _me->modifiers() & Qt::ControlModifier ) ) + ! ( _me->modifiers() & KBD_COPY_MODIFIER ) ) { // User simply clicked, toggle if needed if( isCheckable() ) From 1d5f2c005078963ca8abdc5a7ef1be18eef602c2 Mon Sep 17 00:00:00 2001 From: Sotonye Atemie Date: Tue, 25 Mar 2025 16:27:00 -0400 Subject: [PATCH 18/62] Add ability to change sample rate in the settings menu (#7719) Add a slider in the audio settings menu to allow users to select a few standard sample rates, those being 44100, 48000, 88200, 96000, and 192000. --- include/AudioEngine.h | 3 +- include/SetupDialog.h | 2 ++ src/core/AudioEngine.cpp | 2 +- src/gui/modals/ExportProjectDialog.cpp | 13 +++++--- src/gui/modals/SetupDialog.cpp | 43 ++++++++++++++++++++++++++ src/gui/modals/export_project.ui | 28 +---------------- 6 files changed, 58 insertions(+), 33 deletions(-) diff --git a/include/AudioEngine.h b/include/AudioEngine.h index 20579f54d..49b511a17 100644 --- a/include/AudioEngine.h +++ b/include/AudioEngine.h @@ -50,7 +50,6 @@ class MidiClient; class AudioBusHandle; class AudioEngineWorkerThread; - constexpr fpp_t MINIMUM_BUFFER_SIZE = 32; constexpr fpp_t DEFAULT_BUFFER_SIZE = 256; constexpr fpp_t MAXIMUM_BUFFER_SIZE = 4096; @@ -61,6 +60,8 @@ constexpr int BYTES_PER_FRAME = sizeof(SampleFrame); constexpr float OUTPUT_SAMPLE_MULTIPLIER = 32767.0f; +constexpr auto SUPPORTED_SAMPLERATES = std::array{44100, 48000, 88200, 96000, 192000}; + class LMMS_EXPORT AudioEngine : public QObject { Q_OBJECT diff --git a/include/SetupDialog.h b/include/SetupDialog.h index f544b8977..c314ff42d 100644 --- a/include/SetupDialog.h +++ b/include/SetupDialog.h @@ -182,6 +182,8 @@ private: QSlider * m_bufferSizeSlider; QLabel * m_bufferSizeLbl; QLabel * m_bufferSizeWarnLbl; + int m_sampleRate; + QSlider* m_sampleRateSlider; // MIDI settings widgets. QComboBox * m_midiInterfaces; diff --git a/src/core/AudioEngine.cpp b/src/core/AudioEngine.cpp index 3eb604fc8..99db26d5f 100644 --- a/src/core/AudioEngine.cpp +++ b/src/core/AudioEngine.cpp @@ -73,7 +73,7 @@ static thread_local bool s_renderingThread = false; AudioEngine::AudioEngine( bool renderOnly ) : m_renderOnly( renderOnly ), m_framesPerPeriod( DEFAULT_BUFFER_SIZE ), - m_baseSampleRate(std::max(ConfigManager::inst()->value("audioengine", "samplerate").toInt(), 44100)), + m_baseSampleRate(std::max(ConfigManager::inst()->value("audioengine", "samplerate").toInt(), SUPPORTED_SAMPLERATES.front())), m_inputBufferRead( 0 ), m_inputBufferWrite( 1 ), m_outputBufferRead(nullptr), diff --git a/src/gui/modals/ExportProjectDialog.cpp b/src/gui/modals/ExportProjectDialog.cpp index 016ed447b..01d7c23b0 100644 --- a/src/gui/modals/ExportProjectDialog.cpp +++ b/src/gui/modals/ExportProjectDialog.cpp @@ -98,6 +98,14 @@ ExportProjectDialog::ExportProjectDialog( const QString & _file_name, compressionWidget->setVisible(false); #endif + for (const auto sampleRate : SUPPORTED_SAMPLERATES) + { + samplerateCB->addItem(tr("%1 Hz").arg(sampleRate), sampleRate); + } + + const auto currentIndex = std::max(0, samplerateCB->findData(Engine::audioEngine()->outputSampleRate())); + samplerateCB->setCurrentIndex(currentIndex); + connect( startButton, SIGNAL(clicked()), this, SLOT(startBtnClicked())); } @@ -156,12 +164,9 @@ void ExportProjectDialog::startExport() { auto qs = AudioEngine::qualitySettings( static_cast(interpolationCB->currentIndex())); - const auto samplerates = std::array{44100, 48000, 88200, 96000, 192000}; const auto bitrates = std::array{64, 128, 160, 192, 256, 320}; - const auto bitrate = bitrates[bitrateCB->currentIndex()]; - - OutputSettings os = OutputSettings(samplerates[samplerateCB->currentIndex()], bitrate, + OutputSettings os = OutputSettings(samplerateCB->currentData().toInt(), bitrates[bitrateCB->currentIndex()], static_cast(depthCB->currentIndex()), mapToStereoMode(stereoModeComboBox->currentIndex())); diff --git a/src/gui/modals/SetupDialog.cpp b/src/gui/modals/SetupDialog.cpp index eb65e2a2c..4ea0d4ddd 100644 --- a/src/gui/modals/SetupDialog.cpp +++ b/src/gui/modals/SetupDialog.cpp @@ -143,6 +143,8 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) : "app", "nanhandler", "1").toInt()), m_bufferSize(ConfigManager::inst()->value( "audioengine", "framesperaudiobuffer").toInt()), + m_sampleRate(ConfigManager::inst()->value( + "audioengine", "samplerate").toInt()), m_midiAutoQuantize(ConfigManager::inst()->value( "midi", "autoquantize", "0").toInt() != 0), m_workingDir(QDir::toNativeSeparators(ConfigManager::inst()->workingDir())), @@ -557,6 +559,44 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) : // audio_layout->addWidget(useNaNHandler); // useNaNHandler->setChecked(m_NaNHandler); + auto sampleRateBox = new QGroupBox{tr("Sample rate"), audio_w}; + + m_sampleRateSlider = new QSlider{Qt::Horizontal}; + m_sampleRateSlider->setRange(0, SUPPORTED_SAMPLERATES.size() - 1); + m_sampleRateSlider->setTickPosition(QSlider::TicksBelow); + + auto sampleRateResetButton = new QPushButton{embed::getIconPixmap("reload"), ""}; + sampleRateResetButton->setFixedSize(32, 32); + + auto sampleRateSubLayout = new QHBoxLayout{}; + sampleRateSubLayout->addWidget(m_sampleRateSlider); + sampleRateSubLayout->addWidget(sampleRateResetButton); + + auto sampleRateLabel = new QLabel{sampleRateBox}; + auto sampleRateLayout = new QVBoxLayout{sampleRateBox}; + sampleRateLayout->addLayout(sampleRateSubLayout); + sampleRateLayout->addWidget(sampleRateLabel); + + auto setSampleRate = [this, sampleRateLabel](int sampleRate) + { + const auto it = std::find(SUPPORTED_SAMPLERATES.begin(), SUPPORTED_SAMPLERATES.end(), sampleRate); + const auto index = it == SUPPORTED_SAMPLERATES.end() ? 0 : std::distance(SUPPORTED_SAMPLERATES.begin(), it); + + m_sampleRate = SUPPORTED_SAMPLERATES[index]; + m_sampleRateSlider->setValue(index); + sampleRateLabel->setText(tr("Sample rate: %1").arg(m_sampleRate)); + }; + + setSampleRate(m_sampleRate); + + connect(m_sampleRateSlider, &QSlider::valueChanged, this, &SetupDialog::showRestartWarning); + + connect(m_sampleRateSlider, &QSlider::valueChanged, this, + [setSampleRate](int value) { setSampleRate(SUPPORTED_SAMPLERATES[value]); }); + + connect(sampleRateResetButton, &QPushButton::clicked, this, + [setSampleRate] { setSampleRate(SUPPORTED_SAMPLERATES.front()); }); + // Buffer size group QGroupBox * bufferSizeBox = new QGroupBox(tr("Buffer size"), audio_w); QVBoxLayout * bufferSizeLayout = new QVBoxLayout(bufferSizeBox); @@ -598,6 +638,7 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) : // Audio layout ordering. audio_layout->addWidget(audioInterfaceBox); audio_layout->addWidget(as_w); + audio_layout->addWidget(sampleRateBox); audio_layout->addWidget(bufferSizeBox); audio_layout->addStretch(); @@ -962,6 +1003,8 @@ void SetupDialog::accept() m_audioIfaceNames[m_audioInterfaces->currentText()]); ConfigManager::inst()->setValue("app", "nanhandler", QString::number(m_NaNHandler)); + ConfigManager::inst()->setValue("audioengine", "samplerate", + QString::number(m_sampleRate)); ConfigManager::inst()->setValue("audioengine", "framesperaudiobuffer", QString::number(m_bufferSize)); ConfigManager::inst()->setValue("audioengine", "mididev", diff --git a/src/gui/modals/export_project.ui b/src/gui/modals/export_project.ui index b047bec08..2a923021f 100644 --- a/src/gui/modals/export_project.ui +++ b/src/gui/modals/export_project.ui @@ -122,33 +122,7 @@ - - - - 44100 Hz - - - - - 48000 Hz - - - - - 88200 Hz - - - - - 96000 Hz - - - - - 192000 Hz - - - + From 8afe95aeaf463ec5f9a80efea0da30fae5ad92a0 Mon Sep 17 00:00:00 2001 From: Fawn Date: Tue, 25 Mar 2025 19:02:08 -0600 Subject: [PATCH 19/62] Fix track operations button alignment, size, and style (#7779) Make the track ops button consistently sized and style it and the solo & mute buttons using CSS instead of SVG assets --- data/themes/classic/style.css | 65 ++++++++++++----- data/themes/default/gear.svg | 26 +++++++ data/themes/default/headphones.svg | 44 ++++++++++++ data/themes/default/mute_active.svg | 37 ---------- data/themes/default/mute_inactive.svg | 37 ---------- data/themes/default/solo_active.svg | 37 ---------- data/themes/default/solo_inactive.svg | 37 ---------- data/themes/default/speaker.svg | 42 +++++++++++ data/themes/default/speaker_slash.svg | 44 ++++++++++++ data/themes/default/style.css | 66 +++++++++++++----- data/themes/default/trackop.png | Bin 229 -> 0 bytes include/InstrumentTrackWindow.h | 6 +- include/MixerChannelView.h | 4 +- include/SampleTrackWindow.h | 6 +- include/TrackOperationsWidget.h | 5 +- .../CrossoverEQ/CrossoverEQControlDialog.cpp | 24 +++---- src/gui/MixerChannelView.cpp | 10 ++- src/gui/SampleTrackWindow.cpp | 11 ++- src/gui/instrument/InstrumentTrackWindow.cpp | 10 ++- src/gui/tracks/TrackOperationsWidget.cpp | 48 +++---------- 20 files changed, 297 insertions(+), 262 deletions(-) create mode 100644 data/themes/default/gear.svg create mode 100644 data/themes/default/headphones.svg delete mode 100644 data/themes/default/mute_active.svg delete mode 100644 data/themes/default/mute_inactive.svg delete mode 100644 data/themes/default/solo_active.svg delete mode 100644 data/themes/default/solo_inactive.svg create mode 100644 data/themes/default/speaker.svg create mode 100644 data/themes/default/speaker_slash.svg delete mode 100644 data/themes/default/trackop.png diff --git a/data/themes/classic/style.css b/data/themes/classic/style.css index 8d8854028..a064366a6 100644 --- a/data/themes/classic/style.css +++ b/data/themes/classic/style.css @@ -376,32 +376,61 @@ lmms--gui--TrackContentWidget { /* gear button in tracks */ + +/* 20px = 1px border + 2px padding + 14px icon + 2px padding + 1px border */ +QPushButton#btn-mute, +QPushButton#btn-mute-inv:checked, +QPushButton#btn-solo, lmms--gui--TrackOperationsWidget QPushButton { - max-height: 26px; - max-width: 26px; - min-height: 26px; - min-width: 26px; - background: none; - border: none; + min-height: 14; + max-height: 14; + min-width: 14; + max-width: 14; + padding: 2; + border: 1 solid #0f1621; + border-top: 1 solid #18202b; + border-bottom: 1 solid #02060f; + border-radius: 4; + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 255, 255, 20%), stop:0.1 rgba(255, 255, 255, 2%), stop:0.9 rgba(0, 0, 0, 2%), stop:1 rgba(0, 0, 0, 40%)); } lmms--gui--TrackOperationsWidget QPushButton::menu-indicator { - image: url("resources:trackop.png"); - subcontrol-origin: padding; - subcontrol-position: center; - position: relative; - top: 1px; + image: none; } -lmms--gui--TrackOperationsWidget QPushButton::menu-indicator:hover { - image: url("resources:trackop_h.png"); +lmms--gui--TrackOperationsWidget QPushButton { + image: url("resources:gear.svg"); } -lmms--gui--TrackOperationsWidget QPushButton::menu-indicator:pressed, -lmms--gui--TrackOperationsWidget QPushButton::menu-indicator:checked { - image: url("resources:trackop_c.png"); - position: relative; - top: 2px; +QPushButton#btn-mute, +QPushButton#btn-mute-inv:checked { + image: url("resources:speaker.svg"); +} + +QPushButton#btn-solo { + image: url("resources:headphones.svg"); +} + +QPushButton#btn-mute:hover, +QPushButton#btn-solo:hover, +lmms--gui--TrackOperationsWidget QPushButton:hover { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 255, 255, 25%), stop:0.1 rgba(255, 255, 255, 7%), stop:0.9 transparent, stop:1 rgba(0, 0, 0, 35%)); +} + +lmms--gui--TrackOperationsWidget QPushButton:pressed { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(0, 0, 0, 25%), stop:0.1 transparent, stop:0.8 rgba(0, 0, 0, 15%), stop:1 rgba(0, 0, 0, 35%)); +} + +QPushButton#btn-mute-inv, +QPushButton#btn-mute:checked { + image: url("resources:speaker_slash.svg"); + border: 1 solid #890120; + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #e2515b, stop:0.1 #d40237, stop:0.9 #d40237, stop:1 #900122); +} + +QPushButton#btn-solo:checked { + border: 1 solid #055f89; + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #5aa8d9, stop:0.1 #2696d1, stop:0.9 #2696d1, stop:1 #16638c); } /* actually has no effect yet so disabled */ diff --git a/data/themes/default/gear.svg b/data/themes/default/gear.svg new file mode 100644 index 000000000..dfcd7d4d9 --- /dev/null +++ b/data/themes/default/gear.svg @@ -0,0 +1,26 @@ + + + + + + + + + Fawn Sannar + + + + + + + + + + + + diff --git a/data/themes/default/headphones.svg b/data/themes/default/headphones.svg new file mode 100644 index 000000000..ecdc5a18e --- /dev/null +++ b/data/themes/default/headphones.svg @@ -0,0 +1,44 @@ + + + LMMS solo button (inactive) + + + + + LMMS solo button (inactive) + + + Rebecca Noel Ati, Stakeout Punch + + + + + + + + + + + diff --git a/data/themes/default/mute_active.svg b/data/themes/default/mute_active.svg deleted file mode 100644 index 600144697..000000000 --- a/data/themes/default/mute_active.svg +++ /dev/null @@ -1,37 +0,0 @@ - - - LMMS mute button (active) - - - - - LMMS mute button (active) - - - Rebecca Noel Ati, Stakeout Punch - - - - - - - - - - - - - - - diff --git a/data/themes/default/mute_inactive.svg b/data/themes/default/mute_inactive.svg deleted file mode 100644 index 6042cc767..000000000 --- a/data/themes/default/mute_inactive.svg +++ /dev/null @@ -1,37 +0,0 @@ - - - LMMS mute button (inactive) - - - - - LMMS mute button (inactive) - - - Rebecca Noel Ati, Stakeout Punch - - - - - - - - - - - - - - - diff --git a/data/themes/default/solo_active.svg b/data/themes/default/solo_active.svg deleted file mode 100644 index d5c151be3..000000000 --- a/data/themes/default/solo_active.svg +++ /dev/null @@ -1,37 +0,0 @@ - - - LMMS solo button (active) - - - - - LMMS solo button (active) - - - Rebecca Noel Ati, Stakeout Punch - - - - - - - - - - - - - - - diff --git a/data/themes/default/solo_inactive.svg b/data/themes/default/solo_inactive.svg deleted file mode 100644 index 57788c607..000000000 --- a/data/themes/default/solo_inactive.svg +++ /dev/null @@ -1,37 +0,0 @@ - - - LMMS solo button (inactive) - - - - - LMMS solo button (inactive) - - - Rebecca Noel Ati, Stakeout Punch - - - - - - - - - - - - - - - diff --git a/data/themes/default/speaker.svg b/data/themes/default/speaker.svg new file mode 100644 index 000000000..1f43f5517 --- /dev/null +++ b/data/themes/default/speaker.svg @@ -0,0 +1,42 @@ + + + LMMS mute button (inactive) + + + + + LMMS mute button (inactive) + + + Rebecca Noel Ati, Stakeout Punch + + + + + + + + + + + diff --git a/data/themes/default/speaker_slash.svg b/data/themes/default/speaker_slash.svg new file mode 100644 index 000000000..9b42db7ac --- /dev/null +++ b/data/themes/default/speaker_slash.svg @@ -0,0 +1,44 @@ + + + LMMS mute button (active) + + + + + LMMS mute button (active) + + + Rebecca Noel Ati, Stakeout Punch + + + + + + + + + + + diff --git a/data/themes/default/style.css b/data/themes/default/style.css index 184bc9b56..07ee74617 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -412,30 +412,64 @@ lmms--gui--TrackContentWidget { /* gear button in tracks */ + +/* 20px = 1px border + 2px padding + 14px icon + 2px padding + 1px border */ +QPushButton#btn-mute, +QPushButton#btn-mute-inv:checked, +QPushButton#btn-solo, lmms--gui--TrackOperationsWidget QPushButton { - max-height: 26px; - max-width: 26px; - min-height: 26px; - min-width: 26px; - background: none; - border: none; + min-height: 14; + max-height: 14; + min-width: 14; + max-width: 14; + padding: 2; + border: 1 solid #0f1621; + border-top: 1 solid #18202b; + border-bottom: 1 solid #02060f; + border-radius: 2; + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 255, 255, 20%), stop:0.1 rgba(255, 255, 255, 2%), stop:0.9 rgba(0, 0, 0, 2%), stop:1 rgba(0, 0, 0, 40%)); } lmms--gui--TrackOperationsWidget QPushButton::menu-indicator { - image: url("resources:trackop.png"); - subcontrol-origin: padding; - subcontrol-position: center; - position: relative; - top: 1px; + image: none; } -lmms--gui--TrackOperationsWidget QPushButton::menu-indicator:pressed, -lmms--gui--TrackOperationsWidget QPushButton::menu-indicator:checked { - image: url("resources:trackop.png"); - position: relative; - top: 2px; +lmms--gui--TrackOperationsWidget QPushButton { + image: url("resources:gear.svg"); } +QPushButton#btn-mute, +QPushButton#btn-mute-inv:checked { + image: url("resources:speaker.svg"); +} + +QPushButton#btn-solo { + image: url("resources:headphones.svg"); +} + +QPushButton#btn-mute:hover, +QPushButton#btn-solo:hover, +lmms--gui--TrackOperationsWidget QPushButton:hover { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 255, 255, 25%), stop:0.1 rgba(255, 255, 255, 7%), stop:0.9 transparent, stop:1 rgba(0, 0, 0, 35%)); +} + +lmms--gui--TrackOperationsWidget QPushButton:pressed { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(0, 0, 0, 25%), stop:0.1 transparent, stop:0.8 rgba(0, 0, 0, 15%), stop:1 rgba(0, 0, 0, 35%)); +} + +QPushButton#btn-mute-inv, +QPushButton#btn-mute:checked { + image: url("resources:speaker_slash.svg"); + border: 1 solid #890120; + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #e2515b, stop:0.1 #d40237, stop:0.9 #d40237, stop:1 #900122); +} + +QPushButton#btn-solo:checked { + border: 1 solid #055f89; + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #5aa8d9, stop:0.1 #2696d1, stop:0.9 #2696d1, stop:1 #16638c); +} + + /* font sizes */ lmms--gui--Sf2InstrumentView > QLabel { diff --git a/data/themes/default/trackop.png b/data/themes/default/trackop.png deleted file mode 100644 index a4f90e35c98e61ec6267c13d2ad82661c3ffc1c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 229 zcmV^P)f}#{#)HN#ax>A6=(-8%9Sg0kzV^7O+UdZ|iAa_!AF +#include "AutomatableButton.h" #include "ModelView.h" -#include "PixmapButton.h" #include "SerializingObject.h" #include "PluginView.h" @@ -148,8 +148,8 @@ private: Knob * m_volumeKnob; Knob * m_panningKnob; Knob * m_pitchKnob; - PixmapButton *m_muteBtn; - PixmapButton *m_soloBtn; + AutomatableButton* m_muteBtn; + AutomatableButton* m_soloBtn; QLabel * m_pitchLabel; LcdSpinBox* m_pitchRangeSpinBox; QLabel * m_pitchRangeLabel; diff --git a/include/MixerChannelView.h b/include/MixerChannelView.h index 3d5f4ffb6..e9ee1eddb 100644 --- a/include/MixerChannelView.h +++ b/include/MixerChannelView.h @@ -115,8 +115,8 @@ private: QLineEdit* m_renameLineEdit; QGraphicsView* m_renameLineEditView; QLabel* m_sendArrow; - PixmapButton* m_muteButton; - PixmapButton* m_soloButton; + AutomatableButton* m_muteButton; + AutomatableButton* m_soloButton; PeakIndicator* m_peakIndicator = nullptr; Fader* m_fader; EffectRackView* m_effectRackView; diff --git a/include/SampleTrackWindow.h b/include/SampleTrackWindow.h index 01adb0080..f9aa0f71c 100644 --- a/include/SampleTrackWindow.h +++ b/include/SampleTrackWindow.h @@ -28,9 +28,9 @@ #include #include "ModelView.h" -#include "PixmapButton.h" #include "SampleTrack.h" #include "SerializingObject.h" +#include "AutomatableButton.h" class QLineEdit; @@ -91,8 +91,8 @@ private: QLineEdit * m_nameLineEdit; Knob * m_volumeKnob; Knob * m_panningKnob; - PixmapButton *m_muteBtn; - PixmapButton *m_soloBtn; + AutomatableButton* m_muteBtn; + AutomatableButton* m_soloBtn; MixerChannelLcdSpinBox * m_mixerChannelNumber; EffectRackView * m_effectRack; diff --git a/include/TrackOperationsWidget.h b/include/TrackOperationsWidget.h index 8417298b4..3eb9215b9 100644 --- a/include/TrackOperationsWidget.h +++ b/include/TrackOperationsWidget.h @@ -26,6 +26,7 @@ #define LMMS_GUI_TRACK_OPERATIONS_WIDGET_H #include +#include "AutomatableButton.h" class QPushButton; @@ -69,8 +70,8 @@ private: TrackGrip* m_trackGrip; QPushButton * m_trackOps; - PixmapButton * m_muteBtn; - PixmapButton * m_soloBtn; + AutomatableButton* m_muteBtn; + AutomatableButton* m_soloBtn; friend class TrackView; diff --git a/plugins/CrossoverEQ/CrossoverEQControlDialog.cpp b/plugins/CrossoverEQ/CrossoverEQControlDialog.cpp index 93aaf0cff..c3a61890d 100644 --- a/plugins/CrossoverEQ/CrossoverEQControlDialog.cpp +++ b/plugins/CrossoverEQ/CrossoverEQControlDialog.cpp @@ -56,9 +56,9 @@ CrossoverEQControlDialog::CrossoverEQControlDialog(CrossoverEQControls *controls layout->addLayout(knobsLayout); const auto makeKnob = [this, knobsLayout]( - FloatModel *model, - const QString &label, - const QString &txt_before + FloatModel* model, + const QString& label, + const QString& txt_before ) { auto k = new Knob(KnobType::Bright26, this); k->setModel(model); @@ -76,8 +76,8 @@ CrossoverEQControlDialog::CrossoverEQControlDialog(CrossoverEQControls *controls layout->addLayout(bandsLayout); const auto makeFader = [this, bandsLayout]( - FloatModel *model, - const QString &label, + FloatModel* model, + const QString& label, int column ) { auto f = new Fader(model, label, this, false); @@ -92,20 +92,16 @@ CrossoverEQControlDialog::CrossoverEQControlDialog(CrossoverEQControls *controls makeFader(&controls->m_gain3, tr("Band 3 gain"), 2); makeFader(&controls->m_gain4, tr("Band 4 gain"), 3); - const auto muteOn = embed::getIconPixmap("mute_active"); - const auto muteOff = embed::getIconPixmap("mute_inactive"); - - const auto makeMuteBtn = [this, bandsLayout, muteOn, muteOff]( - BoolModel *model, - const QString &label, + const auto makeMuteBtn = [this, bandsLayout]( + BoolModel* model, + const QString& label, int column ) { - auto b = new PixmapButton(this, label); - b->setActiveGraphic(muteOff); - b->setInactiveGraphic(muteOn); + auto b = new AutomatableButton(this, label); b->setCheckable(true); b->setModel(model); b->setToolTip(label); + b->setObjectName("btn-mute-inv"); bandsLayout->addWidget(b, 1, column, Qt::AlignCenter); }; diff --git a/src/gui/MixerChannelView.cpp b/src/gui/MixerChannelView.cpp index 1eb2fd1bb..ccfb5c75c 100644 --- a/src/gui/MixerChannelView.cpp +++ b/src/gui/MixerChannelView.cpp @@ -107,18 +107,16 @@ MixerChannelView::MixerChannelView(QWidget* parent, MixerView* mixerView, int ch renameLineEditProxy->setRotation(-90); m_renameLineEditView->setFixedSize(m_renameLineEdit->height() + 5, m_renameLineEdit->width() + 5); - m_muteButton = new PixmapButton(this, tr("Mute")); + m_muteButton = new AutomatableButton(this, tr("Mute")); m_muteButton->setModel(&mixerChannel->m_muteModel); - m_muteButton->setActiveGraphic(embed::getIconPixmap("mute_active")); - m_muteButton->setInactiveGraphic(embed::getIconPixmap("mute_inactive")); m_muteButton->setCheckable(true); + m_muteButton->setObjectName("btn-mute"); m_muteButton->setToolTip(tr("Mute this channel")); - m_soloButton = new PixmapButton(this, tr("Solo")); + m_soloButton = new AutomatableButton(this, tr("Solo")); m_soloButton->setModel(&mixerChannel->m_soloModel); - m_soloButton->setActiveGraphic(embed::getIconPixmap("solo_active")); - m_soloButton->setInactiveGraphic(embed::getIconPixmap("solo_inactive")); m_soloButton->setCheckable(true); + m_soloButton->setObjectName("btn-solo"); m_soloButton->setToolTip(tr("Solo this channel")); auto soloMuteLayout = new QVBoxLayout(); diff --git a/src/gui/SampleTrackWindow.cpp b/src/gui/SampleTrackWindow.cpp index e8bd5970d..eb09a2097 100644 --- a/src/gui/SampleTrackWindow.cpp +++ b/src/gui/SampleTrackWindow.cpp @@ -32,7 +32,6 @@ #include #include "EffectRackView.h" -#include "PixmapButton.h" #include "embed.h" #include "GuiApplication.h" #include "Knob.h" @@ -101,18 +100,16 @@ SampleTrackWindow::SampleTrackWindow(SampleTrackView * tv) : soloMuteLayout->setContentsMargins(0, 0, 2, 0); soloMuteLayout->setSpacing(2); - m_muteBtn = new PixmapButton(this, tr("Mute")); + m_muteBtn = new AutomatableButton(this, tr("Mute")); m_muteBtn->setModel(&m_track->m_mutedModel); - m_muteBtn->setActiveGraphic(embed::getIconPixmap("mute_active")); - m_muteBtn->setInactiveGraphic(embed::getIconPixmap("mute_inactive")); + m_muteBtn->setObjectName("btn-mute"); m_muteBtn->setCheckable(true); m_muteBtn->setToolTip(tr("Mute this sample track")); soloMuteLayout->addWidget(m_muteBtn, 0, widgetAlignment); - m_soloBtn = new PixmapButton(this, tr("Solo")); + m_soloBtn = new AutomatableButton(this, tr("Solo")); m_soloBtn->setModel(&m_track->m_soloModel); - m_soloBtn->setActiveGraphic(embed::getIconPixmap("solo_active")); - m_soloBtn->setInactiveGraphic(embed::getIconPixmap("solo_inactive")); + m_soloBtn->setObjectName("btn-solo"); m_soloBtn->setCheckable(true); m_soloBtn->setToolTip(tr("Solo this sample track")); soloMuteLayout->addWidget(m_soloBtn, 0, widgetAlignment); diff --git a/src/gui/instrument/InstrumentTrackWindow.cpp b/src/gui/instrument/InstrumentTrackWindow.cpp index d6c32a205..0446e857e 100644 --- a/src/gui/instrument/InstrumentTrackWindow.cpp +++ b/src/gui/instrument/InstrumentTrackWindow.cpp @@ -144,19 +144,17 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) : soloMuteLayout->setContentsMargins(0, 0, 2, 0); soloMuteLayout->setSpacing(2); - m_muteBtn = new PixmapButton(this, tr("Mute")); + m_muteBtn = new AutomatableButton(this, tr("Mute")); m_muteBtn->setModel(&m_track->m_mutedModel); - m_muteBtn->setActiveGraphic(embed::getIconPixmap("mute_active")); - m_muteBtn->setInactiveGraphic(embed::getIconPixmap("mute_inactive")); m_muteBtn->setCheckable(true); + m_muteBtn->setObjectName("btn-mute"); m_muteBtn->setToolTip(tr("Mute this instrument")); soloMuteLayout->addWidget(m_muteBtn, 0, widgetAlignment); - m_soloBtn = new PixmapButton(this, tr("Solo")); + m_soloBtn = new AutomatableButton(this, tr("Solo")); m_soloBtn->setModel(&m_track->m_soloModel); - m_soloBtn->setActiveGraphic(embed::getIconPixmap("solo_active")); - m_soloBtn->setInactiveGraphic(embed::getIconPixmap("solo_inactive")); m_soloBtn->setCheckable(true); + m_soloBtn->setObjectName("btn-solo"); m_soloBtn->setToolTip(tr("Solo this instrument")); soloMuteLayout->addWidget(m_soloBtn, 0, widgetAlignment); diff --git a/src/gui/tracks/TrackOperationsWidget.cpp b/src/gui/tracks/TrackOperationsWidget.cpp index c43cd022c..238ef8955 100644 --- a/src/gui/tracks/TrackOperationsWidget.cpp +++ b/src/gui/tracks/TrackOperationsWidget.cpp @@ -83,7 +83,7 @@ TrackOperationsWidget::TrackOperationsWidget( TrackView * parent ) : // buttons in a layout. auto operationsWidget = new QWidget(this); auto operationsLayout = new QHBoxLayout(operationsWidget); - operationsLayout->setContentsMargins(0, 3, 0, 0); + operationsLayout->setContentsMargins(2, 6, 0, 6); operationsLayout->setSpacing(2); m_trackOps = new QPushButton(operationsWidget); @@ -91,44 +91,18 @@ TrackOperationsWidget::TrackOperationsWidget( TrackView * parent ) : m_trackOps->setMenu( toMenu ); m_trackOps->setToolTip(tr("Actions")); - // This helper lambda wraps a PixmapButton in a QWidget. This is necessary due to some strange effect where the - // PixmapButtons are resized to a size that's larger than their minimum/fixed size when the method "show" is called - // in "TrackContainerView::realignTracks". Specifically, with the default theme the buttons are resized from - // (16, 14) to (26, 26). This then makes them behave not as expected in layouts. - // The resizing is not done for QWidgets. Therefore we wrap the PixmapButton in a QWidget which is set to a - // fixed size that will be able to show the active and inactive pixmap. We can then use the QWidget in layouts - // without any disturbances. - // - // The resizing only seems to affect the track view hierarchy and is triggered by Qt's internal mechanisms. - // For example the buttons in the mixer view do not seem to be affected. - // If you want to debug this simply override "PixmapButton::resizeEvent" and trigger a break point in there. - auto buildPixmapButtonWrappedInWidget = [](QWidget* parent, const QString& toolTip, - std::string_view activeGraphic, std::string_view inactiveGraphic, PixmapButton*& pixmapButton) - { - const auto activePixmap = embed::getIconPixmap(activeGraphic); - const auto inactivePixmap = embed::getIconPixmap(inactiveGraphic); - - auto wrapperWidget = new QWidget(parent); - - auto button = new PixmapButton(wrapperWidget, toolTip); - button->setCheckable(true); - button->setActiveGraphic(activePixmap); - button->setInactiveGraphic(inactivePixmap); - button->setToolTip(toolTip); - - wrapperWidget->setFixedSize(button->minimumSizeHint()); - - pixmapButton = button; - - return wrapperWidget; - }; - - auto muteWidget = buildPixmapButtonWrappedInWidget(operationsWidget, tr("Mute"), "mute_active", "mute_inactive", m_muteBtn); - auto soloWidget = buildPixmapButtonWrappedInWidget(operationsWidget, tr("Solo"), "solo_active", "solo_inactive", m_soloBtn); + m_muteBtn = new AutomatableButton(operationsWidget, tr("Mute")); + m_muteBtn->setCheckable(true); + m_muteBtn->setToolTip(tr("Mute")); + m_muteBtn->setObjectName("btn-mute"); + m_soloBtn = new AutomatableButton(operationsWidget, tr("Solo")); + m_soloBtn->setCheckable(true); + m_soloBtn->setToolTip(tr("Solo")); + m_soloBtn->setObjectName("btn-solo"); operationsLayout->addWidget(m_trackOps); - operationsLayout->addWidget(muteWidget); - operationsLayout->addWidget(soloWidget); + operationsLayout->addWidget(m_muteBtn); + operationsLayout->addWidget(m_soloBtn); layout->addWidget(operationsWidget, 0, Qt::AlignTop | Qt::AlignLeading); From 16296c1dfb85b34b42df3625b6d806a23fa10725 Mon Sep 17 00:00:00 2001 From: Dalton Messmer Date: Wed, 26 Mar 2025 21:31:38 -0400 Subject: [PATCH 20/62] Fix Vestige file browser filter (#7816) - Remove pointless *.exe option - Add an "All VST files" option on Linux when there is both LinuxVST and Wine VST support - Remove *.dll option from Linux builds without Wine VST support (i.e. LinuxVST-only builds) --- plugins/Vestige/Vestige.cpp | 26 +++++++++++++++++--------- src/gui/FileBrowser.cpp | 14 ++++++++++---- src/lmmsconfig.h.in | 2 ++ 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/plugins/Vestige/Vestige.cpp b/plugins/Vestige/Vestige.cpp index 05716008e..e37292400 100644 --- a/plugins/Vestige/Vestige.cpp +++ b/plugins/Vestige/Vestige.cpp @@ -79,10 +79,14 @@ Plugin::Descriptor Q_DECL_EXPORT vestige_plugin_descriptor = 0x0100, Plugin::Type::Instrument, new PluginPixmapLoader( "logo" ), -#ifdef LMMS_BUILD_LINUX - "dll,so", -#else +#if defined(LMMS_BUILD_WIN32) "dll", +#elif defined(LMMS_BUILD_LINUX) +# if defined(LMMS_HAVE_VST_32) || defined(LMMS_HAVE_VST_64) + "dll,so", +# else + "so", +# endif #endif nullptr, } ; @@ -669,13 +673,17 @@ void VestigeInstrumentView::openPlugin() // set filters QStringList types; - types << tr( "DLL-files (*.dll)" ) - << tr( "EXE-files (*.exe)" ) -#ifdef LMMS_BUILD_LINUX - << tr( "SO-files (*.so)" ) +#if defined(LMMS_BUILD_WIN32) + types << tr("VST2 files (*.dll)"); +#elif defined(LMMS_BUILD_LINUX) +# if defined(LMMS_HAVE_VST_32) || defined(LMMS_HAVE_VST_64) + types << tr("All VST files (*.dll *.so)") + << tr("Windows VST2 files (*.dll)"); +# endif + types << tr("LinuxVST files (*.so)"); #endif - ; - ofd.setNameFilters( types ); + + ofd.setNameFilters(types); if( m_vi->m_pluginDLL != "" ) { diff --git a/src/gui/FileBrowser.cpp b/src/gui/FileBrowser.cpp index 5e8c84e33..3ba634176 100644 --- a/src/gui/FileBrowser.cpp +++ b/src/gui/FileBrowser.cpp @@ -1222,15 +1222,21 @@ void FileItem::determineFileType() m_type = FileType::Midi; m_handling = FileHandling::ImportAsProject; } - else if( ext == "dll" -#ifdef LMMS_BUILD_LINUX - || ext == "so" -#endif +#ifdef LMMS_HAVE_VST + else if ( +# if defined(LMMS_BUILD_LINUX) + ext == "so" || +# endif +# if defined(LMMS_HAVE_VST_32) || defined(LMMS_HAVE_VST_64) + ext == "dll" || +# endif + false ) { m_type = FileType::VstPlugin; m_handling = FileHandling::LoadByPlugin; } +#endif else if ( ext == "lv2" ) { m_type = FileType::Preset; diff --git a/src/lmmsconfig.h.in b/src/lmmsconfig.h.in index 867a04a45..e63ce1fa4 100644 --- a/src/lmmsconfig.h.in +++ b/src/lmmsconfig.h.in @@ -33,6 +33,8 @@ #cmakedefine LMMS_HAVE_SDL #cmakedefine LMMS_HAVE_STK #cmakedefine LMMS_HAVE_VST +#cmakedefine LMMS_HAVE_VST_32 +#cmakedefine LMMS_HAVE_VST_64 #cmakedefine LMMS_HAVE_SF_COMPLEVEL #cmakedefine LMMS_DEBUG_FPE From 9cb3ae7de10f7c99f48f7a7ee5d0597630d26815 Mon Sep 17 00:00:00 2001 From: SpomJ <75751809+SpomJ@users.noreply.github.com> Date: Mon, 31 Mar 2025 17:36:29 +0300 Subject: [PATCH 21/62] Add build flags to output of lmms --version (#7780) --- src/CMakeLists.txt | 12 ++++++++++++ src/core/main.cpp | 7 ++++--- src/lmmsversion.h.in | 1 + 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9612190bf..eb69fd1a2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,18 @@ ADD_SUBDIRECTORY(3rdparty) CONFIGURE_FILE("lmmsconfig.h.in" "${CMAKE_BINARY_DIR}/lmmsconfig.h") + +# Provide config flags to lmmsversion.h +get_cmake_property(_define_vars VARIABLES) +foreach (_define_var ${_define_vars}) + if(_define_var MATCHES "^WANT|LMMS_(HAVE|DEBUG)" ) + list(APPEND LMMS_BUILD_OPTIONS "${_define_var}='${${_define_var}}'") + endif() +endforeach() + +# Format for readibility +string(REPLACE ";" " " LMMS_BUILD_OPTIONS "${LMMS_BUILD_OPTIONS}") + CONFIGURE_FILE("lmmsversion.h.in" "${CMAKE_BINARY_DIR}/lmmsversion.h") SET(LMMS_SRCS "") diff --git a/src/core/main.cpp b/src/core/main.cpp index 995b49d2c..c75e91ccd 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -140,15 +140,16 @@ inline void loadTranslation( const QString & tname, void printVersion( char *executableName ) { - printf( "LMMS %s\n(%s %s, Qt %s, %s)\n\n" + printf("LMMS %s\n(%s %s, Qt %s, %s)\n\n" + "Build options:\n%s\n\n" "Copyright (c) %s\n\n" "This program is free software; you can redistribute it and/or\n" "modify it under the terms of the GNU General Public\n" "License as published by the Free Software Foundation; either\n" "version 2 of the License, or (at your option) any later version.\n\n" "Try \"%s --help\" for more information.\n\n", LMMS_VERSION, - LMMS_BUILDCONF_PLATFORM, LMMS_BUILDCONF_MACHINE, QT_VERSION_STR, LMMS_BUILDCONF_COMPILER_VERSION, - LMMS_PROJECT_COPYRIGHT, executableName ); + LMMS_BUILDCONF_PLATFORM, LMMS_BUILDCONF_MACHINE, QT_VERSION_STR, LMMS_BUILDCONF_COMPILER_VERSION, LMMS_BUILD_OPTIONS, + LMMS_PROJECT_COPYRIGHT, executableName); } diff --git a/src/lmmsversion.h.in b/src/lmmsversion.h.in index dc7ae84d0..47bdba52a 100644 --- a/src/lmmsversion.h.in +++ b/src/lmmsversion.h.in @@ -1,2 +1,3 @@ #define LMMS_VERSION "@VERSION@" #define LMMS_PROJECT_COPYRIGHT "@PROJECT_COPYRIGHT@" +#define LMMS_BUILD_OPTIONS "@LMMS_BUILD_OPTIONS@" From 2482842daabea9130d60f9278b123f0b3b3dc5fb Mon Sep 17 00:00:00 2001 From: qnebra Date: Wed, 2 Apr 2025 21:16:59 +0200 Subject: [PATCH 22/62] Vestige and VST Effect assets replacement (#7791) Replace VST instrument and effect assets with SVG --- plugins/Vestige/CMakeLists.txt | 2 +- plugins/Vestige/artwork.png | Bin 39412 -> 0 bytes plugins/Vestige/artwork.svg | 105 +++++++++++++++++++++++++ plugins/Vestige/controls.png | Bin 841 -> 0 bytes plugins/Vestige/controls.svg | 23 ++++++ plugins/Vestige/controls_active.png | Bin 824 -> 0 bytes plugins/Vestige/controls_active.svg | 23 ++++++ plugins/Vestige/logo-symbolic.svg | 15 ++++ plugins/Vestige/logo.png | Bin 1533 -> 0 bytes plugins/Vestige/logo.svg | 38 +++++++++ plugins/Vestige/select_file.png | Bin 1143 -> 0 bytes plugins/Vestige/select_file.svg | 25 ++++++ plugins/Vestige/select_file_active.png | Bin 1411 -> 0 bytes plugins/Vestige/select_file_active.svg | 25 ++++++ plugins/VstEffect/CMakeLists.txt | 2 +- plugins/VstEffect/controls.png | Bin 841 -> 0 bytes plugins/VstEffect/controls.svg | 23 ++++++ plugins/VstEffect/controls_active.png | Bin 824 -> 0 bytes plugins/VstEffect/controls_active.svg | 23 ++++++ plugins/VstEffect/logo.png | Bin 585 -> 0 bytes plugins/VstEffect/logo.svg | 15 ++++ 21 files changed, 317 insertions(+), 2 deletions(-) delete mode 100644 plugins/Vestige/artwork.png create mode 100644 plugins/Vestige/artwork.svg delete mode 100644 plugins/Vestige/controls.png create mode 100644 plugins/Vestige/controls.svg delete mode 100644 plugins/Vestige/controls_active.png create mode 100644 plugins/Vestige/controls_active.svg create mode 100644 plugins/Vestige/logo-symbolic.svg delete mode 100644 plugins/Vestige/logo.png create mode 100644 plugins/Vestige/logo.svg delete mode 100644 plugins/Vestige/select_file.png create mode 100644 plugins/Vestige/select_file.svg delete mode 100644 plugins/Vestige/select_file_active.png create mode 100644 plugins/Vestige/select_file_active.svg delete mode 100644 plugins/VstEffect/controls.png create mode 100644 plugins/VstEffect/controls.svg delete mode 100644 plugins/VstEffect/controls_active.png create mode 100644 plugins/VstEffect/controls_active.svg delete mode 100644 plugins/VstEffect/logo.png create mode 100644 plugins/VstEffect/logo.svg diff --git a/plugins/Vestige/CMakeLists.txt b/plugins/Vestige/CMakeLists.txt index 0a5847889..874e8f61d 100644 --- a/plugins/Vestige/CMakeLists.txt +++ b/plugins/Vestige/CMakeLists.txt @@ -11,5 +11,5 @@ if(LMMS_BUILD_LINUX) else() set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${PLUGIN_DIR}") endif() -build_plugin(vestige Vestige.cpp Vestige.h MOCFILES Vestige.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") +build_plugin(vestige Vestige.cpp Vestige.h MOCFILES Vestige.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png;*.svg") target_link_libraries(vestige vstbase) diff --git a/plugins/Vestige/artwork.png b/plugins/Vestige/artwork.png deleted file mode 100644 index cd08153eb52c0228c60e96d5cc94b4a32aee543b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39412 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 ZITlL%y>D^sCvSuc3DG<6Vqx4CN` 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 QCIA2c07*qoM6N<$g8T@~1poj5 diff --git a/plugins/Vestige/artwork.svg b/plugins/Vestige/artwork.svg new file mode 100644 index 000000000..47e0275c3 --- /dev/null +++ b/plugins/Vestige/artwork.svg @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/Vestige/controls.png b/plugins/Vestige/controls.png deleted file mode 100644 index 4e03eb35112b855031479a20e45d7a0e6fdc2d55..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 841 zcmV-P1GfB$P)(*1K~zYI?UqewBt;a*f3LfGrdtD%)p z$-#pWJfsI@V3qai*(@GpvowR`5<)IHxx#`dS#c5ruHq$T$kh)B0|5oeOiy>!^N{p7 zZpI-j>>if=Lmj#wuYUEus1jA>VaR(Zl*gdQpkbNek0eQ60qP?167Z*pe7?NAeB&Y; zHJi=R>+7#sYrl5R zVXehE2cW>sIF1{DdS|Cu5s?o>M1mk73`5H0GEo%adESQ?2|3roA*zBD~O{gyEXW6b3C_O|-IkMH|<-f907X|mQbIXOv|Wq6)PyWMU9D?=cK zv#wLsR8@P$O4IZk-}f=b5Cj3n7)qrQVHgrc5k;CxrP9D|jKTMPvMjqZyqBt`1JJ#y zR4Tt+pe`ddF)=aH*Npl(J3D*XTFYrY&*E3bT01gzd7hK!Io4WcW@erT&UNWXP!I&~ z^!t6BbDW%<{Jpriczs+Ho12?g!!WGmc}}@pejE6BB&gYJuBhs@Vt$MKRnD^T?AqFz=6Q~)qN+FQ^?Jhth~xOHgM)*Xb564?qu1+^rYYTS zm%Y8c+xHar+}hpUz18h@2Yy+W;hfXM!^7_zjmB;Gca8*jy;iHeS*z8qRIAlz`u%=y ze}Dg{jg5_uRP~o}5sAp9g@uJr=H}*J2*dEnPN(zh&d$!=R;%?T@P(>68NSLzC9 zFb(v8pH=m{agZV37lF&b6Tt7lkE(h&u#b8(h=}NYJ|_OpcO>XR=+V3Q9zD=Mt+RG? T>c!$x00000NkvXXu0mjf?#z$! diff --git a/plugins/Vestige/controls.svg b/plugins/Vestige/controls.svg new file mode 100644 index 000000000..dc900a770 --- /dev/null +++ b/plugins/Vestige/controls.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/Vestige/controls_active.png b/plugins/Vestige/controls_active.png deleted file mode 100644 index 5759cd7bbb449c4a99c32495fbf41e7ff3b3a955..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 824 zcmV-81IPS{P){ipR;gl8$i|RE@Zfc$m-ONxl-*`#o`+_`+Qw7_ zdnkP{huOF9{pP(lZ)O+~;V6V33FQQI0*Z?npH!>Wt6J;(jIk?>v7bcr+I8KPzj5Sw z-dz9>0Gt8vNo)OTdV2apq-Rkdd7k$IKmh<_jKMGraL&Ow&ySCfuMdJW8jWjO>$gfN zD5cQ#NdQ(|*IfWWybD5kkPSti9C&4?WL& z*-Kq6m)ii)jtsZ9w(h0V>9eM3!Z3^rhk=sGt=B z#`C<9Y&Lt#G)>sHeR_L)n+(H%VHnt3C_qGrQk6=jAf*Ij43$cy3}871BA~nLh$tkY zAR0jsyyKiBa&B1`5{U$a5U_0Pt$f&iPFo27llJuph8(t2bQ zh9Nqg4z$)(tJOZw%*>R4?#a+?uIuyj^EdMO{8T2B$%J7Tl*{GX($dlsBKkTgBF5NA zp-^}>H#g@xj^p^g?-z^3>e|}c27n?FX%@fA8Dr-GOaVv(2msWH=;I(r%=R*XNdTt+ zd7R2#qm0000 + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/Vestige/logo-symbolic.svg b/plugins/Vestige/logo-symbolic.svg new file mode 100644 index 000000000..136c204e8 --- /dev/null +++ b/plugins/Vestige/logo-symbolic.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/plugins/Vestige/logo.png b/plugins/Vestige/logo.png deleted file mode 100644 index b8aa149b054af59e2733b6ebe3965ee22855496e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1533 zcmVT zK~!ko?U&n+T~!^&KfkrF=gc{Crqj{Vk%WS#kpM}d8d9v$#F$`WtReV7Xd6l3O~PN$ zr{T$4=!-GYs6a_cG&N2e^uYv3)F4tX0g?0p2JBd;)6SW5&OVoYS?lM+J~QpyppCw{ zKgsW5U$V~kyVmcr4^&iCR8&+{R8&+{{J$a}_HfQfZNDFVe@_rUs8NUuBZwo2W26=% zwK}F=!_;b+IKpK)ZaBd82k3Cb(!zT@va)>p+InA_d+0l#-hb!Ajj&G5kFn|)7DIYj zLXsyWd5p*Hw5!VtOHWl~G;ys- zWU2*IEzGVdOlu0$Y+#}Y&2wxo!L4?1omHYv2UeEX*W5ibGqdkgpWHVUHE9HO3N1*o zKAo&HjyBz_O)vLJ3L)N=o(ckcbdmPin=)_$!b45(M~B5f9=i;iCZG-qi{v>v9HHGF z?#fkM977m@G2j&P97ZV&2apUlth=ys5r6q@A}_=@9)e=EAaf%!*C(|Jsq2zy7nK1s zx0uQ4M(gzC2|50cxy3U#WZ=6sYWK9fXC4TH<8dyKDSkG>>*v_P0Nd?iqX-QnGz{pe zLybX2KoxNgiUKJL(r#zN+83_8%jHWczVY!yFrpZ7h~#)C$MbR`zo6zjCPGI&P-;zY zVo$U2+{Dwf`5Uhfx1^K5dFSuD)jRpwhW~h6D5Ss;FoL8OKePCw6zvKs1_VwQ{hwXJ zJ$oYsH^@hj_Mlu6-zb5nL>|O}5{M)6lzQN3gvw;3G$Kd6WhQ1P>n}7S9(`qgV>Zcik}=N&O3l zC3iwvD8pRndF78~*3ljsoWd{{7B7w7UTxbq z*Hh@vf6;UPq#iwVo4nYX)j9~ALY*k=yQ^DR&6 zq*A7j-R7J>iuv|yBigrbz8!vR-%rE6&2O%YN3c#Qq!cd#CLfpGSp2Xk>%kLPUKpHz z=X|m*eoGsD@FmX7uWEk(UFOuZWs*e7ddSAL;I;3=LcorUC&@BOuRWaTH<#BV#u{`3dVAuTg*t zEACuYFCK1UKW7WD4y-MY_(nV=o8Bsx`oBv07vJCc<6g!SS`bu=)%1t<7gv_5e?1iy j6%`c~6%`c~72EM2nqk2YzJ=1600000NkvXXu0mjfc+J}4 diff --git a/plugins/Vestige/logo.svg b/plugins/Vestige/logo.svg new file mode 100644 index 000000000..01d4c0a28 --- /dev/null +++ b/plugins/Vestige/logo.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/Vestige/select_file.png b/plugins/Vestige/select_file.png deleted file mode 100644 index 63af460f6f6d5abfa970518657501693eac35f30..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1143 zcmV--1c>{IP)zPO+r000CQNkl1v7&Ye5I zNz;ibD#mJPY5f6Puobsb7gBZQ!kv3Jx)Y%wg5bIfK?E1N6A>w`1gezMA1qQUwA$LH zNi%JlNlj)lGtJyP_x!rZO^9iPF2oOBI9#~j%X{AUd>{NzCH})cn|p;E0H}v-AUB*h zLQXupyHu?YReG&%1Qc>a`9EufME}OH@4!J-C=@DJF8%7t)oN5Kl~f2ptg$!e=M5l7 zKB#q`AA62&@&fIC2c2c~|4R7%$dz%R2PA-9Cwk?z*Tt#H6Khh15TaB(Sn9p>{>w-! z$jkt149+-2rWj9mvRrI&c<&*8?wn;%b%^1Zx+>6L?g2aj%mEs3>qI%p`n5D>X5_@% z6MV7%bDEtg+Nyz830_8I1Tyak2+p_8V5BDthQ94n4{b~m13Ut>b9+Pt@Zph<#M$%b zD$A{wxHEezSm&bl(qd`X@Gh)NxS3pHky+AUNQEU0hQw4)Qu{cpbb*Z1#ywqt7UGD({9)xAlo+xqOSjFVtUg2qct zr1}uL;0$RJXf&I|g9=W7)-d8m9thV)RuTS?r!F)v!)S{R4Q$AmcQh#sQ7E4Ko zvD$WQ!QqN442KxQ04p?B8Laep0zL#h77>F$;EbYFE*s}u7f1oSS&;{Hb+&fwyNf4j zucVBWM{t#lvXsP1;NuJ{4JK%;3wXbZK%iI_XfnKskV5KwB0QjIt?b=fa~B$S=9aR4 znOd!c5q(AqMVuO7!hp8n#`_U$~- z?#^`Tqw*Of3MC~{h}EgFzCi*ZKnMZvJ;KA}(G%|h%{&OM*XeWsvDM;JKp7YV9tY|` zym_Q-Q6W!Q4_L@?KmWERk;t#K0u1M*>LItW0|q%kA27Kc{0)rrMK96$FdhH^002ov JPDHLkV1im42h;!n diff --git a/plugins/Vestige/select_file.svg b/plugins/Vestige/select_file.svg new file mode 100644 index 000000000..1af383dc3 --- /dev/null +++ b/plugins/Vestige/select_file.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/Vestige/select_file_active.png b/plugins/Vestige/select_file_active.png deleted file mode 100644 index 029e58cfc3268fd4c258e7744e1ab5f7630c6f65..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1411 zcmV-}1$_F6P)%Wqv*6~=#SpL6cR zj<0i_IBEJKv{I!u0}vpv5P_m*1T!T63;Ydqb`hxKGJpv%V^j;INR?nv=mSxShmfd9 zNb?}Uj^i{p_Psv$y!LZ3a9oN=N9^IZ_gbGmt-Zca_Y;JDK-KWV1YY+Q_(I8V*6N_3pfEJQZB0Vv(B1=OVtfj!H^$j6f+PVvI;mf9U zQ}fUH{MN?I@_kbN7$w{tkuS~L@oIkN$q?J6%t&8-mJgB_PeP62)FY13(-)t4<4dD2 zFt>9a*csM0lm`pm``KGxZ=37}Kj#2&$3*hA@4X`VAj)uUXnZpp4tt{~pZ)Clzh<9P zvLv*j!AD2*9`OaHD|pbq&v(v!n?JO_r5!|A>a#9=%JL4eiO+>a_i)@BFJ{bq4#>-s zm(9w#rMzmZURnEouZSy2?Ahk2lGmR4A*FplB_9z@N0JPO;x(bb`^~#JNwhf1SaQbP z$8`fz7fx#zm!p*okbE+k@Q;(%3bz~14hFI|7<4PfWUHpH&zj|oq@F$hAv5mKs-u-a ztByu3HJ(~KEQutJ3)z$vb%;HM@f4a&rbz`Le}{KWoW|mO{=$oAzP$A7H1dcDqC#A; z?C0Cu9{!OQPm?T-Ix2P4YALltX*m;?d4U5=bcB>RY1c6^Gg-_?Bmi=NVpy2{{O;78 zc&7aE7hfA4G&{Ip`DOJR`n`_l^Ye6gTD7P+GFbozppGuUc6F0pSS1SB0H@+9a?Qzz z_(w!${mQzsAevx;ewhBg-R?ktPR%Y}Ewgl+L)~MpJ0#iRwAo=_}cp8E_9F~A0#*h~dFD(GpDnI(gt3L@o%%k(QszyK!pyNy97(!?c-Ql)g*DHHX z$vn(i>x~H#iRO?K3?XkC&2>3{YwDTI00Et0yKU0qK!Jo1(!ROY#N% zd`XfLC6fe456rRQk1vg5*LKacdaxaD>6M%1eX=Z&unZaEsc^J-EJ2m1iKL2ZL`f(S zB%ywYrYwny4h7+*Ba8dkRJX2f>BZ4S>*nr&J$d`e?Ali@--8=|N|)|oH4<f8IDj0t(}yO;9QO?h%K9De?ljc@&G zapLeqW->BzGLw-Bh?qy&B7mX_s)?8qlHjMWzy5>nq5r_o{RhVO?ya00+uiMdrT@yK z$z&pz|8{w3Q(6zsKJT6XObUKYQz}$29M9vEMTw(@jT%B1YFoPdp>nr_XQw@A4}tKw z;FFDw4bv3OV9_mBnz9)ObXlVsqbg*dlEx8IN~rTL#!xw{vu@T^fP3`o_&59#P$Arh R + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/VstEffect/CMakeLists.txt b/plugins/VstEffect/CMakeLists.txt index 3cbe8e8cc..7bb42dc24 100644 --- a/plugins/VstEffect/CMakeLists.txt +++ b/plugins/VstEffect/CMakeLists.txt @@ -13,5 +13,5 @@ ELSE() SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${PLUGIN_DIR}") ENDIF() -BUILD_PLUGIN(vsteffect VstEffect.cpp VstEffectControls.cpp VstEffectControlDialog.cpp VstSubPluginFeatures.cpp VstEffect.h VstEffectControls.h VstEffectControlDialog.h VstSubPluginFeatures.h MOCFILES VstEffectControlDialog.h VstEffectControls.h EMBEDDED_RESOURCES *.png) +BUILD_PLUGIN(vsteffect VstEffect.cpp VstEffectControls.cpp VstEffectControlDialog.cpp VstSubPluginFeatures.cpp VstEffect.h VstEffectControls.h VstEffectControlDialog.h VstSubPluginFeatures.h MOCFILES VstEffectControlDialog.h VstEffectControls.h EMBEDDED_RESOURCES *.png *.svg) TARGET_LINK_LIBRARIES(vsteffect vstbase) diff --git a/plugins/VstEffect/controls.png b/plugins/VstEffect/controls.png deleted file mode 100644 index 4e03eb35112b855031479a20e45d7a0e6fdc2d55..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 841 zcmV-P1GfB$P)(*1K~zYI?UqewBt;a*f3LfGrdtD%)p z$-#pWJfsI@V3qai*(@GpvowR`5<)IHxx#`dS#c5ruHq$T$kh)B0|5oeOiy>!^N{p7 zZpI-j>>if=Lmj#wuYUEus1jA>VaR(Zl*gdQpkbNek0eQ60qP?167Z*pe7?NAeB&Y; zHJi=R>+7#sYrl5R zVXehE2cW>sIF1{DdS|Cu5s?o>M1mk73`5H0GEo%adESQ?2|3roA*zBD~O{gyEXW6b3C_O|-IkMH|<-f907X|mQbIXOv|Wq6)PyWMU9D?=cK zv#wLsR8@P$O4IZk-}f=b5Cj3n7)qrQVHgrc5k;CxrP9D|jKTMPvMjqZyqBt`1JJ#y zR4Tt+pe`ddF)=aH*Npl(J3D*XTFYrY&*E3bT01gzd7hK!Io4WcW@erT&UNWXP!I&~ z^!t6BbDW%<{Jpriczs+Ho12?g!!WGmc}}@pejE6BB&gYJuBhs@Vt$MKRnD^T?AqFz=6Q~)qN+FQ^?Jhth~xOHgM)*Xb564?qu1+^rYYTS zm%Y8c+xHar+}hpUz18h@2Yy+W;hfXM!^7_zjmB;Gca8*jy;iHeS*z8qRIAlz`u%=y ze}Dg{jg5_uRP~o}5sAp9g@uJr=H}*J2*dEnPN(zh&d$!=R;%?T@P(>68NSLzC9 zFb(v8pH=m{agZV37lF&b6Tt7lkE(h&u#b8(h=}NYJ|_OpcO>XR=+V3Q9zD=Mt+RG? T>c!$x00000NkvXXu0mjf?#z$! diff --git a/plugins/VstEffect/controls.svg b/plugins/VstEffect/controls.svg new file mode 100644 index 000000000..dc900a770 --- /dev/null +++ b/plugins/VstEffect/controls.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/VstEffect/controls_active.png b/plugins/VstEffect/controls_active.png deleted file mode 100644 index 5759cd7bbb449c4a99c32495fbf41e7ff3b3a955..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 824 zcmV-81IPS{P){ipR;gl8$i|RE@Zfc$m-ONxl-*`#o`+_`+Qw7_ zdnkP{huOF9{pP(lZ)O+~;V6V33FQQI0*Z?npH!>Wt6J;(jIk?>v7bcr+I8KPzj5Sw z-dz9>0Gt8vNo)OTdV2apq-Rkdd7k$IKmh<_jKMGraL&Ow&ySCfuMdJW8jWjO>$gfN zD5cQ#NdQ(|*IfWWybD5kkPSti9C&4?WL& z*-Kq6m)ii)jtsZ9w(h0V>9eM3!Z3^rhk=sGt=B z#`C<9Y&Lt#G)>sHeR_L)n+(H%VHnt3C_qGrQk6=jAf*Ij43$cy3}871BA~nLh$tkY zAR0jsyyKiBa&B1`5{U$a5U_0Pt$f&iPFo27llJuph8(t2bQ zh9Nqg4z$)(tJOZw%*>R4?#a+?uIuyj^EdMO{8T2B$%J7Tl*{GX($dlsBKkTgBF5NA zp-^}>H#g@xj^p^g?-z^3>e|}c27n?FX%@fA8Dr-GOaVv(2msWH=;I(r%=R*XNdTt+ zd7R2#qm0000 + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/VstEffect/logo.png b/plugins/VstEffect/logo.png deleted file mode 100644 index ca98f15d442949081596bcdd2af6d821754a346f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 585 zcmV-P0=E5$P)W0zE{=NR@ho4jFQY zjv3k;REpG2xdc5y#*o*+4s`&7AysTDf06|W!v24o_xwl_5n;;Q_!e*}Z3Z%60~CM+ z(h999Yp4|wvptDODdAFz(pYIu;kw2L@pudoj`m;GL%XYcSJew~4QC3F0~M3Z#D^ z%9T`)Y1@S>T(eLPtS(VBkd+>@m-xGoZ=wO;L<7Ew27D6@_{KEw?M=mSDy^o0FAqY- zQfV~}yrBNV44orND{yXL3p^9Hi6ctmw)&>!{WBAhjffNy4uvR9nbh=}<@6+(d>Z`$ X2=u);Ne5ga00000NkvXXu0mjfNj>>t diff --git a/plugins/VstEffect/logo.svg b/plugins/VstEffect/logo.svg new file mode 100644 index 000000000..5d70a5784 --- /dev/null +++ b/plugins/VstEffect/logo.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + From 17215343e40e68854d6a5b150ed29248f8111b51 Mon Sep 17 00:00:00 2001 From: Fawn Date: Sat, 5 Apr 2025 20:45:40 -0600 Subject: [PATCH 23/62] Upgrade Stereo Matrix plugin assets to SVG (#7803) Upgrade Stereo Matrix plugin assets to SVG * Use QHBoxLayout for StereoMatrix --- plugins/StereoMatrix/CMakeLists.txt | 2 +- .../StereoMatrixControlDialog.cpp | 51 ++++++++---------- plugins/StereoMatrix/artwork.png | Bin 5071 -> 0 bytes plugins/StereoMatrix/artwork.svg | 29 ++++++++++ plugins/StereoMatrix/logo.png | Bin 774 -> 0 bytes plugins/StereoMatrix/logo.svg | 3 ++ 6 files changed, 55 insertions(+), 30 deletions(-) delete mode 100644 plugins/StereoMatrix/artwork.png create mode 100644 plugins/StereoMatrix/artwork.svg delete mode 100644 plugins/StereoMatrix/logo.png create mode 100644 plugins/StereoMatrix/logo.svg diff --git a/plugins/StereoMatrix/CMakeLists.txt b/plugins/StereoMatrix/CMakeLists.txt index 4e6de02ca..2990f8ec8 100644 --- a/plugins/StereoMatrix/CMakeLists.txt +++ b/plugins/StereoMatrix/CMakeLists.txt @@ -1,4 +1,4 @@ INCLUDE(BuildPlugin) -BUILD_PLUGIN(stereomatrix StereoMatrix.cpp StereoMatrixControls.cpp StereoMatrixControlDialog.cpp StereoMatrix.h StereoMatrixControls.h StereoMatrixControlDialog.h MOCFILES StereoMatrixControls.h StereoMatrixControlDialog.h EMBEDDED_RESOURCES artwork.png logo.png) +BUILD_PLUGIN(stereomatrix StereoMatrix.cpp StereoMatrixControls.cpp StereoMatrixControlDialog.cpp StereoMatrix.h StereoMatrixControls.h StereoMatrixControlDialog.h MOCFILES StereoMatrixControls.h StereoMatrixControlDialog.h EMBEDDED_RESOURCES artwork.svg logo.svg) diff --git a/plugins/StereoMatrix/StereoMatrixControlDialog.cpp b/plugins/StereoMatrix/StereoMatrixControlDialog.cpp index da9a3aa9e..1530a0d25 100644 --- a/plugins/StereoMatrix/StereoMatrixControlDialog.cpp +++ b/plugins/StereoMatrix/StereoMatrixControlDialog.cpp @@ -23,10 +23,9 @@ */ - - #include "StereoMatrixControlDialog.h" +#include #include "embed.h" #include "Knob.h" #include "StereoMatrixControls.h" @@ -35,38 +34,32 @@ namespace lmms::gui { -StereoMatrixControlDialog::StereoMatrixControlDialog( - StereoMatrixControls * _controls ) : - EffectControlDialog( _controls ) +StereoMatrixControlDialog::StereoMatrixControlDialog(StereoMatrixControls* controls) : + EffectControlDialog(controls) { - - setFixedSize( 160, 185 ); - - setAutoFillBackground( true ); QPalette pal; - pal.setBrush( backgroundRole(), - PLUGIN_NAME::getIconPixmap( "artwork" ) ); - setPalette( pal ); + setAutoFillBackground(true); + setFixedSize(160, 185); + pal.setBrush(backgroundRole(), PLUGIN_NAME::getIconPixmap("artwork")); + setPalette(pal); - auto llKnob = new Knob(KnobType::Bright26, this); - llKnob->setModel( &_controls->m_llModel ); - llKnob->setHintText( tr( "Left to Left Vol:" ) , "" ); - llKnob->move( 10, 79 ); + auto layout = new QHBoxLayout(this); - auto lrKnob = new Knob(KnobType::Bright26, this); - lrKnob->setModel( &_controls->m_lrModel ); - lrKnob->setHintText( tr( "Left to Right Vol:" ) , "" ); - lrKnob->move( 48, 79 ); + const auto makeKnob = [this, layout]( + FloatModel *model, + const QString &txt_before + ) { + auto k = new Knob(KnobType::Bright26, this); + k->setModel(model); + k->setHintText(txt_before, ""); + layout->addWidget(k, 0, Qt::AlignHCenter); + return k; + }; - auto rlKnob = new Knob(KnobType::Bright26, this); - rlKnob->setModel( &_controls->m_rlModel ); - rlKnob->setHintText( tr( "Right to Left Vol:" ) , "" ); - rlKnob->move( 85, 79 ); - - auto rrKnob = new Knob(KnobType::Bright26, this); - rrKnob->setModel( &_controls->m_rrModel ); - rrKnob->setHintText( tr( "Right to Right Vol:" ) , "" ); - rrKnob->move( 123, 79 ); + makeKnob(&controls->m_llModel, tr("Left to Left Vol:")); + makeKnob(&controls->m_lrModel, tr("Left to Right Vol:")); + makeKnob(&controls->m_rlModel, tr("Right to Left Vol:")); + makeKnob(&controls->m_rrModel, tr("Right to Right Vol:")); } diff --git a/plugins/StereoMatrix/artwork.png b/plugins/StereoMatrix/artwork.png deleted file mode 100644 index f76567fd2c913e8bad12848008abf2a7eb6f37ad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5071 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 VKv>;L7B`^-bTsrKHR?8q{{iI8yo~?= diff --git a/plugins/StereoMatrix/artwork.svg b/plugins/StereoMatrix/artwork.svg new file mode 100644 index 000000000..b54fa8530 --- /dev/null +++ b/plugins/StereoMatrix/artwork.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/StereoMatrix/logo.png b/plugins/StereoMatrix/logo.png deleted file mode 100644 index 9340da708dd79ed97111eb535f51b81a91d6a15b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 774 zcmV+h1Nr=kP)7WEc)VQ)zLm`B#lSD% z0Wg;#IH?7|3b0p&fj_`2;A{cm@zx+(Ge?s$@EN$6LtMjg>;+(boCbaXw;ja=b6mQ?gRp67Yf18(18niCN0v&eY^{Cr&#;#IcF{ks?!* z&o!_q>9xbSw`QytRI!M?zP_o#K-8ExJaV?vi znPt?~0KhTu23U+Gy8^Tw;^SzWSet9n + + From 3f0044dc7f487488a81ecf8f31885ba7451657f0 Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Fri, 11 Apr 2025 10:25:27 -0400 Subject: [PATCH 24/62] Handle SIGINT (#7698) Handle SIGINT Allows Ctrl + C to close LMMS for unix-like environments that --------- Co-authored-by: Fawn Sannar --- include/GuiApplication.h | 9 ++++++ src/core/main.cpp | 9 +++--- src/gui/GuiApplication.cpp | 57 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 4 deletions(-) diff --git a/include/GuiApplication.h b/include/GuiApplication.h index 3a0851499..16680934f 100644 --- a/include/GuiApplication.h +++ b/include/GuiApplication.h @@ -26,6 +26,7 @@ #define LMMS_GUI_GUI_APPLICATION_H #include +#include #include "lmms_export.h" #include "lmmsconfig.h" @@ -53,10 +54,13 @@ public: ~GuiApplication() override; static GuiApplication* instance(); + static void sigintHandler(int); #ifdef LMMS_BUILD_WIN32 static QFont getWin32SystemFont(); #endif + void createSocketNotifier(); + MainWindow* mainWindow() { return m_mainWindow; } MixerView* mixerView() { return m_mixerView; } SongEditorWindow* songEditor() { return m_songEditor; } @@ -67,11 +71,15 @@ public: AutomationEditorWindow* automationEditor() { return m_automationEditor; } ControllerRackView* getControllerRackView() { return m_controllerRackView; } + //! File descriptors for unix socketpair, used to receive SIGINT + static inline int s_sigintFd[2]; + public slots: void displayInitProgress(const QString &msg); private slots: void childDestroyed(QObject *obj); + void sigintOccurred(); private: static GuiApplication* s_instance; @@ -86,6 +94,7 @@ private: MicrotunerConfig* m_microtunerConfig; ControllerRackView* m_controllerRackView; QLabel* m_loadingProgressLabel; + QSocketNotifier* m_sigintNotifier; }; // Short-hand function diff --git a/src/core/main.cpp b/src/core/main.cpp index c75e91ccd..fb54feeab 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -55,7 +55,7 @@ #include #endif -#include +#include // To register the signal handler #include "MainApplication.h" #include "ConfigManager.h" @@ -76,12 +76,12 @@ #include // For feenableexcept #include // For backtrace and backtrace_symbols_fd #include // For STDERR_FILENO -#include // To register the signal handler #endif #ifdef LMMS_DEBUG_FPE -void signalHandler( int signum ) { +void sigfpeHandler(int signum) +{ // Get a back trace void *array[10]; @@ -315,8 +315,9 @@ int main( int argc, char * * argv ) // Install the trap handler // register signal SIGFPE and signal handler - signal(SIGFPE, signalHandler); + signal(SIGFPE, sigfpeHandler); #endif + signal(SIGINT, gui::GuiApplication::sigintHandler); #ifdef LMMS_BUILD_WIN32 // Don't touch redirected streams here diff --git a/src/gui/GuiApplication.cpp b/src/gui/GuiApplication.cpp index 8c674112d..7e62a3141 100644 --- a/src/gui/GuiApplication.cpp +++ b/src/gui/GuiApplication.cpp @@ -41,14 +41,22 @@ #include "SongEditor.h" #include +#include #include #include #include #include #include +#include +#include #ifdef LMMS_BUILD_WIN32 +#include +#include #include +#else +#include +#include #endif namespace lmms @@ -75,6 +83,9 @@ GuiApplication* GuiApplication::instance() GuiApplication::GuiApplication() { + // Immediately register our SIGINT handler + createSocketNotifier(); + // prompt the user to create the LMMS working directory (e.g. ~/Documents/lmms) if it doesn't exist if ( !ConfigManager::inst()->hasWorkingDir() && QMessageBox::question( nullptr, @@ -240,6 +251,52 @@ void GuiApplication::childDestroyed(QObject *obj) } } +/** \brief Called from main when SIGINT is fired + * + * Unix signal handlers can only call async-signal-safe functions: + * write(fd) --> QSocketNotifier --> SLOT sigintOccurred() + * + * See https://doc.qt.io/qt-6/unix-signals.html + */ +void GuiApplication::sigintHandler(int) +{ +#ifdef LMMS_BUILD_WIN32 + char message[] = "Sorry, SIGINT is unhandled on this platform\n"; + std::ignore = _write(_fileno(stderr), message, sizeof(message)); +#else + char a = 1; + std::ignore = ::write(s_sigintFd[0], &a, sizeof(a)); +#endif +} + +// Create our unix signal notifiers +void GuiApplication::createSocketNotifier() +{ +#ifdef LMMS_BUILD_WIN32 + // no-op +#else + if (::socketpair(AF_UNIX, SOCK_STREAM, 0, s_sigintFd)) + { + qFatal("Couldn't create SIGINT socketpair"); + return; + } + + // Listen on the file descriptor for SIGINT + m_sigintNotifier = new QSocketNotifier(s_sigintFd[1], QSocketNotifier::Read, this); + connect(m_sigintNotifier, SIGNAL(activated(QSocketDescriptor)), this, SLOT(sigintOccurred()), Qt::QueuedConnection); +#endif +} + +// Handle the shutdown event +void GuiApplication::sigintOccurred() +{ + m_sigintNotifier->setEnabled(false); + qDebug() << "Shutting down..."; + // cleanup, etc + qApp->exit(3); + m_sigintNotifier->setEnabled(true); +} + #ifdef LMMS_BUILD_WIN32 /*! * @brief Returns the Windows System font. From c2912b89ee19426da91f70d7aef15eb5079613ff Mon Sep 17 00:00:00 2001 From: Andrew Wiltshire <62200778+AW1534@users.noreply.github.com> Date: Sun, 13 Apr 2025 04:42:15 +0100 Subject: [PATCH 25/62] Auto assign MIDI device when track is created (#7835) Newly created MIDI tracks were not automatically assigned to the desired MIDI device when the option to do so was specified in settings. This commit fixes that. --- src/tracks/InstrumentTrack.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 70ac7432e..0d97d500c 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -109,6 +109,8 @@ InstrumentTrack::InstrumentTrack(TrackContainer* tc) : connect(&m_pitchModel, SIGNAL(dataChanged()), this, SLOT(updatePitch()), Qt::DirectConnection); connect(&m_pitchRangeModel, SIGNAL(dataChanged()), this, SLOT(updatePitchRange()), Qt::DirectConnection); connect(&m_mixerChannelModel, SIGNAL(dataChanged()), this, SLOT(updateMixerChannel()), Qt::DirectConnection); + + autoAssignMidiDevice(true); } From 795d513c7f933e3f23f12319d910bd0904a8e928 Mon Sep 17 00:00:00 2001 From: Fawn Date: Tue, 15 Apr 2025 09:52:43 -0600 Subject: [PATCH 26/62] Add clangd cache to .gitignore (#7846) * Add /.cache/ to .gitignore Normally clangd's .cache exists in /build/, which is already in the .gitignore, but if cmake is run in the repository root instead of /build/ the cache is in turn generated in the repository root. * Also add compile_commands.json to .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index cc2823ba0..5afe75307 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ /plugins/ZynAddSubFx/zynaddsubfx/doc/gen/Makefile /data/locale/*.qm Brewfile.lock.json +/.cache/ +compile_commands.json From 64053342d8cae2d38c49cfb6c5b24fe4234ffdef Mon Sep 17 00:00:00 2001 From: Cas Pascal <81458575+khoidauminh@users.noreply.github.com> Date: Wed, 16 Apr 2025 10:40:16 +0700 Subject: [PATCH 27/62] [Follow up] Improve performance when rendering sample waveforms (#7695) A follow up to 786088baec64bec452cbf7fc736582c0a4ac9cb2 to fix rendering issues and crashes. --------- Co-authored-by: Sotonye Atemie --- include/SampleClipView.h | 1 + include/SampleThumbnail.h | 13 +++-- src/gui/SampleThumbnail.cpp | 74 ++++++++++++++++++---------- src/gui/clips/ClipView.cpp | 2 + src/gui/clips/SampleClipView.cpp | 34 +++++++++---- src/gui/editors/AutomationEditor.cpp | 1 + 6 files changed, 82 insertions(+), 43 deletions(-) diff --git a/include/SampleClipView.h b/include/SampleClipView.h index 10ce5b2f3..14f9a8235 100644 --- a/include/SampleClipView.h +++ b/include/SampleClipView.h @@ -67,6 +67,7 @@ private: SampleClip * m_clip; SampleThumbnail m_sampleThumbnail; QPixmap m_paintPixmap; + long m_paintPixmapXPosition; bool splitClip( const TimePos pos ) override; } ; diff --git a/include/SampleThumbnail.h b/include/SampleThumbnail.h index b8e1e85af..7d1082437 100644 --- a/include/SampleThumbnail.h +++ b/include/SampleThumbnail.h @@ -54,10 +54,8 @@ public: { QRect sampleRect; //!< A rectangle that covers the entire range of samples. - QRect drawRect; //!< Specifies the location in `sampleRect` where the waveform will be drawn. Equals - //!< `sampleRect` when null. - - QRect viewportRect; //!< Clips `drawRect`. Equals `drawRect` when null. + QRect viewportRect; //!< Specifies the location in `sampleRect` where the waveform will be drawn. Equals + //!< `sampleRect` when null. float amplification = 1.0f; //!< The amount of amplification to apply to the waveform. @@ -95,8 +93,8 @@ private: Peak operator+(const Peak& other) const { return Peak(std::min(min, other.min), std::max(max, other.max)); } Peak operator+(const SampleFrame& frame) const { return *this + Peak{frame}; } - float min = std::numeric_limits::max(); - float max = std::numeric_limits::min(); + float min = std::numeric_limits::infinity(); + float max = -std::numeric_limits::infinity(); }; Thumbnail() = default; @@ -105,6 +103,7 @@ private: Thumbnail zoomOut(float factor) const; + Peak* data() { return m_peaks.data(); } Peak& operator[](size_t index) { return m_peaks[index]; } const Peak& operator[](size_t index) const { return m_peaks[index]; } @@ -134,7 +133,7 @@ private: using ThumbnailCache = std::vector; std::shared_ptr m_thumbnailCache = std::make_shared(); - + std::shared_ptr m_buffer = SampleBuffer::emptyBuffer(); inline static std::unordered_map, Hash> s_sampleThumbnailCacheMap; }; diff --git a/src/gui/SampleThumbnail.cpp b/src/gui/SampleThumbnail.cpp index c31c0d93e..8ec18b5cb 100644 --- a/src/gui/SampleThumbnail.cpp +++ b/src/gui/SampleThumbnail.cpp @@ -70,6 +70,7 @@ SampleThumbnail::Thumbnail SampleThumbnail::Thumbnail::zoomOut(float factor) con } SampleThumbnail::SampleThumbnail(const Sample& sample) + : m_buffer(sample.buffer()) { auto entry = SampleThumbnailEntry{sample.sampleFile(), QFileInfo{sample.sampleFile()}.lastModified()}; if (!entry.filePath.isEmpty()) @@ -91,11 +92,9 @@ SampleThumbnail::SampleThumbnail(const Sample& sample) s_sampleThumbnailCacheMap[std::move(entry)] = m_thumbnailCache; } - if (!sample.buffer()) { throw std::runtime_error{"Cannot create a sample thumbnail with no buffer"}; } - if (sample.sampleSize() == 0) { return; } - - const auto fullResolutionWidth = sample.sampleSize() * DEFAULT_CHANNELS; - m_thumbnailCache->emplace_back(&sample.buffer()->data()->left(), fullResolutionWidth, fullResolutionWidth); + const auto flatBuffer = m_buffer->data()->data(); + const auto flatBufferSize = m_buffer->size() * DEFAULT_CHANNELS; + m_thumbnailCache->emplace_back(flatBuffer, flatBufferSize, flatBufferSize / AggregationPerZoomStep); while (m_thumbnailCache->back().width() >= AggregationPerZoomStep) { @@ -107,48 +106,71 @@ SampleThumbnail::SampleThumbnail(const Sample& sample) void SampleThumbnail::visualize(VisualizeParameters parameters, QPainter& painter) const { const auto& sampleRect = parameters.sampleRect; - const auto& drawRect = parameters.drawRect.isNull() ? sampleRect : parameters.drawRect; - const auto& viewportRect = parameters.viewportRect.isNull() ? drawRect : parameters.viewportRect; + const auto& viewportRect = parameters.viewportRect.isNull() ? sampleRect : parameters.viewportRect; - const auto renderRect = sampleRect.intersected(drawRect).intersected(viewportRect); + const auto renderRect = sampleRect.intersected(viewportRect); if (renderRect.isNull()) { return; } const auto sampleRange = parameters.sampleEnd - parameters.sampleStart; - if (sampleRange <= 0 || sampleRange > 1) { return; } + if (sampleRange <= 0.0f || sampleRange > 1.0f) { return; } - const auto targetThumbnailWidth = static_cast(static_cast(sampleRect.width()) / sampleRange); + const auto targetThumbnailWidth = static_cast(sampleRect.width() / sampleRange); const auto finerThumbnail = std::find_if(m_thumbnailCache->rbegin(), m_thumbnailCache->rend(), [&](const auto& thumbnail) { return thumbnail.width() >= targetThumbnailWidth; }); - if (finerThumbnail == m_thumbnailCache->rend()) - { - qDebug() << "Could not find closest finer thumbnail for a target width of" << targetThumbnailWidth; - return; - } + const auto useOriginalBuffer = finerThumbnail == m_thumbnailCache->rend(); + const auto drawOriginalBuffer = static_cast(targetThumbnailWidth) == m_buffer->size(); painter.save(); painter.setRenderHint(QPainter::Antialiasing, true); - const auto thumbnailBeginForward = std::max(renderRect.x() - sampleRect.x(), static_cast(parameters.sampleStart * targetThumbnailWidth)); - const auto thumbnailEndForward = std::max(renderRect.x() + renderRect.width() - sampleRect.x(), static_cast(parameters.sampleEnd * targetThumbnailWidth)); + const auto thumbnailBeginForward = std::max(renderRect.x() - sampleRect.x(), parameters.sampleStart * targetThumbnailWidth); + const auto thumbnailEndForward = std::max(renderRect.x() + renderRect.width() - sampleRect.x(), parameters.sampleEnd * targetThumbnailWidth); const auto thumbnailBegin = parameters.reversed ? targetThumbnailWidth - thumbnailBeginForward - 1 : thumbnailBeginForward; const auto thumbnailEnd = parameters.reversed ? targetThumbnailWidth - thumbnailEndForward : thumbnailEndForward; const auto advanceThumbnailBy = parameters.reversed ? -1 : 1; - const auto finerThumbnailScaleFactor = static_cast(finerThumbnail->width()) / targetThumbnailWidth; - const auto yScale = drawRect.height() / 2 * parameters.amplification; + const auto finerThumbnailWidth = useOriginalBuffer ? m_buffer->size() : finerThumbnail->width(); + const auto finerThumbnailScaleFactor = static_cast(finerThumbnailWidth) / targetThumbnailWidth; + const auto yScale = renderRect.height() / 2 * parameters.amplification; for (auto x = renderRect.x(), i = thumbnailBegin; x < renderRect.x() + renderRect.width() && i != thumbnailEnd; - ++x, i += advanceThumbnailBy) + ++x, i += advanceThumbnailBy) { - const auto beginAggregationAt = &(*finerThumbnail)[static_cast(std::floor(i * finerThumbnailScaleFactor))]; - const auto endAggregationAt = &(*finerThumbnail)[static_cast(std::ceil((i + 1) * finerThumbnailScaleFactor))]; - const auto peak = std::accumulate(beginAggregationAt, endAggregationAt, Thumbnail::Peak{}); + if (useOriginalBuffer && drawOriginalBuffer) + { + const auto value = m_buffer->data()->data()[i]; + painter.drawPoint(x, renderRect.center().y() - value * yScale); + continue; + } + else + { + const auto beginIndex = std::clamp(std::floor(i * finerThumbnailScaleFactor), 0, finerThumbnail->width() - 1); + const auto endIndex = std::clamp(std::ceil((i + 1) * finerThumbnailScaleFactor), 0, finerThumbnail->width() - 1); - const auto yMin = drawRect.center().y() - peak.min * yScale; - const auto yMax = drawRect.center().y() - peak.max * yScale; + auto minPeak = 0.f; + auto maxPeak = 0.f; - painter.drawLine(x, yMin, x, yMax); + if (useOriginalBuffer) + { + const auto flatBuffer = m_buffer->data()->data(); + const auto [min, max] = std::minmax_element(flatBuffer + beginIndex, flatBuffer + endIndex); + minPeak = *min; + maxPeak = *max; + } + else + { + const auto beginAggregationAt = finerThumbnail->data() + beginIndex; + const auto endAggregationAt = finerThumbnail->data() + endIndex; + const auto peak = std::accumulate(beginAggregationAt, endAggregationAt, Thumbnail::Peak{}); + minPeak = peak.min; + maxPeak = peak.max; + } + + const auto yMin = renderRect.center().y() - minPeak * yScale; + const auto yMax = renderRect.center().y() - maxPeak * yScale; + painter.drawLine(x, yMin, x, yMax); + } } painter.restore(); diff --git a/src/gui/clips/ClipView.cpp b/src/gui/clips/ClipView.cpp index f98351f37..6c3953cf5 100644 --- a/src/gui/clips/ClipView.cpp +++ b/src/gui/clips/ClipView.cpp @@ -306,6 +306,8 @@ void ClipView::remove() // as actually deleting the Clip with the deleteLater function. That being said, it shouldn't // be possible to make a Clip without a Track (i.e., Clip::getTrack is never nullptr). m_clip->deleteLater(); + + m_trackView->update(); } diff --git a/src/gui/clips/SampleClipView.cpp b/src/gui/clips/SampleClipView.cpp index d5cfb211e..a420d271a 100644 --- a/src/gui/clips/SampleClipView.cpp +++ b/src/gui/clips/SampleClipView.cpp @@ -37,6 +37,7 @@ #include "SampleThumbnail.h" #include "Song.h" #include "StringPairDrag.h" +#include "TrackView.h" namespace lmms::gui { @@ -45,7 +46,8 @@ namespace lmms::gui SampleClipView::SampleClipView( SampleClip * _clip, TrackView * _tv ) : ClipView( _clip, _tv ), m_clip( _clip ), - m_paintPixmap() + m_paintPixmap(), + m_paintPixmapXPosition(0) { // update UI and tooltip updateSample(); @@ -210,15 +212,22 @@ void SampleClipView::paintEvent( QPaintEvent * pe ) if( !needsUpdate() ) { - painter.drawPixmap( 0, 0, m_paintPixmap ); + painter.drawPixmap(m_paintPixmapXPosition, 0, m_paintPixmap); return; } setNeedsUpdate( false ); - if (m_paintPixmap.isNull() || m_paintPixmap.size() != size()) + const auto trackViewWidth = getTrackView()->rect().width(); + + // Use the clip's height to avoid artifacts when rendering while something else is overlaying the clip. + const auto viewPortRect = QRect(0, 0, trackViewWidth * 2, rect().height()); + + m_paintPixmapXPosition = std::max(0, pe->rect().x() - trackViewWidth); + + if (m_paintPixmap.isNull() || m_paintPixmap.size() != viewPortRect.size()) { - m_paintPixmap = QPixmap(size()); + m_paintPixmap = QPixmap(viewPortRect.size()); } QPainter p( &m_paintPixmap ); @@ -274,12 +283,14 @@ void SampleClipView::paintEvent( QPaintEvent * pe ) float sampleLength = m_clip->sampleLength() * ppb / ticksPerBar; const auto& sample = m_clip->m_sample; + + const auto sampleRextX = static_cast(offsetStart) - m_paintPixmapXPosition; + if (sample.sampleSize() > 0) { const auto param = SampleThumbnail::VisualizeParameters{ - .sampleRect = QRect(offsetStart, spacing, sampleLength, height() - spacing), - .drawRect = QRect(0, spacing, width(), height() - spacing), - .viewportRect = pe->rect(), + .sampleRect = QRect(sampleRextX, spacing, sampleLength, height() - spacing), + .viewportRect = viewPortRect, .amplification = sample.amplification(), .reversed = sample.reversed() }; @@ -295,12 +306,15 @@ void SampleClipView::paintEvent( QPaintEvent * pe ) // inner border p.setPen( c.lighter( 135 ) ); - p.drawRect( 1, 1, rect().right() - BORDER_WIDTH, + p.drawRect( + -m_paintPixmapXPosition + 1, + 1, + rect().right() - BORDER_WIDTH, rect().bottom() - BORDER_WIDTH ); // outer border p.setPen( c.darker( 200 ) ); - p.drawRect( 0, 0, rect().right(), rect().bottom() ); + p.drawRect(-m_paintPixmapXPosition, 0, rect().right(), rect().bottom()); // draw the 'muted' pixmap only if the clip was manualy muted if( m_clip->isMuted() ) @@ -332,7 +346,7 @@ void SampleClipView::paintEvent( QPaintEvent * pe ) p.end(); - painter.drawPixmap( 0, 0, m_paintPixmap ); + painter.drawPixmap(m_paintPixmapXPosition, 0, m_paintPixmap); } diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index e1805f727..1239da55e 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -1216,6 +1216,7 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) const auto param = SampleThumbnail::VisualizeParameters{ .sampleRect = QRect(startPos, yOffset, sampleWidth, sampleHeight), + .viewportRect = rect(), .amplification = sample.amplification(), .sampleStart = static_cast(sample.startFrame()) / sample.sampleSize(), .sampleEnd = static_cast(sample.endFrame()) / sample.sampleSize(), From d06c5941f21396ac0806cb1964c64e491a2d69d7 Mon Sep 17 00:00:00 2001 From: TgeorgeT <76885076+TgeorgeT@users.noreply.github.com> Date: Sat, 19 Apr 2025 18:08:23 +0300 Subject: [PATCH 28/62] Fix memory leak in `RemoteVstPlugin::loadPresetFile` (#7827) --- plugins/VstBase/RemoteVstPlugin.cpp | 44 +++++++++++++---------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/plugins/VstBase/RemoteVstPlugin.cpp b/plugins/VstBase/RemoteVstPlugin.cpp index 966c16dc8..077216b30 100644 --- a/plugins/VstBase/RemoteVstPlugin.cpp +++ b/plugins/VstBase/RemoteVstPlugin.cpp @@ -1517,9 +1517,9 @@ void RemoteVstPlugin::savePreset( const std::string & _file ) void RemoteVstPlugin::loadPresetFile( const std::string & _file ) { void * chunk = nullptr; - auto pLen = new unsigned int[1]; + unsigned int pLen; unsigned int len = 0; - auto pBank = (sBank*)new char[sizeof(sBank)]; + sBank pBank; FILE * stream = F_OPEN_UTF8( _file, "rb" ); if (!stream) { @@ -1527,36 +1527,34 @@ void RemoteVstPlugin::loadPresetFile( const std::string & _file ) "Error opening file for loading preset.\n" ); return; } - if ( fread ( pBank, 1, 56, stream ) != 56 ) + if ( fread ( &pBank, 1, 56, stream ) != 56 ) { fprintf( stderr, "Error loading preset file.\n" ); } - pBank->fxID = endian_swap( pBank->fxID ); - pBank->numPrograms = endian_swap( pBank->numPrograms ); + pBank.fxID = endian_swap( pBank.fxID ); + pBank.numPrograms = endian_swap( pBank.numPrograms ); unsigned int toUInt; float * pFloat; - if (static_cast(m_plugin->uniqueID) != pBank->fxID) { + if (static_cast(m_plugin->uniqueID) != pBank.fxID) { sendMessage( message( IdVstCurrentProgramName ). addString( "Error: Plugin UniqID not match" ) ); fclose( stream ); - delete[] (unsigned int*)pLen; - delete[] (sBank*)pBank; return; } if( _file.substr( _file.find_last_of( "." ) + 1 ) != "fxp" ) fseek ( stream , 156 , SEEK_SET ); - if(pBank->fxMagic != 0x6B427846) { - if(pBank->fxMagic != 0x6B437846) { - if ( fread (pLen, 1, 4, stream) != 4 ) + if(pBank.fxMagic != 0x6B427846) { + if(pBank.fxMagic != 0x6B437846) { + if ( fread (&pLen, 1, 4, stream) != 4 ) { fprintf( stderr, "Error loading preset file.\n" ); } - chunk = new char[len = endian_swap(*pLen)]; - } else chunk = new char[len = sizeof(float)*pBank->numPrograms]; + chunk = new char[len = endian_swap(pLen)]; + } else chunk = new char[len = sizeof(float)*pBank.numPrograms]; if ( fread (chunk, len, 1, stream) != 1 ) { fprintf( stderr, "Error loading preset file.\n" ); @@ -1565,14 +1563,14 @@ void RemoteVstPlugin::loadPresetFile( const std::string & _file ) } if(_file.substr(_file.find_last_of(".") + 1) == "fxp") { - pBank->prgName[23] = 0; - pluginDispatch( 4, 0, 0, pBank->prgName ); - if(pBank->fxMagic != 0x6B437846) + pBank.prgName[23] = 0; + pluginDispatch( 4, 0, 0, pBank.prgName ); + if(pBank.fxMagic != 0x6B437846) pluginDispatch( 24, 1, len, chunk ); else { auto toUIntArray = reinterpret_cast(chunk); - for (auto i = 0u; i < pBank->numPrograms; i++) + for (auto i = 0u; i < pBank.numPrograms; i++) { toUInt = endian_swap( toUIntArray[ i ] ); pFloat = ( float* ) &toUInt; @@ -1580,16 +1578,16 @@ void RemoteVstPlugin::loadPresetFile( const std::string & _file ) } } } else { - if(pBank->fxMagic != 0x6B427846) { + if(pBank.fxMagic != 0x6B427846) { pluginDispatch( 24, 0, len, chunk ); } else { - int numPrograms = pBank->numPrograms; + int numPrograms = pBank.numPrograms; unsigned int * toUIntArray; int currProgram = pluginDispatch( effGetProgram ); chunk = new char[ len = sizeof(float)*m_plugin->numParams ]; toUIntArray = reinterpret_cast( chunk ); for (int i =0; i < numPrograms; i++) { - if ( fread (pBank, 1, 56, stream) != 56 ) + if ( fread (&pBank, 1, 56, stream) != 56 ) { fprintf( stderr, "Error loading preset file.\n" ); @@ -1600,8 +1598,8 @@ void RemoteVstPlugin::loadPresetFile( const std::string & _file ) "Error loading preset file.\n" ); } pluginDispatch( effSetProgram, 0, i ); - pBank->prgName[23] = 0; - pluginDispatch( 4, 0, 0, pBank->prgName ); + pBank.prgName[23] = 0; + pluginDispatch( 4, 0, 0, pBank.prgName ); for (int j = 0; j < m_plugin->numParams; j++ ) { toUInt = endian_swap( toUIntArray[ j ] ); pFloat = ( float* ) &toUInt; @@ -1615,8 +1613,6 @@ void RemoteVstPlugin::loadPresetFile( const std::string & _file ) sendCurrentProgramName(); - delete[] (unsigned int*)pLen; - delete[] (sBank*)pBank; delete[] (char*)chunk; } From cb8badc0bb9e3ee448df1ae6f538ec2777fe7331 Mon Sep 17 00:00:00 2001 From: regulus79 <117475203+regulus79@users.noreply.github.com> Date: Sat, 19 Apr 2025 15:08:15 -0400 Subject: [PATCH 29/62] Fix Clip Creation Quantization in Song Editor (#7763) Previously, clicking on a track or dragging in a sample would always create a clip on the closest/previous bar, no matter what the Song Editor's snap size was. This fixes that issue by using the Song Editor's quantization when placing new clips, so for example if the snap size is 1/4 bar, the clip will be added at the closest/previous 1/4 bar mark. --- include/TimePos.h | 2 +- src/core/TimePos.cpp | 4 ++-- src/gui/tracks/SampleTrackView.cpp | 4 +++- src/gui/tracks/TrackContentWidget.cpp | 4 ++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/include/TimePos.h b/include/TimePos.h index ab41832b3..02338c6e7 100644 --- a/include/TimePos.h +++ b/include/TimePos.h @@ -70,7 +70,7 @@ public: TimePos( const bar_t bar, const tick_t ticks ); TimePos( const tick_t ticks = 0 ); - TimePos quantize(float) const; + TimePos quantize(float bars, bool forceRoundDown = false) const; TimePos toAbsoluteBar() const { return getBar() * s_ticksPerBar; } TimePos& operator+=(const TimePos& time) diff --git a/src/core/TimePos.cpp b/src/core/TimePos.cpp index 6e5e034fd..6cf657c72 100644 --- a/src/core/TimePos.cpp +++ b/src/core/TimePos.cpp @@ -53,7 +53,7 @@ TimePos::TimePos( const tick_t ticks ) : { } -TimePos TimePos::quantize(float bars) const +TimePos TimePos::quantize(float bars, bool forceRoundDown) const { //The intervals we should snap to, our new position should be a factor of this int interval = s_ticksPerBar * bars; @@ -65,7 +65,7 @@ TimePos TimePos::quantize(float bars) const // Ternary expression is making sure that the snap happens in the direction to // the right even if m_ticks is negative and the offset is exactly half-way // More details on issue #5840 and PR #5847 - int snapUp = ((2 * offset) == -interval) + int snapUp = forceRoundDown || ((2 * offset) == -interval) ? 0 : (2 * offset) / interval; diff --git a/src/gui/tracks/SampleTrackView.cpp b/src/gui/tracks/SampleTrackView.cpp index 064cc5206..2c7fde8c6 100644 --- a/src/gui/tracks/SampleTrackView.cpp +++ b/src/gui/tracks/SampleTrackView.cpp @@ -37,6 +37,7 @@ #include "Knob.h" #include "SampleClip.h" #include "SampleTrackWindow.h" +#include "SongEditor.h" #include "StringPairDrag.h" #include "TrackContainerView.h" #include "TrackLabelButton.h" @@ -211,11 +212,12 @@ void SampleTrackView::dropEvent(QDropEvent *de) ? trackHeadWidth : de->pos().x(); + const float snapSize = getGUI()->songEditor()->m_editor->getSnapSize(); TimePos clipPos = trackContainerView()->fixedClips() ? TimePos(0) : TimePos(((xPos - trackHeadWidth) / trackContainerView()->pixelsPerBar() * TimePos::ticksPerBar()) + trackContainerView()->currentPosition() - ).quantize(1.0); + ).quantize(snapSize, true); auto sClip = static_cast(getTrack()->createClip(clipPos)); if (sClip) { sClip->setSampleFile(value); } diff --git a/src/gui/tracks/TrackContentWidget.cpp b/src/gui/tracks/TrackContentWidget.cpp index 1d39f97b3..397d9feee 100644 --- a/src/gui/tracks/TrackContentWidget.cpp +++ b/src/gui/tracks/TrackContentWidget.cpp @@ -598,8 +598,8 @@ void TrackContentWidget::mousePressEvent( QMouseEvent * me ) so.at( i )->setSelected( false); } getTrack()->addJournalCheckPoint(); - const TimePos pos = getPosition( me->x() ).getBar() * - TimePos::ticksPerBar(); + const float snapSize = getGUI()->songEditor()->m_editor->getSnapSize(); + const TimePos pos = TimePos(getPosition(me->x())).quantize(snapSize, true); getTrack()->createClip(pos); } } From 32c427eab4a7b7f7073e39c22f22137ebd16bb00 Mon Sep 17 00:00:00 2001 From: regulus79 <117475203+regulus79@users.noreply.github.com> Date: Sat, 19 Apr 2025 15:40:21 -0400 Subject: [PATCH 30/62] Add Midi Reversing (#7606) Adds the ability to reverse the selected midi notes in the PianoRoll. The tool is located under the wrench icon in the PianoRoll and can also be triggered with Shift-R. --------- Co-authored-by: szeli1 <143485814+szeli1@users.noreply.github.com> --- include/MidiClip.h | 3 +++ include/PianoRoll.h | 1 + src/gui/editors/PianoRoll.cpp | 19 +++++++++++++++++++ src/tracks/MidiClip.cpp | 19 +++++++++++++++++++ 4 files changed, 42 insertions(+) diff --git a/include/MidiClip.h b/include/MidiClip.h index f3150ba6f..5d1d7a789 100644 --- a/include/MidiClip.h +++ b/include/MidiClip.h @@ -79,6 +79,9 @@ public: Note * addStepNote( int step ); void setStep( int step, bool enabled ); + //! Horizontally flip the positions of the given notes. + void reverseNotes(const NoteVector& notes); + // Split the list of notes on the given position void splitNotes(const NoteVector& notes, TimePos pos); diff --git a/include/PianoRoll.h b/include/PianoRoll.h index fb175c374..00bdbaeb9 100644 --- a/include/PianoRoll.h +++ b/include/PianoRoll.h @@ -248,6 +248,7 @@ protected slots: void clearGhostClip(); void glueNotes(); void fitNoteLengths(bool fill); + void reverseNotes(); void constrainNoteLengths(bool constrainMax); void changeSnapMode(); diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 63d2a81a6..9dba3e7f0 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -786,6 +786,20 @@ void PianoRoll::constrainNoteLengths(bool constrainMax) Engine::getSong()->setModified(); } +void PianoRoll::reverseNotes() +{ + if (!hasValidMidiClip()) { return; } + + const NoteVector selectedNotes = getSelectedNotes(); + const auto& notes = selectedNotes.empty() ? m_midiClip->notes() : selectedNotes; + + m_midiClip->reverseNotes(notes); + + update(); + getGUI()->songEditor()->update(); + Engine::getSong()->setModified(); +} + void PianoRoll::loadMarkedSemiTones(const QDomElement & de) { @@ -5035,6 +5049,10 @@ PianoRollWindow::PianoRollWindow() : auto maxLengthAction = new QAction(embed::getIconPixmap("max_length"), tr("Max length as last"), noteToolsButton); connect(maxLengthAction, &QAction::triggered, [this](){ m_editor->constrainNoteLengths(true); }); + auto reverseAction = new QAction(embed::getIconPixmap("flip_x"), tr("Reverse Notes"), noteToolsButton); + connect(reverseAction, &QAction::triggered, [this](){ m_editor->reverseNotes(); }); + reverseAction->setShortcut(combine(Qt::SHIFT, Qt::Key_R)); + noteToolsButton->addAction(glueAction); noteToolsButton->addAction(knifeAction); noteToolsButton->addAction(strumAction); @@ -5042,6 +5060,7 @@ PianoRollWindow::PianoRollWindow() : noteToolsButton->addAction(cutOverlapsAction); noteToolsButton->addAction(minLengthAction); noteToolsButton->addAction(maxLengthAction); + noteToolsButton->addAction(reverseAction); notesActionsToolBar->addWidget(noteToolsButton); diff --git a/src/tracks/MidiClip.cpp b/src/tracks/MidiClip.cpp index ab5321687..0d3e5a5b4 100644 --- a/src/tracks/MidiClip.cpp +++ b/src/tracks/MidiClip.cpp @@ -314,6 +314,25 @@ void MidiClip::setStep( int step, bool enabled ) +void MidiClip::reverseNotes(const NoteVector& notes) +{ + addJournalCheckPoint(); + + // Find the very first start position and the very last end position of all the notes. + TimePos firstPos = (*std::min_element(notes.begin(), notes.end(), [](const Note* n1, const Note* n2){ return Note::lessThan(n1, n2); }))->pos(); + TimePos lastPos = (*std::max_element(notes.begin(), notes.end(), [](const Note* n1, const Note* n2){ return n1->endPos() < n2->endPos(); }))->endPos(); + + for (auto note : notes) + { + TimePos newStart = lastPos - (note->pos() - firstPos) - note->length(); + note->setPos(newStart); + } + + rearrangeAllNotes(); + emit dataChanged(); +} + + void MidiClip::splitNotes(const NoteVector& notes, TimePos pos) { From 8b5297f914a367509043dc36f6ce8523b216f44c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petar=20Kati=C4=87?= Date: Tue, 22 Apr 2025 07:06:06 +0200 Subject: [PATCH 31/62] Zyn filter FREQ brought back to 64, fix lowpass issues (#7844) --- plugins/ZynAddSubFx/ZynAddSubFx.cpp | 7 +------ plugins/ZynAddSubFx/zynaddsubfx | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/plugins/ZynAddSubFx/ZynAddSubFx.cpp b/plugins/ZynAddSubFx/ZynAddSubFx.cpp index 19864932d..ef4225967 100644 --- a/plugins/ZynAddSubFx/ZynAddSubFx.cpp +++ b/plugins/ZynAddSubFx/ZynAddSubFx.cpp @@ -109,7 +109,7 @@ ZynAddSubFxInstrument::ZynAddSubFxInstrument( m_plugin( nullptr ), m_remotePlugin( nullptr ), m_portamentoModel( 0, 0, 127, 1, this, tr( "Portamento" ) ), - m_filterFreqModel( 127, 0, 127, 1, this, tr( "Filter frequency" ) ), + m_filterFreqModel( 64, 0, 127, 1, this, tr( "Filter frequency" ) ), m_filterQModel( 64, 0, 127, 1, this, tr( "Filter resonance" ) ), m_bandwidthModel( 64, 0, 127, 1, this, tr( "Bandwidth" ) ), m_fmGainModel( 127, 0, 127, 1, this, tr( "FM gain" ) ), @@ -143,11 +143,6 @@ ZynAddSubFxInstrument::ZynAddSubFxInstrument( connect( instrumentTrack()->pitchRangeModel(), SIGNAL( dataChanged() ), this, SLOT( updatePitchRange() ), Qt::DirectConnection ); - - // ZynAddSubFX's internal value that LMMS's FREQ knob controls - // isn't set properly when the instrument is first loaded in, - // and doesn't update until the FREQ knob is moved - updateFilterFreq(); } diff --git a/plugins/ZynAddSubFx/zynaddsubfx b/plugins/ZynAddSubFx/zynaddsubfx index 9903fc44f..db8ffedb7 160000 --- a/plugins/ZynAddSubFx/zynaddsubfx +++ b/plugins/ZynAddSubFx/zynaddsubfx @@ -1 +1 @@ -Subproject commit 9903fc44ff61c932914fc5b43358c06b1946c446 +Subproject commit db8ffedb7212a7ed9777ffacb9118afbbf779d7c From 510fbf6ffcaf8f1d3b01cb111a98eb01d046b978 Mon Sep 17 00:00:00 2001 From: regulus79 <117475203+regulus79@users.noreply.github.com> Date: Sat, 26 Apr 2025 18:26:26 -0400 Subject: [PATCH 32/62] Global Spacebar Play (#7762) This PR refactors how the spacebar is handled by the editors and the MainWindow to allow it to play/stop (Shift+Space for play/pause) the last played editor, no matter if it is in focus or not. --------- Co-authored-by: Fawn Co-authored-by: Sotonye Atemie --- include/Editor.h | 13 +++++++------ include/MixerChannelView.h | 2 +- include/Song.h | 2 ++ src/core/Song.cpp | 3 +++ src/gui/EffectRackView.cpp | 1 + src/gui/EffectView.cpp | 1 + src/gui/MainWindow.cpp | 25 +++++++++++++++++++++++++ src/gui/MixerChannelView.cpp | 22 ++++++++++------------ src/gui/MixerView.cpp | 11 ++--------- src/gui/editors/AutomationEditor.cpp | 1 + src/gui/editors/Editor.cpp | 11 ++++++++++- src/gui/editors/PatternEditor.cpp | 2 ++ src/gui/editors/PianoRoll.cpp | 1 + 13 files changed, 66 insertions(+), 29 deletions(-) diff --git a/include/Editor.h b/include/Editor.h index a5b667166..46caab538 100644 --- a/include/Editor.h +++ b/include/Editor.h @@ -57,6 +57,13 @@ protected: DropToolBar * addDropToolBar(QWidget * parent, Qt::ToolBarArea whereToAdd, QString const & windowTitle); void closeEvent(QCloseEvent * event) override; + void keyPressEvent(QKeyEvent *ke) override; +public slots: + //! Called by pressing the space key. Plays or stops. + void togglePlayStop(); + //! Called by pressing shift+space. Toggles pause state. + void togglePause(); + protected slots: virtual void play() {} virtual void record() {} @@ -65,12 +72,6 @@ protected slots: virtual void stop() {} private slots: - /// Called by pressing the space key. Plays or stops. - void togglePlayStop(); - - /// Called by pressing shift+space. Toggles pause state. - void togglePause(); - void toggleMaximize(); signals: diff --git a/include/MixerChannelView.h b/include/MixerChannelView.h index e9ee1eddb..ed0d4f1ff 100644 --- a/include/MixerChannelView.h +++ b/include/MixerChannelView.h @@ -61,7 +61,7 @@ public: void contextMenuEvent(QContextMenuEvent*) override; void mousePressEvent(QMouseEvent*) override; void mouseDoubleClickEvent(QMouseEvent*) override; - bool eventFilter(QObject* dist, QEvent* event) override; + void keyPressEvent(QKeyEvent* ke) override; void reset(); int channelIndex() const { return m_channelIndex; } diff --git a/include/Song.h b/include/Song.h index f08edfff6..7fedaa44d 100644 --- a/include/Song.h +++ b/include/Song.h @@ -257,6 +257,7 @@ public: return m_playMode; } + PlayMode lastPlayMode() const { return m_lastPlayMode; } inline PlayPos & getPlayPos( PlayMode pm ) { return m_playPos[static_cast(pm)]; @@ -492,6 +493,7 @@ private: std::array m_timelines; PlayMode m_playMode; + PlayMode m_lastPlayMode; PlayPos m_playPos[PlayModeCount]; bar_t m_length; diff --git a/src/core/Song.cpp b/src/core/Song.cpp index ea60e349b..c2e027003 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -493,6 +493,7 @@ void Song::playSong() } m_playMode = PlayMode::Song; + m_lastPlayMode = m_playMode; m_playing = true; m_paused = false; @@ -532,6 +533,7 @@ void Song::playPattern() } m_playMode = PlayMode::Pattern; + m_lastPlayMode = m_playMode; m_playing = true; m_paused = false; @@ -558,6 +560,7 @@ void Song::playMidiClip( const MidiClip* midiClipToPlay, bool loop ) if( m_midiClipToPlay != nullptr ) { m_playMode = PlayMode::MidiClip; + m_lastPlayMode = m_playMode; m_playing = true; m_paused = false; } diff --git a/src/gui/EffectRackView.cpp b/src/gui/EffectRackView.cpp index 6a4b3124d..c6d62b334 100644 --- a/src/gui/EffectRackView.cpp +++ b/src/gui/EffectRackView.cpp @@ -65,6 +65,7 @@ EffectRackView::EffectRackView( EffectChain* model, QWidget* parent ) : auto addButton = new QPushButton; addButton->setText( tr( "Add effect" ) ); + addButton->setFocusPolicy(Qt::NoFocus); effectsLayout->addWidget( addButton ); diff --git a/src/gui/EffectView.cpp b/src/gui/EffectView.cpp index a5095ee6d..dee9eb136 100644 --- a/src/gui/EffectView.cpp +++ b/src/gui/EffectView.cpp @@ -93,6 +93,7 @@ EffectView::EffectView( Effect * _model, QWidget * _parent ) : QFont f = ctls_btn->font(); ctls_btn->setFont(adjustedToPixelSize(f, DEFAULT_FONT_SIZE)); ctls_btn->setGeometry( 150, 14, 50, 20 ); + ctls_btn->setFocusPolicy(Qt::NoFocus); connect( ctls_btn, SIGNAL(clicked()), this, SLOT(editControls())); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 7ca1387bf..b012eca7a 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -1287,6 +1287,31 @@ void MainWindow::keyPressEvent( QKeyEvent * _ke ) case Qt::Key_Control: m_keyMods.m_ctrl = true; break; case Qt::Key_Shift: m_keyMods.m_shift = true; break; case Qt::Key_Alt: m_keyMods.m_alt = true; break; + case Qt::Key_Space: + { + Editor* lastEditor = nullptr; + switch (Engine::getSong()->lastPlayMode()) + { + case Song::PlayMode::Song: + lastEditor = getGUI()->songEditor(); + break; + case Song::PlayMode::MidiClip: + lastEditor = getGUI()->pianoRoll(); + break; + case Song::PlayMode::Pattern: + lastEditor = getGUI()->patternEditor(); + break; + case Song::PlayMode::AutomationClip: + lastEditor = getGUI()->automationEditor(); + break; + default: + lastEditor = getGUI()->songEditor(); + break; + } + if (m_keyMods.m_shift) { lastEditor->togglePause(); } + else { lastEditor->togglePlayStop(); } + break; + } default: { InstrumentTrackWindow * w = diff --git a/src/gui/MixerChannelView.cpp b/src/gui/MixerChannelView.cpp index ccfb5c75c..13b18ce4b 100644 --- a/src/gui/MixerChannelView.cpp +++ b/src/gui/MixerChannelView.cpp @@ -147,6 +147,8 @@ MixerChannelView::MixerChannelView(QWidget* parent, MixerView* mixerView, int ch mainLayout->addWidget(m_fader, 1, Qt::AlignHCenter); connect(m_renameLineEdit, &QLineEdit::editingFinished, this, &MixerChannelView::renameFinished); + + setFocusPolicy(Qt::StrongFocus); } void MixerChannelView::contextMenuEvent(QContextMenuEvent*) @@ -220,23 +222,19 @@ void MixerChannelView::mouseDoubleClickEvent(QMouseEvent*) renameChannel(); } -bool MixerChannelView::eventFilter(QObject*, QEvent* event) +void MixerChannelView::keyPressEvent(QKeyEvent* ke) { - // If we are in a rename, capture the enter/return events and handle them - if (event->type() == QEvent::KeyPress) + if (ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Return) { - auto keyEvent = static_cast(event); - if (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return) + if (m_inRename) { - if (m_inRename) - { - renameFinished(); - event->accept(); // Stop the event from propagating - return true; - } + renameFinished(); } } - return false; + else if (ke->key() == Qt::Key_Space) + { + m_fader->adjustByDialog(); + } } void MixerChannelView::setChannelIndex(int index) diff --git a/src/gui/MixerView.cpp b/src/gui/MixerView.cpp index e0b483bd6..fd6391bf0 100644 --- a/src/gui/MixerView.cpp +++ b/src/gui/MixerView.cpp @@ -536,15 +536,8 @@ void MixerView::keyPressEvent(QKeyEvent * e) case Qt::Key_F2: renameChannel(m_currentMixerChannel->channelIndex()); break; - case Qt::Key_Space: - { - auto* mixerChannel = currentMixerChannel(); - - if (mixerChannel) - { - mixerChannel->fader()->adjustByDialog(); - } - } + default: + e->ignore(); break; } } diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index 1239da55e..c90f45a86 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -287,6 +287,7 @@ void AutomationEditor::keyPressEvent(QKeyEvent * ke ) break; default: + ke->ignore(); break; } } diff --git a/src/gui/editors/Editor.cpp b/src/gui/editors/Editor.cpp index c32fb5437..750a28fc2 100644 --- a/src/gui/editors/Editor.cpp +++ b/src/gui/editors/Editor.cpp @@ -118,7 +118,6 @@ Editor::Editor(bool record, bool stepRecord) : connect(m_recordAccompanyAction, SIGNAL(triggered()), this, SLOT(recordAccompany())); connect(m_toggleStepRecordingAction, SIGNAL(triggered()), this, SLOT(toggleStepRecording())); connect(m_stopAction, SIGNAL(triggered()), this, SLOT(stop())); - new QShortcut(Qt::Key_Space, this, SLOT(togglePlayStop())); new QShortcut(QKeySequence(combine(Qt::SHIFT, Qt::Key_Space)), this, SLOT(togglePause())); new QShortcut(QKeySequence(combine(Qt::SHIFT, Qt::Key_F11)), this, SLOT(toggleMaximize())); @@ -155,6 +154,16 @@ void Editor::closeEvent(QCloseEvent * event) event->ignore(); } + void Editor::keyPressEvent(QKeyEvent *ke) + { + if (ke->key() == Qt::Key_Space) + { + togglePlayStop(); + return; + } + ke->ignore(); + } + DropToolBar::DropToolBar(QWidget* parent) : QToolBar(parent) { setAcceptDrops(true); diff --git a/src/gui/editors/PatternEditor.cpp b/src/gui/editors/PatternEditor.cpp index 291578698..42bb47e9c 100644 --- a/src/gui/editors/PatternEditor.cpp +++ b/src/gui/editors/PatternEditor.cpp @@ -49,6 +49,8 @@ PatternEditor::PatternEditor(PatternStore* ps) : m_ps(ps) { setModel(ps); + setFocusPolicy(Qt::StrongFocus); + setFocus(); } diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 9dba3e7f0..db8ae6144 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -1505,6 +1505,7 @@ void PianoRoll::keyPressEvent(QKeyEvent* ke) } break; default: + ke->ignore(); break; } From d403a5414065784ff1459c4e6516ea50b3113bf2 Mon Sep 17 00:00:00 2001 From: Andrew Wiltshire <62200778+AW1534@users.noreply.github.com> Date: Mon, 28 Apr 2025 02:34:37 +0100 Subject: [PATCH 33/62] Add option to favorite items in the file browser (#7753) Added the ability to favorite items. This gets added to its own tab named "My Favorites". --------- Co-authored-by: Sotonye Atemie --- data/themes/classic/{hq_mode.png => star.png} | Bin data/themes/default/{hq_mode.png => star.png} | Bin include/ConfigManager.h | 8 ++ include/FileBrowser.h | 34 ++++--- include/MainWindow.h | 1 + include/PathUtil.h | 4 +- src/core/ConfigManager.cpp | 73 ++++++++++--- src/core/PathUtil.cpp | 16 ++- src/gui/FileBrowser.cpp | 96 ++++++++++++++---- src/gui/MainWindow.cpp | 47 ++++----- 10 files changed, 202 insertions(+), 77 deletions(-) rename data/themes/classic/{hq_mode.png => star.png} (100%) rename data/themes/default/{hq_mode.png => star.png} (100%) diff --git a/data/themes/classic/hq_mode.png b/data/themes/classic/star.png similarity index 100% rename from data/themes/classic/hq_mode.png rename to data/themes/classic/star.png diff --git a/data/themes/default/hq_mode.png b/data/themes/default/star.png similarity index 100% rename from data/themes/default/hq_mode.png rename to data/themes/default/star.png diff --git a/include/ConfigManager.h b/include/ConfigManager.h index 3cba834e1..bf98f5999 100644 --- a/include/ConfigManager.h +++ b/include/ConfigManager.h @@ -214,6 +214,8 @@ public: return m_recentlyOpenedProjects; } + const QStringList& favoriteItems() { return m_favoriteItems; } + QString localeDir() const { return m_dataDir + LOCALE_PATH; @@ -240,6 +242,10 @@ public: void addRecentlyOpenedProject(const QString & _file); + void addFavoriteItem(const QString& item); + void removeFavoriteItem(const QString& item); + bool isFavoriteItem(const QString& item); + QString value(const QString& cls, const QString& attribute, const QString& defaultVal = "") const; void setValue(const QString & cls, const QString & attribute, @@ -265,6 +271,7 @@ public: signals: void valueChanged( QString cls, QString attribute, QString value ); + void favoritesChanged(); private: static ConfigManager * s_instanceOfMe; @@ -299,6 +306,7 @@ private: QString m_version; unsigned int m_configVersion; QStringList m_recentlyOpenedProjects; + QStringList m_favoriteItems; using stringPairVector = std::vector>; using settingsMap = QMap; diff --git a/include/FileBrowser.h b/include/FileBrowser.h index 6c10ee763..71a878fc1 100644 --- a/include/FileBrowser.h +++ b/include/FileBrowser.h @@ -61,19 +61,22 @@ class FileBrowser : public SideBarWidget { Q_OBJECT public: + enum class Type + { + Normal, + Favorites + }; + /** - Create a file browser side bar widget - @param directories '*'-separated list of directories to search for. - If a directory of factory files should be in the list it - must be the last one (for the factory files delimiter to work) - @param filter Filter as used in QDir::match - @param recurse *to be documented* - */ - FileBrowser( const QString & directories, const QString & filter, - const QString & title, const QPixmap & pm, - QWidget * parent, bool dirs_as_items = false, - const QString& userDir = "", - const QString& factoryDir = ""); + Create a file browser side bar widget + @param directories '*'-separated list of directories to search for. + If a directory of factory files should be in the list it + must be the last one (for the factory files delimiter to work) + @param filter Filter as used in QDir::match + @param recurse *to be documented* + */ + FileBrowser(Type type, const QString& directories, const QString& filter, const QString& title, const QPixmap& pm, + QWidget* parent, bool dirs_as_items = false, const QString& userDir = "", const QString& factoryDir = ""); ~FileBrowser() override = default; @@ -90,6 +93,7 @@ public: }; return s_excludedPaths; } + static QDir::Filters dirFilters() { return QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden; } static QDir::SortFlags sortFlags() { return QDir::LocaleAware | QDir::DirsFirst | QDir::Name | QDir::IgnoreCase; } @@ -101,7 +105,7 @@ private slots: private: void keyPressEvent( QKeyEvent * ke ) override; - void addItems( const QString & path ); + void addItems(const QString & path); void saveDirectoriesStates(); void restoreDirectoriesStates(); @@ -117,6 +121,7 @@ private: FileBrowserTreeWidget * m_searchTreeWidget; QLineEdit * m_filterEdit; + Type m_type; std::shared_ptr m_currentSearch; QProgressBar* m_searchIndicator = nullptr; @@ -216,8 +221,7 @@ public: { path += QDir::separator(); } - return( QDir::cleanPath( path + text( 0 ) ) + - QDir::separator() ); + return QDir::cleanPath(path + text(0)); } inline void addDirectory( const QString & dir ) diff --git a/include/MainWindow.h b/include/MainWindow.h index 1330de8c5..d2efd4ac0 100644 --- a/include/MainWindow.h +++ b/include/MainWindow.h @@ -46,6 +46,7 @@ class ConfigManager; namespace gui { +class FileBrowser; class PluginView; class SubWindow; class ToolButton; diff --git a/include/PathUtil.h b/include/PathUtil.h index 9b410d014..9bc71c1a1 100644 --- a/include/PathUtil.h +++ b/include/PathUtil.h @@ -31,8 +31,8 @@ namespace lmms::PathUtil { - enum class Base { Absolute, ProjectDir, FactorySample, UserSample, UserVST, Preset, - UserLADSPA, DefaultLADSPA, UserSoundfont, DefaultSoundfont, UserGIG, DefaultGIG, + enum class Base { Absolute, ProjectDir, FactoryProjects, FactorySample, UserSample, UserVST, Preset, + FactoryPresets, UserLADSPA, DefaultLADSPA, UserSoundfont, DefaultSoundfont, UserGIG, DefaultGIG, LocalDir }; //! Return the directory associated with a given base as a QString diff --git a/src/core/ConfigManager.cpp b/src/core/ConfigManager.cpp index d3c973020..bdf4d6c32 100644 --- a/src/core/ConfigManager.cpp +++ b/src/core/ConfigManager.cpp @@ -22,19 +22,19 @@ * */ +#include "ConfigManager.h" -#include -#include -#include +#include #include +#include +#include +#include #include #include -#include "ConfigManager.h" +#include "GuiApplication.h" #include "MainWindow.h" #include "ProjectVersion.h" -#include "GuiApplication.h" - #include "lmmsversion.h" namespace lmms @@ -299,9 +299,6 @@ void ConfigManager::setBackgroundPicFile(const QString & backgroundPicFile) m_backgroundPicFile = backgroundPicFile; } - - - void ConfigManager::createWorkingDir() { QDir().mkpath(m_workingDir); @@ -335,8 +332,27 @@ void ConfigManager::addRecentlyOpenedProject(const QString & file) } } +void ConfigManager::addFavoriteItem(const QString& item) +{ + m_favoriteItems.push_back(item); + saveConfigFile(); + emit favoritesChanged(); +} +void ConfigManager::removeFavoriteItem(const QString& item) +{ + m_favoriteItems.removeAll(item); + saveConfigFile(); + emit favoritesChanged(); +} +bool ConfigManager::isFavoriteItem(const QString& item) +{ + const auto& items = favoriteItems(); + const auto it = std::find_if(items.begin(), items.end(), + [&](const auto& favoriteItem) { return QFileInfo{item} == QFileInfo{favoriteItem}; }); + return it != items.end(); +} QString ConfigManager::value(const QString& cls, const QString& attribute, const QString& defaultVal) const { @@ -466,8 +482,20 @@ void ConfigManager::loadConfigFile(const QString & configFile) { if(n.isElement() && n.toElement().hasAttributes()) { - m_recentlyOpenedProjects << - n.toElement().attribute("path"); + m_recentlyOpenedProjects << n.toElement().attribute("path"); + } + n = n.nextSibling(); + } + } + else if (node.nodeName() == "favoriteitems") + { + m_favoriteItems.clear(); + QDomNode n = node.firstChild(); + while (!n.isNull()) + { + if (n.isElement() && n.toElement().hasAttributes()) + { + m_favoriteItems << n.toElement().attribute("path"); } n = n.nextSibling(); } @@ -571,6 +599,16 @@ void ConfigManager::loadConfigFile(const QString & configFile) { createWorkingDir(); } + + for (auto& file : m_recentlyOpenedProjects) + { + file = PathUtil::toAbsolute(file); + } + + for (auto& file : m_favoriteItems) + { + file = PathUtil::toAbsolute(file); + } } @@ -614,11 +652,22 @@ void ConfigManager::saveConfigFile() for (const auto& recentlyOpenedProject : m_recentlyOpenedProjects) { QDomElement n = doc.createElement("file"); - n.setAttribute("path", recentlyOpenedProject); + n.setAttribute("path", PathUtil::toShortestRelative(recentlyOpenedProject)); recent_files.appendChild(n); } lmms_config.appendChild(recent_files); + QDomElement favorite_items = doc.createElement("favoriteitems"); + + for (const auto& favoriteItem : m_favoriteItems) + { + QDomElement n = doc.createElement("item"); + n.setAttribute("path", PathUtil::toShortestRelative(favoriteItem)); + favorite_items.appendChild(n); + } + + lmms_config.appendChild(favorite_items); + QString xml = "\n" + doc.toString(2); QFile outfile(m_lmmsRcFile); diff --git a/src/core/PathUtil.cpp b/src/core/PathUtil.cpp index 03ec465a9..144c8e986 100644 --- a/src/core/PathUtil.cpp +++ b/src/core/PathUtil.cpp @@ -9,8 +9,8 @@ namespace lmms::PathUtil { - auto relativeBases = std::array{ Base::ProjectDir, Base::FactorySample, Base::UserSample, Base::UserVST, Base::Preset, - Base::UserLADSPA, Base::DefaultLADSPA, Base::UserSoundfont, Base::DefaultSoundfont, Base::UserGIG, Base::DefaultGIG, + auto relativeBases = std::array{ Base::ProjectDir, Base::FactoryProjects, Base::FactorySample, Base::UserSample, Base::UserVST, Base::Preset, + Base::FactoryPresets, Base::UserLADSPA, Base::DefaultLADSPA, Base::UserSoundfont, Base::DefaultSoundfont, Base::UserGIG, Base::DefaultGIG, Base::LocalDir }; QString baseLocation(const Base base, bool* error /* = nullptr*/) @@ -22,6 +22,11 @@ namespace lmms::PathUtil switch (base) { case Base::ProjectDir : loc = ConfigManager::inst()->userProjectsDir(); break; + case Base::FactoryProjects : + { + QDir fpd = QDir(ConfigManager::inst()->factoryProjectsDir()); + loc = fpd.absolutePath(); break; + } case Base::FactorySample : { QDir fsd = QDir(ConfigManager::inst()->factorySamplesDir()); @@ -30,6 +35,11 @@ namespace lmms::PathUtil case Base::UserSample : loc = ConfigManager::inst()->userSamplesDir(); break; case Base::UserVST : loc = ConfigManager::inst()->userVstDir(); break; case Base::Preset : loc = ConfigManager::inst()->userPresetsDir(); break; + case Base::FactoryPresets : + { + QDir fpd = QDir(ConfigManager::inst()->factoryPresetsDir()); + loc = fpd.absolutePath(); break; + } case Base::UserLADSPA : loc = ConfigManager::inst()->ladspaDir(); break; case Base::DefaultLADSPA : loc = ConfigManager::inst()->userLadspaDir(); break; case Base::UserSoundfont : loc = ConfigManager::inst()->sf2Dir(); break; @@ -70,10 +80,12 @@ namespace lmms::PathUtil switch (base) { case Base::ProjectDir : return QStringLiteral("userprojects:"); + case Base::FactoryProjects : return QStringLiteral("factoryprojects:"); case Base::FactorySample : return QStringLiteral("factorysample:"); case Base::UserSample : return QStringLiteral("usersample:"); case Base::UserVST : return QStringLiteral("uservst:"); case Base::Preset : return QStringLiteral("preset:"); + case Base::FactoryPresets : return QStringLiteral("factorypreset:"); case Base::UserLADSPA : return QStringLiteral("userladspa:"); case Base::DefaultLADSPA : return QStringLiteral("defaultladspa:"); case Base::UserSoundfont : return QStringLiteral("usersoundfont:"); diff --git a/src/gui/FileBrowser.cpp b/src/gui/FileBrowser.cpp index 3ba634176..11e48c00f 100644 --- a/src/gui/FileBrowser.cpp +++ b/src/gui/FileBrowser.cpp @@ -25,6 +25,7 @@ #include "FileBrowser.h" +#include #include #include #include @@ -77,18 +78,15 @@ enum TreeWidgetItemTypes TypeDirectoryItem } ; - -FileBrowser::FileBrowser(const QString & directories, const QString & filter, - const QString & title, const QPixmap & pm, - QWidget * parent, bool dirs_as_items, - const QString& userDir, - const QString& factoryDir): - SideBarWidget( title, pm, parent ), - m_directories( directories ), - m_filter( filter ), - m_dirsAsItems( dirs_as_items ), - m_userDir(userDir), - m_factoryDir(factoryDir) +FileBrowser::FileBrowser(Type type, const QString& directories, const QString& filter, const QString& title, const QPixmap& pm, + QWidget* parent, bool dirs_as_items, const QString& userDir, const QString& factoryDir) + : SideBarWidget(title, pm, parent) + , m_type(type) + , m_directories(directories) + , m_filter(filter) + , m_dirsAsItems(dirs_as_items) + , m_userDir(userDir) + , m_factoryDir(factoryDir) { setWindowTitle( tr( "Browser" ) ); @@ -136,6 +134,14 @@ FileBrowser::FileBrowser(const QString & directories, const QString & filter, m_previousFilterValue = ""; + if (m_type == Type::Favorites) + { + connect(ConfigManager::inst(), &ConfigManager::favoritesChanged, [this] { + m_directories = ConfigManager::inst()->favoriteItems().join("*"); + reloadTree(); + }); + } + reloadTree(); show(); } @@ -329,7 +335,7 @@ void FileBrowser::reloadTree() m_fileBrowserTreeWidget->clear(); - QStringList paths = m_directories.split('*'); + auto paths = m_directories.isEmpty() ? QStringList{} : m_directories.split('*'); if (m_showUserContent && !m_showUserContent->isChecked()) { @@ -341,12 +347,37 @@ void FileBrowser::reloadTree() paths.removeAll(m_factoryDir); } - if (!paths.isEmpty()) + switch (m_type) { + case Type::Favorites: + for (auto& path : paths) + { + while (path.endsWith('/') || path.endsWith('\\') || path.endsWith(".")) + { + path.chop(1); + } + + auto info = QFileInfo{PathUtil::toAbsolute(path)}; + + if (info.isDir()) + { + auto dir = new Directory(info.fileName(), info.absolutePath(), m_filter); + dir->update(); + m_fileBrowserTreeWidget->addTopLevelItem(dir); + } + else if (info.isFile()) + { + auto file = new FileItem(info.fileName(), info.path()); + m_fileBrowserTreeWidget->addTopLevelItem(file); + } + } + break; + case Type::Normal: for (const auto& path : paths) { addItems(path); } + break; } if (m_filterEdit->text().isEmpty()) @@ -642,17 +673,29 @@ void FileBrowserTreeWidget::contextMenuEvent(QContextMenuEvent* e) { case TypeFileItem: { auto file = dynamic_cast(item); - - if (file->isTrack()) - { - contextMenu.addAction( - tr("Send to active instrument-track"), [=, this] { sendToActiveInstrumentTrack(file); }); - contextMenu.addSeparator(); - } + const auto path = QFileInfo{file->fullName()}.absoluteFilePath(); contextMenu.addAction(QIcon(embed::getIconPixmap("folder")), tr("Show in %1").arg(fileManager), [=] { FileRevealer::reveal(file->fullName()); }); + if (ConfigManager::inst()->isFavoriteItem(file->fullName())) + { + contextMenu.addAction( + QIcon(embed::getIconPixmap("star")), tr("Remove favorite file"), [path] { ConfigManager::inst()->removeFavoriteItem(path); }); + } + else + { + contextMenu.addAction( + QIcon(embed::getIconPixmap("star")), tr("Add favorite file"), [path] { ConfigManager::inst()->addFavoriteItem(path); }); + } + + if (file->isTrack()) + { + contextMenu.addSeparator(); + contextMenu.addAction( + tr("Send to active instrument-track"), [=, this] { sendToActiveInstrumentTrack(file); }); + } + auto songEditorHeader = new QAction(tr("Song Editor"), nullptr); songEditorHeader->setDisabled(true); contextMenu.addAction( songEditorHeader ); @@ -666,9 +709,20 @@ void FileBrowserTreeWidget::contextMenuEvent(QContextMenuEvent* e) } case TypeDirectoryItem: { auto dir = dynamic_cast(item); + const auto path = QFileInfo{dir->fullName()}.absoluteFilePath(); + contextMenu.addAction(QIcon(embed::getIconPixmap("folder")), tr("Open in %1").arg(fileManager), [=] { FileRevealer::openDir(dir->fullName()); }); + + if (ConfigManager::inst()->isFavoriteItem(dir->fullName())) + { + contextMenu.addAction(QIcon(embed::getIconPixmap("star")), tr("Remove favorite folder"), [path] { ConfigManager::inst()->removeFavoriteItem(path); }); + } + else + { + contextMenu.addAction(QIcon(embed::getIconPixmap("star")), tr("Add favorite folder"), [path] { ConfigManager::inst()->addFavoriteItem(path); }); + } break; } } diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index b012eca7a..90d4196a3 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -111,30 +111,27 @@ MainWindow::MainWindow() : emit initProgress(tr("Preparing plugin browser")); sideBar->appendTab( new PluginBrowser( splitter ) ); emit initProgress(tr("Preparing file browsers")); - sideBar->appendTab( new FileBrowser( - confMgr->userProjectsDir() + "*" + - confMgr->factoryProjectsDir(), - "*.mmp *.mmpz *.xml *.mid *.mpt", - tr( "My Projects" ), - embed::getIconPixmap( "project_file" ).transformed( QTransform().rotate( 90 ) ), - splitter, false, - confMgr->userProjectsDir(), - confMgr->factoryProjectsDir())); - sideBar->appendTab( - new FileBrowser(confMgr->userSamplesDir() + "*" + confMgr->factorySamplesDir(), FileItem::defaultFilters(), - tr("My Samples"), embed::getIconPixmap("sample_file").transformed(QTransform().rotate(90)), splitter, false, - confMgr->userSamplesDir(), confMgr->factorySamplesDir())); - sideBar->appendTab( new FileBrowser( - confMgr->userPresetsDir() + "*" + - confMgr->factoryPresetsDir(), - "*.xpf *.cs.xml *.xiz *.lv2", - tr( "My Presets" ), - embed::getIconPixmap( "preset_file" ).transformed( QTransform().rotate( 90 ) ), - splitter , false, - confMgr->userPresetsDir(), - confMgr->factoryPresetsDir())); - sideBar->appendTab(new FileBrowser(QDir::homePath(), FileItem::defaultFilters(), tr("My Home"), - embed::getIconPixmap("home").transformed(QTransform().rotate(90)), splitter, false)); + + sideBar->appendTab(new FileBrowser(FileBrowser::Type::Favorites, ConfigManager::inst()->favoriteItems().join("*"), FileItem::defaultFilters(), "My Favorites", + embed::getIconPixmap("star").transformed(QTransform().rotate(90)), splitter, false, "", "")); + + sideBar->appendTab(new FileBrowser(FileBrowser::Type::Normal, + confMgr->userProjectsDir() + "*" + confMgr->factoryProjectsDir(), "*.mmp *.mmpz *.xml *.mid *.mpt", + tr("My Projects"), embed::getIconPixmap("project_file").transformed(QTransform().rotate(90)), splitter, false, + confMgr->userProjectsDir(), confMgr->factoryProjectsDir())); + + sideBar->appendTab(new FileBrowser(FileBrowser::Type::Normal, + confMgr->userSamplesDir() + "*" + confMgr->factorySamplesDir(), FileItem::defaultFilters(), tr("My Samples"), + embed::getIconPixmap("sample_file").transformed(QTransform().rotate(90)), splitter, false, + confMgr->userSamplesDir(), confMgr->factorySamplesDir())); + + sideBar->appendTab(new FileBrowser(FileBrowser::Type::Normal, + confMgr->userPresetsDir() + "*" + confMgr->factoryPresetsDir(), "*.xpf *.cs.xml *.xiz *.lv2", tr("My Presets"), + embed::getIconPixmap("preset_file").transformed(QTransform().rotate(90)), splitter, false, + confMgr->userPresetsDir(), confMgr->factoryPresetsDir())); + + sideBar->appendTab(new FileBrowser(FileBrowser::Type::Normal, QDir::homePath(), FileItem::defaultFilters(), + tr("My Home"), embed::getIconPixmap("home").transformed(QTransform().rotate(90)), splitter, false)); QStringList root_paths; QString title = tr("Root Directory"); @@ -156,7 +153,7 @@ MainWindow::MainWindow() : } #endif - sideBar->appendTab(new FileBrowser(root_paths.join("*"), FileItem::defaultFilters(), title, + sideBar->appendTab(new FileBrowser(FileBrowser::Type::Normal, root_paths.join("*"), FileItem::defaultFilters(), title, embed::getIconPixmap("computer").transformed(QTransform().rotate(90)), splitter, dirs_as_items)); m_workspace = new MovableQMdiArea(splitter); From c91c81eee7b3cddf750a686930b58c3540689fc6 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Mon, 28 Apr 2025 19:21:20 +0200 Subject: [PATCH 34/62] Update src/3rdparty/qt5-x11embed to latest version (#7825) Update the submodule https://github.com/Lukas-W/qt5-x11embed.git to commit 499d737c8e8. This is done to enable LMMS to build with CMake 4 and newer. --- src/3rdparty/qt5-x11embed | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/3rdparty/qt5-x11embed b/src/3rdparty/qt5-x11embed index 022b39a1d..499d737c8 160000 --- a/src/3rdparty/qt5-x11embed +++ b/src/3rdparty/qt5-x11embed @@ -1 +1 @@ -Subproject commit 022b39a1d496d72eb3e5b5188e5559f66afca957 +Subproject commit 499d737c8e8d6882c55ef6d26981d313b296e5ca From 679f9b848e67ae42e05ee92ca44fc93755fa8b3a Mon Sep 17 00:00:00 2001 From: regulus79 <117475203+regulus79@users.noreply.github.com> Date: Tue, 29 Apr 2025 17:56:13 -0400 Subject: [PATCH 35/62] Add splitting (and resizing) to all types of clips (#7477) Allow for splitting and resizing all types of clips (automation, MIDI, sample, pattern, etc) using the knife tool in the Song Editor. --------- Co-authored-by: szeli1 <143485814+szeli1@users.noreply.github.com> Co-authored-by: Dalton Messmer --- data/themes/classic/auto_resize.png | Bin 0 -> 920 bytes data/themes/classic/auto_resize_disable.png | Bin 0 -> 1031 bytes .../classic/clear_notes_out_of_bounds.png | Bin 0 -> 1241 bytes data/themes/classic/style.css | 1 + data/themes/default/auto_resize.png | Bin 0 -> 633 bytes data/themes/default/auto_resize_disable.png | Bin 0 -> 656 bytes .../default/clear_notes_out_of_bounds.png | Bin 0 -> 630 bytes data/themes/default/style.css | 1 + include/AutomationClip.h | 15 +- include/AutomationClipView.h | 2 + include/AutomationEditor.h | 2 + include/Clip.h | 32 +- include/ClipView.h | 33 +- include/DetuningHelper.h | 4 + include/InlineAutomation.h | 24 +- include/MidiClip.h | 9 +- include/MidiClipView.h | 10 + include/Note.h | 7 +- include/PatternClip.h | 5 + include/PianoRoll.h | 2 + include/SampleClip.h | 8 +- include/SampleClipView.h | 1 - src/core/AutomatableModel.cpp | 2 +- src/core/AutomationClip.cpp | 143 ++++--- src/core/Clip.cpp | 25 +- src/core/Note.cpp | 40 +- src/core/PatternClip.cpp | 4 +- src/core/SampleClip.cpp | 48 ++- src/core/Song.cpp | 11 +- src/core/TrackContainer.cpp | 6 +- src/gui/clips/AutomationClipView.cpp | 42 ++- src/gui/clips/ClipView.cpp | 350 ++++++++---------- src/gui/clips/MidiClipView.cpp | 273 +++++++++++++- src/gui/clips/PatternClipView.cpp | 11 +- src/gui/clips/SampleClipView.cpp | 35 +- src/gui/editors/AutomationEditor.cpp | 22 +- src/gui/editors/PianoRoll.cpp | 46 ++- src/gui/editors/SongEditor.cpp | 2 +- src/tracks/InstrumentTrack.cpp | 22 +- src/tracks/MidiClip.cpp | 52 ++- 40 files changed, 868 insertions(+), 422 deletions(-) create mode 100644 data/themes/classic/auto_resize.png create mode 100644 data/themes/classic/auto_resize_disable.png create mode 100644 data/themes/classic/clear_notes_out_of_bounds.png create mode 100644 data/themes/default/auto_resize.png create mode 100644 data/themes/default/auto_resize_disable.png create mode 100644 data/themes/default/clear_notes_out_of_bounds.png diff --git a/data/themes/classic/auto_resize.png b/data/themes/classic/auto_resize.png new file mode 100644 index 0000000000000000000000000000000000000000..491966a8e6bee10ef26f7019e66109f80b3639f9 GIT binary patch literal 920 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 DS}Ha{ literal 0 HcmV?d00001 diff --git a/data/themes/classic/style.css b/data/themes/classic/style.css index a064366a6..a0fb48209 100644 --- a/data/themes/classic/style.css +++ b/data/themes/classic/style.css @@ -761,6 +761,7 @@ lmms--gui--ClipView { qproperty-textBackgroundColor: rgba(0, 0, 0, 75); qproperty-textShadowColor: rgb( 0, 0, 0 ); qproperty-gradient: true; /* boolean property, set true to have a gradient */ + qproperty-markerColor: rgb(0, 0, 0); /* finger tip offset of cursor */ qproperty-mouseHotspotHand: 3px 3px; qproperty-mouseHotspotKnife: 0px 0px; diff --git a/data/themes/default/auto_resize.png b/data/themes/default/auto_resize.png new file mode 100644 index 0000000000000000000000000000000000000000..52b65742d849e77a2e3c1c58ed39094f555cf1f4 GIT binary patch literal 633 zcmV-<0*3vGP)EX>4Tx04R}tkv&MmP!xqvQ$>-Af*C{{GE^r!s6`xW6^c+H)C#RSn7s54nlvOS zE{=k0!NH%!s)LKOt`4q(Aov5~E;uQ=NQw8Qg%&Yhc)XAE?m4`7A0X7rOt-sYfNnTe zDiN15*;T3M6#v&}hKPQtgo?x~yVF2b|?`~IwcEoUY^3Ns?&BZv{4%*za+Scy zv49G6D4rku4}Q(i|qi@OrL$^TRT61o#bDTZ^X_{5?1~@nb z#tW3a?(^>6&bj^D)0*E8_`Gu5n-=>)00009a7bBm000fw000fw0YWI7cmMzZ2XskI zMF;5t6crOKAz5|q0000PbVXQnLvL+uWo~o;Lvm$dbY)~9cWHEJAV*0}P*;Ht7XSbN zV@X6oR5;7+luHhPKnO#5-v0z#wr*zSpb*7b7`q|i)0C8eBv}FgNvcHCKtyy^JL})9 z5df`(A6F>JBZaqJ9wk5Ko)S)^)u9ajipyn%?JKV`#ambYpTh!$(fLm&kwkzU=7uy} T=ZV(300000NkvXXu0mjf3~CG! literal 0 HcmV?d00001 diff --git a/data/themes/default/auto_resize_disable.png b/data/themes/default/auto_resize_disable.png new file mode 100644 index 0000000000000000000000000000000000000000..a997c81b65c8d66108776ab0c53823d4b5849b77 GIT binary patch literal 656 zcmV;B0&o3^P)EX>4Tx04R}tkv&MmP!xqvQ$>-Af*C{{GE^r!s6`xW6^c+H)C#RSn7s54nlvOS zE{=k0!NH%!s)LKOt`4q(Aov5~E;uQ=NQw8Qg%&Yhc)XAE?m4`7A0X7rOt-sYfNnTe zDiN15*;T3M6#v&}hKPQtgo?x~yVF2b|?`~IwcEoUY^3Ns?&BZv{4%*za+Scy zv49G6D4rku4}Q(i|qi@OrL$^TRT61o#bDTZ^X_{5?1~@nb z#tW3a?(^>6&bj^D)0*E8_`Gu5n-=>)00009a7bBm000fw000fw0YWI7cmMzZ2XskI zMF;5t6crN~cj~dt0000PbVXQnLvL+uWo~o;Lvm$dbY)~9cWHEJAV*0}P*;Ht7XSbN zdPzhJP)EX>4Tx04R}tkv&MmP!xqvTcsiu5sQd8WT;N10C4=2nH^D|{Hh5Tclsn5oZ+VhW!1bx++?cQKyj-S=npDS49tK7lySbi*RvAfDc| zbk6(4VOEk9;&bAtK^G)`h$D)sQNBOx zvch?bvs$UK);;+PLwRjwnd`Jhk;EdFAVPqQ8p^1^LX38e6ccGWPk8u89KT2|nOtQs zax9<<6_Voz|AXJNH4BpyZc-=#bidg4#~2XY1)6o+{yw(t<_QpZ2ClTWzuEw1K1r{) zwa5|BzYSbmw>5bWxZDATpLEHP94SD{Unl_YXY@@uVDJ{`U32HwI>+e)kfB+nZh(VB zV7y4#>mKj!>73iYJ+1lu00200-z&QXrT_o{32;bRa{vGUw*UYDw*f{j4Hf_Z00(qQ zO+^Rk0TKWt5uvx28UO$Q8FWQhbVF}#ZDnqB07G(RVRU6=Aa`kWXdp*PO;A^X4i^9b z0AEQ&K~y-)`%68`g~T36VLp^3?N? zZvl9hBCsSYc!X82R?7HQQb}^Q*xPcpLy77lNq2rdc9uFMtOoPL_0>-J0P^G{F*HDY Qr~m)}07*qoM6N<$g0T4vO8@`> literal 0 HcmV?d00001 diff --git a/data/themes/default/style.css b/data/themes/default/style.css index 07ee74617..f7a5acc14 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -804,6 +804,7 @@ lmms--gui--ClipView { qproperty-textBackgroundColor: rgba(0, 0, 0, 75); qproperty-textShadowColor: rgba(0,0,0,200); qproperty-gradient: false; /* boolean property, set true to have a gradient */ + qproperty-markerColor: rgb(0, 0, 0); /* finger tip offset of cursor */ qproperty-mouseHotspotHand: 7px 2px; qproperty-mouseHotspotKnife: 0px 0px; diff --git a/include/AutomationClip.h b/include/AutomationClip.h index 0b49978c7..abc713869 100644 --- a/include/AutomationClip.h +++ b/include/AutomationClip.h @@ -68,7 +68,6 @@ public: using TimemapIterator = timeMap::const_iterator; AutomationClip( AutomationTrack * _auto_track ); - AutomationClip( const AutomationClip & _clip_to_copy ); ~AutomationClip() override = default; bool addObject( AutomatableModel * _obj, bool _search_dup = true ); @@ -90,7 +89,7 @@ public: void setTension( QString _new_tension ); TimePos timeMapLength() const; - void updateLength(); + void updateLength() override; TimePos putValue( const TimePos & time, @@ -196,12 +195,22 @@ public: static int quantization() { return s_quantization; } static void setQuantization(int q) { s_quantization = q; } + AutomationClip* clone() override + { + return new AutomationClip(*this); + } + + void clearObjects() { m_objects.clear(); } + public slots: void clear(); void objectDestroyed( lmms::jo_id_t ); void flipY( int min, int max ); void flipY(); - void flipX( int length = -1 ); + void flipX(int start = -1, int end = -1); + +protected: + AutomationClip( const AutomationClip & _clip_to_copy ); private: void cleanObjects(); diff --git a/include/AutomationClipView.h b/include/AutomationClipView.h index bdd2f0568..e55c44228 100644 --- a/include/AutomationClipView.h +++ b/include/AutomationClipView.h @@ -75,6 +75,8 @@ private: QStaticText m_staticTextName; void scaleTimemapToFit( float oldMin, float oldMax ); + + bool isResizableBeforeStart() override { return false; } } ; diff --git a/include/AutomationEditor.h b/include/AutomationEditor.h index 61f1cb791..c7eaca8aa 100644 --- a/include/AutomationEditor.h +++ b/include/AutomationEditor.h @@ -74,6 +74,7 @@ class AutomationEditor : public QWidget, public JournallingObject Q_PROPERTY(QColor ghostNoteColor MEMBER m_ghostNoteColor) Q_PROPERTY(QColor detuningNoteColor MEMBER m_detuningNoteColor) Q_PROPERTY(QColor ghostSampleColor MEMBER m_ghostSampleColor) + Q_PROPERTY(QColor outOfBoundsShade MEMBER m_outOfBoundsShade) public: void setCurrentClip(AutomationClip * new_clip); void setGhostMidiClip(MidiClip* newMidiClip); @@ -291,6 +292,7 @@ private: QColor m_ghostNoteColor; QColor m_detuningNoteColor; QColor m_ghostSampleColor; + QColor m_outOfBoundsShade; SampleThumbnail m_sampleThumbnail; diff --git a/include/Clip.h b/include/Clip.h index a520ad4e4..706b982c8 100644 --- a/include/Clip.h +++ b/include/Clip.h @@ -100,12 +100,28 @@ public: * resized by clicking and dragging its edge. * */ - inline void setAutoResize( const bool r ) + inline void setResizable( const bool r ) + { + m_resizable = r; + } + + inline const bool getResizable() const + { + return m_resizable; + } + + /*! \brief Set whether a clip has been resized yet by the user or the knife tool. + * + * If a clip has been resized previously, it will not automatically + * resize when editing it. + * + */ + void setAutoResize(const bool r) { m_autoResize = r; } - inline const bool getAutoResize() const + bool getAutoResize() const { return m_autoResize; } @@ -115,6 +131,7 @@ public: virtual void movePosition( const TimePos & pos ); virtual void changeLength( const TimePos & length ); + virtual void updateLength() {}; virtual gui::ClipView * createView( gui::TrackView * tv ) = 0; @@ -137,6 +154,12 @@ public: // Will copy the state of a clip to another clip static void copyStateTo( Clip *src, Clip *dst ); + /** + * Creates a copy of this clip + * @return pointer to the new clip object + */ + virtual Clip* clone() = 0; + public slots: void toggleMute(); @@ -147,6 +170,8 @@ signals: void destroyedClip(); void colorChanged(); +protected: + Clip(const Clip& other); private: Track * m_track; @@ -158,7 +183,8 @@ private: BoolModel m_mutedModel; BoolModel m_soloModel; - bool m_autoResize; + bool m_resizable = true; + bool m_autoResize = true; bool m_selectViewOnCreate; diff --git a/include/ClipView.h b/include/ClipView.h index 14898db65..6e904b856 100644 --- a/include/ClipView.h +++ b/include/ClipView.h @@ -63,6 +63,7 @@ class ClipView : public selectableObject, public ModelView Q_PROPERTY( QColor textShadowColor READ textShadowColor WRITE setTextShadowColor ) Q_PROPERTY( QColor patternClipBackground READ patternClipBackground WRITE setPatternClipBackground ) Q_PROPERTY( bool gradient READ gradient WRITE setGradient ) + Q_PROPERTY(QColor markerColor READ markerColor WRITE setMarkerColor) // We have to use a QSize here because using QPoint isn't supported. // width -> x, height -> y Q_PROPERTY( QSize mouseHotspotHand MEMBER m_mouseHotspotHand ) @@ -94,6 +95,7 @@ public: QColor textBackgroundColor() const; QColor textShadowColor() const; QColor patternClipBackground() const; + QColor markerColor() const; bool gradient() const; void setMutedColor( const QColor & c ); void setMutedBackgroundColor( const QColor & c ); @@ -103,6 +105,7 @@ public: void setTextShadowColor( const QColor & c ); void setPatternClipBackground(const QColor& c); void setGradient( const bool & b ); + void setMarkerColor(const QColor& c); // access needsUpdate member variable bool needsUpdate(); @@ -121,10 +124,8 @@ public: // some metadata to be written to the clipboard. static void remove( QVector clipvs ); static void toggleMute( QVector clipvs ); - static void mergeClips(QVector clipvs); - // Returns true if selection can be merged and false if not - static bool canMergeSelection(QVector clipvs); + void toggleSelectedAutoResize(); QColor getColorForDisplay( QColor ); @@ -147,8 +148,7 @@ protected: Cut, Copy, Paste, - Mute, - Merge + Mute }; TrackView * m_trackView; @@ -176,7 +176,7 @@ protected: } bool unquantizedModHeld( QMouseEvent * me ); - TimePos quantizeSplitPos( TimePos, bool shiftMode ); + TimePos quantizeSplitPos(TimePos); float pixelsPerBar(); @@ -224,6 +224,7 @@ private: QColor m_textShadowColor; QColor m_patternClipBackground; bool m_gradient; + QColor m_markerColor; QSize m_mouseHotspotHand; // QSize must be used because QPoint QSize m_mouseHotspotKnife; // isn't supported by property system QCursor m_cursorHand; @@ -244,8 +245,24 @@ private: TimePos draggedClipPos( QMouseEvent * me ); int knifeMarkerPos( QMouseEvent * me ); void setColor(const std::optional& color); - //! Return true iff the clip could be split. Currently only implemented for samples - virtual bool splitClip( const TimePos pos ){ return false; }; + + //! Returns whether the user can left-resize this clip so that the start of the clip bounds is before the start of the clip content. + virtual bool isResizableBeforeStart() { return true; }; + /** + * Split this Clip into two clips + * @param pos the position of the split, relative to the start of the clip + * @return true if the clip could be split + */ + bool splitClip(const TimePos pos); + /** + * Destructively split this Clip into two clips. If the clip type does not implement this feature, it will default to normal splitting. + * @param pos the position of the split, relative to the start of the clip + * @return true if the clip could be split + */ + virtual bool destructiveSplitClip(const TimePos pos) + { + return splitClip(pos); + } void updateCursor(QMouseEvent * me); } ; diff --git a/include/DetuningHelper.h b/include/DetuningHelper.h index da8eb5983..b76708082 100644 --- a/include/DetuningHelper.h +++ b/include/DetuningHelper.h @@ -39,6 +39,10 @@ public: InlineAutomation() { } + DetuningHelper(const DetuningHelper& _copy) : + InlineAutomation(_copy) + { + } ~DetuningHelper() override = default; diff --git a/include/InlineAutomation.h b/include/InlineAutomation.h index 3e27e137b..5928b2db4 100644 --- a/include/InlineAutomation.h +++ b/include/InlineAutomation.h @@ -32,22 +32,24 @@ namespace lmms { -class InlineAutomation : public FloatModel, public sharedObject +class InlineAutomation : public FloatModel { public: InlineAutomation() : - FloatModel(), - sharedObject(), - m_autoClip( nullptr ) + FloatModel() { } + InlineAutomation(const InlineAutomation& _copy) : + FloatModel(_copy.value(), _copy.minValue(), _copy.maxValue(), _copy.step()), + m_autoClip(_copy.m_autoClip->clone()) + { + m_autoClip->clearObjects(); + m_autoClip->addObject(this); + } + ~InlineAutomation() override { - if( m_autoClip ) - { - delete m_autoClip; - } } virtual float defaultValue() const = 0; @@ -81,10 +83,10 @@ public: { if( m_autoClip == nullptr ) { - m_autoClip = new AutomationClip( nullptr ); + m_autoClip = std::make_unique(nullptr); m_autoClip->addObject( this ); } - return m_autoClip; + return m_autoClip.get(); } void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override; @@ -92,7 +94,7 @@ public: private: - AutomationClip * m_autoClip; + std::unique_ptr m_autoClip; } ; diff --git a/include/MidiClip.h b/include/MidiClip.h index 5d1d7a789..076ec93b0 100644 --- a/include/MidiClip.h +++ b/include/MidiClip.h @@ -53,12 +53,11 @@ public: } ; MidiClip( InstrumentTrack* instrumentTrack ); - MidiClip( const MidiClip& other ); ~MidiClip() override; void init(); - void updateLength(); + void updateLength() override; // note management Note * addNote( const Note & _new_note, const bool _quant_pos = true ); @@ -117,6 +116,11 @@ public: gui::ClipView * createView( gui::TrackView * _tv ) override; + MidiClip* clone() override + { + return new MidiClip(*this); + } + using Model::dataChanged; @@ -127,6 +131,7 @@ public slots: void clear(); protected: + MidiClip( const MidiClip& other ); void updatePatternTrack(); protected slots: diff --git a/include/MidiClipView.h b/include/MidiClipView.h index 4285bf9da..9f1894755 100644 --- a/include/MidiClipView.h +++ b/include/MidiClipView.h @@ -63,6 +63,11 @@ public: QColor const & getMutedNoteBorderColor() const { return m_mutedNoteBorderColor; } void setMutedNoteBorderColor(QColor const & color) { m_mutedNoteBorderColor = color; } + // Returns true if selection can be merged and false if not + static bool canMergeSelection(QVector clipvs); + static void mergeClips(QVector clipvs); + static void bulkClearNotesOutOfBounds(QVector clipvs); + public slots: lmms::MidiClip* getMidiClip(); void update() override; @@ -76,6 +81,7 @@ protected slots: void resetName(); void changeName(); void transposeSelection(); + void clearNotesOutOfBounds(); protected: @@ -103,6 +109,10 @@ private: QStaticText m_staticTextName; bool m_legacySEPattern; + + bool isResizableBeforeStart() override { return false; } + + bool destructiveSplitClip(const TimePos pos) override; } ; diff --git a/include/Note.h b/include/Note.h index 08cbce3db..7d79eeecd 100644 --- a/include/Note.h +++ b/include/Note.h @@ -26,6 +26,7 @@ #ifndef LMMS_NOTE_H #define LMMS_NOTE_H +#include #include #include @@ -107,6 +108,8 @@ public: Note( const Note & note ); ~Note() override; + Note& operator=(const Note& note); + // Note types enum class Type { @@ -236,7 +239,7 @@ public: DetuningHelper * detuning() const { - return m_detuning; + return m_detuning.get(); } bool hasDetuningInfo() const; bool withinRange(int tickStart, int tickEnd) const; @@ -262,7 +265,7 @@ private: panning_t m_panning; TimePos m_length; TimePos m_pos; - DetuningHelper * m_detuning; + std::unique_ptr m_detuning; Type m_type = Type::Regular; }; diff --git a/include/PatternClip.h b/include/PatternClip.h index 968a0b198..0be217407 100644 --- a/include/PatternClip.h +++ b/include/PatternClip.h @@ -51,6 +51,11 @@ public: gui::ClipView * createView( gui::TrackView * _tv ) override; + PatternClip* clone() override + { + return new PatternClip(*this); + } + private: friend class PatternClipView; } ; diff --git a/include/PianoRoll.h b/include/PianoRoll.h index 00bdbaeb9..96557f33d 100644 --- a/include/PianoRoll.h +++ b/include/PianoRoll.h @@ -90,6 +90,7 @@ class PianoRoll : public QWidget Q_PROPERTY(int ghostNoteOpacity MEMBER m_ghostNoteOpacity) Q_PROPERTY(bool ghostNoteBorders MEMBER m_ghostNoteBorders) Q_PROPERTY(QColor backgroundShade MEMBER m_backgroundShade) + Q_PROPERTY(QColor outOfBoundsShade MEMBER m_outOfBoundsShade) /* white key properties */ Q_PROPERTY(int whiteKeyWidth MEMBER m_whiteKeyWidth) @@ -516,6 +517,7 @@ private: bool m_noteBorders; bool m_ghostNoteBorders; QColor m_backgroundShade; + QColor m_outOfBoundsShade; /* white key properties */ int m_whiteKeyWidth; QColor m_whiteKeyActiveTextColor; diff --git a/include/SampleClip.h b/include/SampleClip.h index 3beca338b..cbd3ac5d5 100644 --- a/include/SampleClip.h +++ b/include/SampleClip.h @@ -49,7 +49,6 @@ class SampleClip : public Clip public: SampleClip(Track* track, Sample sample, bool isPlaying); SampleClip(Track* track); - SampleClip( const SampleClip& orig ); ~SampleClip() override; SampleClip& operator=( const SampleClip& that ) = delete; @@ -81,6 +80,11 @@ public: void setIsPlaying(bool isPlaying); void setSampleBuffer(std::shared_ptr sb); + SampleClip* clone() override + { + return new SampleClip(*this); + } + public slots: void setSampleFile(const QString& sf); void updateLength(); @@ -88,6 +92,8 @@ public slots: void playbackPositionChanged(); void updateTrackClips(); +protected: + SampleClip( const SampleClip& orig ); private: Sample m_sample; diff --git a/include/SampleClipView.h b/include/SampleClipView.h index 14f9a8235..4ab4d77e7 100644 --- a/include/SampleClipView.h +++ b/include/SampleClipView.h @@ -68,7 +68,6 @@ private: SampleThumbnail m_sampleThumbnail; QPixmap m_paintPixmap; long m_paintPixmapXPosition; - bool splitClip( const TimePos pos ) override; } ; diff --git a/src/core/AutomatableModel.cpp b/src/core/AutomatableModel.cpp index e006be651..fa523e106 100644 --- a/src/core/AutomatableModel.cpp +++ b/src/core/AutomatableModel.cpp @@ -754,7 +754,7 @@ float AutomatableModel::globalAutomationValueAt( const TimePos& time ) if( latestClip ) { // scale/fit the value appropriately and return it - const float value = latestClip->valueAt( time - latestClip->startPosition() ); + const float value = latestClip->valueAt(time - latestClip->startPosition() + latestClip->startTimeOffset()); const float scaled_value = scaledValue( value ); return fittedValue( scaled_value ); } diff --git a/src/core/AutomationClip.cpp b/src/core/AutomationClip.cpp index ef57a60d5..ba2ffe1f3 100644 --- a/src/core/AutomationClip.cpp +++ b/src/core/AutomationClip.cpp @@ -65,13 +65,13 @@ AutomationClip::AutomationClip( AutomationTrack * _auto_track ) : switch( getTrack()->trackContainer()->type() ) { case TrackContainer::Type::Pattern: - setAutoResize( true ); + setResizable(false); break; case TrackContainer::Type::Song: // move down default: - setAutoResize( false ); + setResizable(true); break; } } @@ -81,14 +81,17 @@ AutomationClip::AutomationClip( AutomationTrack * _auto_track ) : AutomationClip::AutomationClip( const AutomationClip & _clip_to_copy ) : - Clip( _clip_to_copy.m_autoTrack ), + Clip(_clip_to_copy), #if (QT_VERSION < QT_VERSION_CHECK(5,14,0)) m_clipMutex(QMutex::Recursive), #endif m_autoTrack( _clip_to_copy.m_autoTrack ), m_objects( _clip_to_copy.m_objects ), m_tension( _clip_to_copy.m_tension ), - m_progressionType( _clip_to_copy.m_progressionType ) + m_progressionType(_clip_to_copy.m_progressionType), + m_dragging(false), + m_isRecording(_clip_to_copy.m_isRecording), + m_lastRecordedValue(0) { // Locks the mutex of the copied AutomationClip to make sure it // doesn't change while it's being copied @@ -106,13 +109,13 @@ AutomationClip::AutomationClip( const AutomationClip & _clip_to_copy ) : switch( getTrack()->trackContainer()->type() ) { case TrackContainer::Type::Pattern: - setAutoResize( true ); + setResizable(false); break; case TrackContainer::Type::Song: // move down default: - setAutoResize( false ); + setResizable(true); break; } } @@ -225,8 +228,15 @@ TimePos AutomationClip::timeMapLength() const void AutomationClip::updateLength() { - // Do not resize down in case user manually extended up - changeLength(std::max(length(), timeMapLength())); + // Technically it only matters if the clip has been resized from the right, but this + // checks if it has been resized from either direction. + if (getAutoResize()) + { + // Using 1 bar as the min length for an un-resized clip. + // This does not prevent the user from resizing the clip to be less than a bar later on. + changeLength(std::max(TimePos::ticksPerBar(), static_cast(timeMapLength()))); + setStartTimeOffset(TimePos(0)); + } } @@ -253,6 +263,7 @@ TimePos AutomationClip::putValue( cleanObjects(); TimePos newTime = quantPos ? Note::quantized(time, quantization()) : time; + newTime = std::max(TimePos(0), newTime); // Create a node or replace the existing one on newTime m_timeMap[newTime] = AutomationNode(this, value, newTime); @@ -308,6 +319,7 @@ TimePos AutomationClip::putValues( cleanObjects(); TimePos newTime = quantPos ? Note::quantized(time, quantization()) : time; + newTime = std::max(TimePos(0), newTime); // Create a node or replace the existing one on newTime m_timeMap[newTime] = AutomationNode(this, inValue, outValue, newTime); @@ -455,12 +467,12 @@ void AutomationClip::recordValue(TimePos time, float value) if( value != m_lastRecordedValue ) { - putValue( time, value, true ); + putValue(time - startTimeOffset(), value, true); m_lastRecordedValue = value; } - else if( valueAt( time ) != value ) + else if( valueAt(time - startTimeOffset()) != value ) { - removeNode(time); + removeNode(time - startTimeOffset()); } } @@ -721,90 +733,61 @@ void AutomationClip::flipY() -void AutomationClip::flipX(int length) +void AutomationClip::flipX(int start, int end) { QMutexLocker m(&m_clipMutex); - timeMap::const_iterator it = m_timeMap.lowerBound(0); + timeMap::const_iterator firstIterator = m_timeMap.lowerBound(0); - if (it == m_timeMap.end()) { return; } + if (firstIterator == m_timeMap.end()) { return; } + + if (start == -1 && end == -1) { start = 0; end = length() - startTimeOffset(); } + else if (!(end >= 0 && start >= 0 && end > start)) { return; } // Temporary map where we will store the flipped version // of our clip timeMap tempMap; - float tempValue = 0; - float tempOutValue = 0; - - // We know the QMap isn't empty, making this safe: - float realLength = m_timeMap.lastKey(); - - // If we have a positive length, we want to flip the area covered by that - // length, even if it goes beyond the clip. A negative length means that - // we just want to flip the nodes we have - if (length >= 0 && length != realLength) + for (auto it = m_timeMap.begin(); it != m_timeMap.end(); ++it) { - // If length to be flipped is bigger than the real length - if (realLength < length) + if (POS(it) < start || POS(it) > end) { - // We are flipping an area that goes beyond the last node. So we add a node to the - // beginning of the flipped timeMap representing the value of the end of the area - tempValue = valueAt(length); - tempMap[0] = AutomationNode(this, tempValue, 0); - - // Now flip the nodes we have in relation to the length - do - { - // We swap the inValue and outValue when flipping horizontally - tempValue = OUTVAL(it); - tempOutValue = INVAL(it); - auto newTime = TimePos(length - POS(it)); - - tempMap[newTime] = AutomationNode(this, tempValue, tempOutValue, newTime); - - ++it; - } while (it != m_timeMap.end()); + tempMap[POS(it)] = *it; } - else // If the length to be flipped is smaller than the real length + else { - do + // If the first node in the clip is not at the start, it can break things when clipping, so + // we have to set its in value to 0. + if (it == firstIterator && POS(firstIterator) > 0) { - TimePos newTime; - - // Only flips the length to be flipped and keep the remaining values in place - // We also only swap the inValue and outValue if we are flipping the node - if (POS(it) <= length) - { - newTime = length - POS(it); - tempValue = OUTVAL(it); - tempOutValue = INVAL(it); - } - else - { - newTime = POS(it); - tempValue = INVAL(it); - tempOutValue = OUTVAL(it); - } - - tempMap[newTime] = AutomationNode(this, tempValue, tempOutValue, newTime); - - ++it; - } while (it != m_timeMap.end()); + tempMap[end - (POS(it) - start)] = AutomationNode(this, OUTVAL(it), 0, end - (POS(it) - start)); + } + else + { + tempMap[end - (POS(it) - start)] = AutomationNode(this, OUTVAL(it), INVAL(it), end - (POS(it) - start)); + } } } - else // Length to be flipped is the same as the real length + + if (m_timeMap.contains(start) && m_timeMap.contains(end)) { - do - { - // Swap the inValue and outValue - tempValue = OUTVAL(it); - tempOutValue = INVAL(it); - - auto newTime = TimePos(realLength - POS(it)); - tempMap[newTime] = AutomationNode(this, tempValue, tempOutValue, newTime); - - ++it; - } while (it != m_timeMap.end()); + tempMap[start] = AutomationNode(this, m_timeMap[start].getInValue(), m_timeMap[end].getInValue(), start); + tempMap[end] = AutomationNode(this, m_timeMap[start].getOutValue(), m_timeMap[end].getOutValue(), end); + } + else if (m_timeMap.contains(start)) + { + tempMap[start] = AutomationNode(this, m_timeMap[start].getInValue(), valueAt(end), start); + tempMap[end] = AutomationNode(this, m_timeMap[start].getOutValue(), valueAt(end), end); + } + else if (m_timeMap.contains(end)) + { + tempMap[start] = AutomationNode(this, valueAt(start), m_timeMap[end].getInValue(), start); + tempMap[end] = AutomationNode(this, valueAt(start), m_timeMap[end].getOutValue(), end); + } + else + { + tempMap[start] = AutomationNode(this, valueAt(start), valueAt(end), start); + tempMap[end] = AutomationNode(this, valueAt(start), valueAt(end), end); } m_timeMap.clear(); @@ -830,6 +813,8 @@ void AutomationClip::saveSettings( QDomDocument & _doc, QDomElement & _this ) _this.setAttribute( "prog", QString::number( static_cast(progressionType()) ) ); _this.setAttribute( "tens", QString::number( getTension() ) ); _this.setAttribute( "mute", QString::number( isMuted() ) ); + _this.setAttribute("off", startTimeOffset()); + _this.setAttribute("autoresize", QString::number(getAutoResize())); if (const auto& c = color()) { @@ -880,6 +865,8 @@ void AutomationClip::loadSettings( const QDomElement & _this ) "prog" ).toInt() ) ); setTension( _this.attribute( "tens" ) ); setMuted(_this.attribute( "mute", QString::number( false ) ).toInt() ); + setAutoResize(_this.attribute("autoresize").toInt()); + setStartTimeOffset(_this.attribute("off").toInt()); for( QDomNode node = _this.firstChild(); !node.isNull(); node = node.nextSibling() ) diff --git a/src/core/Clip.cpp b/src/core/Clip.cpp index b18391df1..ea7d1f933 100644 --- a/src/core/Clip.cpp +++ b/src/core/Clip.cpp @@ -61,7 +61,30 @@ Clip::Clip( Track * track ) : } - +/*! \brief Copy a Clip + * + * Creates a duplicate clip of the one provided. + * + * \param other The clip object which will be copied. + */ +Clip::Clip(const Clip& other): + Model(other.m_track), + m_track(other.m_track), + m_name(other.m_name), + m_startPosition(other.m_startPosition), + m_length(other.m_length), + m_startTimeOffset(other.m_startTimeOffset), + m_mutedModel(other.m_mutedModel.value(), this, tr( "Mute" )), + m_resizable(other.m_resizable), + m_autoResize(other.m_autoResize), + m_selectViewOnCreate{other.m_selectViewOnCreate}, + m_color(other.m_color) +{ + if (getTrack()) + { + getTrack()->addClip(this); + } +} /*! \brief Destroy a Clip * diff --git a/src/core/Note.cpp b/src/core/Note.cpp index ed3a00f10..167d75f30 100644 --- a/src/core/Note.cpp +++ b/src/core/Note.cpp @@ -46,12 +46,11 @@ Note::Note( const TimePos & length, const TimePos & pos, m_volume(std::clamp(volume, MinVolume, MaxVolume)), m_panning(std::clamp(panning, PanningLeft, PanningRight)), m_length( length ), - m_pos( pos ), - m_detuning( nullptr ) + m_pos( pos ) { - if( detuning ) + if (detuning) { - m_detuning = sharedObject::ref( detuning ); + m_detuning = std::make_unique(*detuning); } else { @@ -74,24 +73,41 @@ Note::Note( const Note & note ) : m_panning( note.m_panning ), m_length( note.m_length ), m_pos( note.m_pos ), - m_detuning(nullptr), m_type(note.m_type) { - if( note.m_detuning ) + if (note.m_detuning) { - m_detuning = sharedObject::ref( note.m_detuning ); + m_detuning = std::make_unique(*note.m_detuning); } } +Note& Note::operator=(const Note& note) +{ + m_selected = note.m_selected; + m_oldKey = note.m_oldKey; + m_oldPos = note.m_oldPos; + m_oldLength = note.m_oldLength; + m_isPlaying = note.m_isPlaying; + m_key = note.m_key; + m_volume = note.m_volume; + m_panning = note.m_panning; + m_length = note.m_length; + m_pos = note.m_pos; + m_type = note.m_type; + + if (note.m_detuning) + { + m_detuning = std::make_unique(*note.m_detuning); + } + + return *this; +} + Note::~Note() { - if( m_detuning ) - { - sharedObject::unref( m_detuning ); - } } @@ -218,7 +234,7 @@ void Note::createDetuning() { if( m_detuning == nullptr ) { - m_detuning = new DetuningHelper; + m_detuning = std::make_unique(); (void) m_detuning->automationClip(); m_detuning->setRange( -MaxDetuning, MaxDetuning, 0.5f ); m_detuning->automationClip()->setProgressionType( AutomationClip::ProgressionType::Linear ); diff --git a/src/core/PatternClip.cpp b/src/core/PatternClip.cpp index 15a1d1f54..01bd48336 100644 --- a/src/core/PatternClip.cpp +++ b/src/core/PatternClip.cpp @@ -45,7 +45,7 @@ PatternClip::PatternClip(Track* track) : changeLength( TimePos( t, 0 ) ); restoreJournallingState(); } - setAutoResize( false ); + setResizable(true); } void PatternClip::saveSettings(QDomDocument& doc, QDomElement& element) @@ -62,6 +62,7 @@ void PatternClip::saveSettings(QDomDocument& doc, QDomElement& element) element.setAttribute( "len", length() ); element.setAttribute("off", startTimeOffset()); element.setAttribute( "muted", isMuted() ); + element.setAttribute("autoresize", QString::number(getAutoResize())); if (const auto& c = color()) { element.setAttribute("color", c->name()); @@ -79,6 +80,7 @@ void PatternClip::loadSettings(const QDomElement& element) movePosition( element.attribute( "pos" ).toInt() ); } changeLength( element.attribute( "len" ).toInt() ); + setAutoResize(element.attribute("autoresize").toInt()); setStartTimeOffset(element.attribute("off").toInt()); if (static_cast(element.attribute("muted").toInt()) != isMuted()) { diff --git a/src/core/SampleClip.cpp b/src/core/SampleClip.cpp index 5ef001e20..06e1af99d 100644 --- a/src/core/SampleClip.cpp +++ b/src/core/SampleClip.cpp @@ -70,13 +70,13 @@ SampleClip::SampleClip(Track* _track, Sample sample, bool isPlaying) switch( getTrack()->trackContainer()->type() ) { case TrackContainer::Type::Pattern: - setAutoResize( true ); + setResizable(false); break; case TrackContainer::Type::Song: // move down default: - setAutoResize( false ); + setResizable(true); break; } updateTrackClips(); @@ -88,8 +88,48 @@ SampleClip::SampleClip(Track* track) } SampleClip::SampleClip(const SampleClip& orig) : - SampleClip(orig.getTrack(), orig.m_sample, orig.m_isPlaying) + Clip(orig), + m_sample(std::move(orig.m_sample)), + m_isPlaying(orig.m_isPlaying) { + saveJournallingState( false ); + setSampleFile( "" ); + restoreJournallingState(); + + // we need to receive bpm-change-events, because then we have to + // change length of this Clip + connect( Engine::getSong(), SIGNAL(tempoChanged(lmms::bpm_t)), + this, SLOT(updateLength()), Qt::DirectConnection ); + connect( Engine::getSong(), SIGNAL(timeSignatureChanged(int,int)), + this, SLOT(updateLength())); + + //playbutton clicked or space key / on Export Song set isPlaying to false + connect( Engine::getSong(), SIGNAL(playbackStateChanged()), + this, SLOT(playbackPositionChanged()), Qt::DirectConnection ); + //care about loops and jumps + connect( Engine::getSong(), SIGNAL(updateSampleTracks()), + this, SLOT(playbackPositionChanged()), Qt::DirectConnection ); + //care about mute Clips + connect( this, SIGNAL(dataChanged()), this, SLOT(playbackPositionChanged())); + //care about mute track + connect( getTrack()->getMutedModel(), SIGNAL(dataChanged()), + this, SLOT(playbackPositionChanged()), Qt::DirectConnection ); + //care about Clip position + connect( this, SIGNAL(positionChanged()), this, SLOT(updateTrackClips())); + + switch( getTrack()->trackContainer()->type() ) + { + case TrackContainer::Type::Pattern: + setResizable(false); + break; + + case TrackContainer::Type::Song: + // move down + default: + setResizable(true); + break; + } + updateTrackClips(); } @@ -267,6 +307,7 @@ void SampleClip::saveSettings( QDomDocument & _doc, QDomElement & _this ) _this.setAttribute( "muted", isMuted() ); _this.setAttribute( "src", sampleFile() ); _this.setAttribute( "off", startTimeOffset() ); + _this.setAttribute("autoresize", QString::number(getAutoResize())); if( sampleFile() == "" ) { QString s; @@ -315,6 +356,7 @@ void SampleClip::loadSettings( const QDomElement & _this ) changeLength( _this.attribute( "len" ).toInt() ); setMuted( _this.attribute( "muted" ).toInt() ); setStartTimeOffset( _this.attribute( "off" ).toInt() ); + setAutoResize(_this.attribute("autoresize").toInt()); if (_this.hasAttribute("color")) { diff --git a/src/core/Song.cpp b/src/core/Song.cpp index c2e027003..ad689107b 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -290,7 +290,7 @@ void Song::processNextBuffer() } else if (m_playMode == PlayMode::MidiClip && m_loopMidiClip && !loopEnabled) { - enforceLoop(TimePos{0}, m_midiClipToPlay->length()); + enforceLoop(-m_midiClipToPlay->startTimeOffset(), m_midiClipToPlay->length() - m_midiClipToPlay->startTimeOffset()); } // Handle loop points, and inform VST plugins of the loop status @@ -663,7 +663,14 @@ void Song::stop() switch (timeline.stopBehaviour()) { case Timeline::StopBehaviour::BackToZero: - getPlayPos().setTicks(0); + if (m_playMode == PlayMode::MidiClip) + { + getPlayPos().setTicks(std::max(0, -m_midiClipToPlay->startTimeOffset())); + } + else + { + getPlayPos().setTicks(0); + } m_elapsedMilliSeconds[static_cast(m_playMode)] = 0; break; diff --git a/src/core/TrackContainer.cpp b/src/core/TrackContainer.cpp index d4120e761..c92d6edf0 100644 --- a/src/core/TrackContainer.cpp +++ b/src/core/TrackContainer.cpp @@ -297,9 +297,9 @@ AutomatedValueMap TrackContainer::automatedValuesFromTracks(const TrackList &tra if (! p->hasAutomation()) { continue; } - TimePos relTime = time - p->startPosition(); - if (! p->getAutoResize()) { - relTime = std::min(relTime, p->length()); + TimePos relTime = time - p->startPosition() - p->startTimeOffset(); + if (p->getResizable()) { + relTime = std::min(static_cast(relTime), p->length() - p->startTimeOffset()); } float value = p->valueAt(relTime); diff --git a/src/gui/clips/AutomationClipView.cpp b/src/gui/clips/AutomationClipView.cpp index e098710d9..89fbd4f8e 100644 --- a/src/gui/clips/AutomationClipView.cpp +++ b/src/gui/clips/AutomationClipView.cpp @@ -23,6 +23,8 @@ */ #include "AutomationClipView.h" +#include + #include #include #include @@ -37,6 +39,8 @@ #include "StringPairDrag.h" #include "TextFloat.h" #include "Track.h" +#include "TrackContainerView.h" +#include "TrackView.h" #include "Engine.h" @@ -149,7 +153,7 @@ void AutomationClipView::flipY() void AutomationClipView::flipX() { - m_clip->flipX( m_clip->length() ); + m_clip->flipX(std::max(0, -m_clip->startTimeOffset()), std::max(0, m_clip->length() - m_clip->startTimeOffset())); update(); } @@ -183,6 +187,7 @@ void AutomationClipView::constructContextMenu( QMenu * _cm ) _cm->addAction( embed::getIconPixmap( "flip_x" ), tr( "Flip Horizontally (Visible)" ), this, SLOT(flipX())); + if (!m_clip->m_objects.empty()) { _cm->addSeparator(); @@ -207,6 +212,8 @@ void AutomationClipView::constructContextMenu( QMenu * _cm ) void AutomationClipView::mouseDoubleClickEvent( QMouseEvent * me ) { + if (m_trackView->trackContainerView()->knifeMode()) { return; } + if(me->button() != Qt::LeftButton) { me->ignore(); @@ -269,6 +276,7 @@ void AutomationClipView::paintEvent( QPaintEvent * ) const float y_scale = max - min; const float h = ( height() - 2 * BORDER_WIDTH ) / y_scale; const float ppTick = ppb / TimePos::ticksPerBar(); + const int offset = m_clip->startTimeOffset() * ppTick; p.translate( 0.0f, max * height() / y_scale - BORDER_WIDTH ); p.scale( 1.0f, -h ); @@ -289,7 +297,7 @@ void AutomationClipView::paintEvent( QPaintEvent * ) { if( it+1 == m_clip->getTimeMap().end() ) { - const float x1 = POS(it) * ppTick; + const float x1 = POS(it) * ppTick + offset; const auto x2 = (float)(width() - BORDER_WIDTH); if( x1 > ( width() - BORDER_WIDTH ) ) break; // We are drawing the space after the last node, so we use the outValue @@ -317,20 +325,19 @@ void AutomationClipView::paintEvent( QPaintEvent * ) : INVAL(it + 1); QPainterPath path; - QPointF origin = QPointF(POS(it) * ppTick, 0.0f); - path.moveTo( origin ); - path.moveTo(QPointF(POS(it) * ppTick,values[0])); + QPointF origin = QPointF(POS(it) * ppTick + offset, 0.0f); + path.moveTo(origin); + path.moveTo(QPointF(POS(it) * ppTick + offset, values[0])); for (int i = POS(it) + 1; i < POS(it + 1); i++) { - float x = i * ppTick; - if( x > ( width() - BORDER_WIDTH ) ) break; + float x = i * ppTick + offset; + if(x > (width() - BORDER_WIDTH)) break; float value = values[i - POS(it)]; - path.lineTo( QPointF( x, value ) ); - + path.lineTo(QPointF(x, value)); } - path.lineTo((POS(it + 1)) * ppTick, nextValue); - path.lineTo((POS(it + 1)) * ppTick, 0.0f); - path.lineTo( origin ); + path.lineTo((POS(it + 1)) * ppTick + offset, nextValue); + path.lineTo((POS(it + 1)) * ppTick + offset, 0.0f); + path.lineTo(origin); if( gradient() ) { @@ -355,10 +362,10 @@ void AutomationClipView::paintEvent( QPaintEvent * ) const int bx = BORDER_WIDTH + static_cast(ppb * b) - 2; //top line - p.drawLine(bx, BORDER_WIDTH, bx, BORDER_WIDTH + lineSize); + p.drawLine(bx + offset, BORDER_WIDTH, bx + offset, BORDER_WIDTH + lineSize); //bottom line - p.drawLine(bx, rect().bottom() - (lineSize + BORDER_WIDTH), bx, rect().bottom() - BORDER_WIDTH); + p.drawLine(bx + offset, rect().bottom() - (lineSize + BORDER_WIDTH), bx + offset, rect().bottom() - BORDER_WIDTH); } // recording icon for when recording automation @@ -388,6 +395,12 @@ void AutomationClipView::paintEvent( QPaintEvent * ) p.drawPixmap( spacing, height() - ( size + spacing ), embed::getIconPixmap( "muted", size, size ) ); } + + if (m_marker) + { + p.setPen(markerColor()); + p.drawLine(m_markerPos, rect().bottom(), m_markerPos, rect().top()); + } p.end(); @@ -484,5 +497,4 @@ void AutomationClipView::scaleTimemapToFit( float oldMin, float oldMax ) m_clip->generateTangents(); } - } // namespace lmms::gui diff --git a/src/gui/clips/ClipView.cpp b/src/gui/clips/ClipView.cpp index 6c3953cf5..89d5a5e79 100644 --- a/src/gui/clips/ClipView.cpp +++ b/src/gui/clips/ClipView.cpp @@ -101,6 +101,7 @@ ClipView::ClipView( Clip * clip, m_textShadowColor( 0, 0, 0 ), m_patternClipBackground( 0, 0, 0 ), m_gradient( true ), + m_markerColor(0, 0, 0), m_mouseHotspotHand( 0, 0 ), m_mouseHotspotKnife( 0, 0 ), m_cursorHand( QCursor( embed::getIconPixmap( "hand" ) ) ), @@ -232,6 +233,9 @@ QColor ClipView::patternClipBackground() const bool ClipView::gradient() const { return m_gradient; } +QColor ClipView::markerColor() const +{ return m_markerColor; } + //! \brief CSS theming qproperty access method void ClipView::setMutedColor( const QColor & c ) { m_mutedColor = QColor( c ); } @@ -259,6 +263,9 @@ void ClipView::setPatternClipBackground( const QColor & c ) void ClipView::setGradient( const bool & b ) { m_gradient = b; } +void ClipView::setMarkerColor(const QColor & c) +{ m_markerColor = QColor(c); } + // access needsUpdate member variable bool ClipView::needsUpdate() { return m_needsUpdate; } @@ -494,17 +501,14 @@ void ClipView::dropEvent( QDropEvent * de ) */ void ClipView::updateCursor(QMouseEvent * me) { - auto sClip = dynamic_cast(m_clip); - auto pClip = dynamic_cast(m_clip); - // If we are at the edges, use the resize cursor - if (!me->buttons() && !m_clip->getAutoResize() && !isSelected() - && ((me->x() > width() - RESIZE_GRIP_WIDTH) || (me->x() < RESIZE_GRIP_WIDTH && (sClip || pClip)))) + if (!me->buttons() && m_clip->getResizable() && !isSelected() + && ((me->x() > width() - RESIZE_GRIP_WIDTH) || (me->x() < RESIZE_GRIP_WIDTH))) { setCursor(Qt::SizeHorCursor); } // If we are in the middle on knife mode, use the knife cursor - else if (sClip && m_trackView->trackContainerView()->knifeMode() && !isSelected()) + else if (m_trackView->trackContainerView()->knifeMode() && !isSelected()) { setCursor(m_cursorKnife); } @@ -631,11 +635,9 @@ void ClipView::mousePressEvent( QMouseEvent * me ) setInitialOffsets(); if( !fixedClips() && me->button() == Qt::LeftButton ) { - auto sClip = dynamic_cast(m_clip); - auto pClip = dynamic_cast(m_clip); const bool knifeMode = m_trackView->trackContainerView()->knifeMode(); - if (me->modifiers() & KBD_COPY_MODIFIER && !(sClip && knifeMode)) + if (me->modifiers() & KBD_COPY_MODIFIER && !knifeMode) { if( isSelected() ) { @@ -667,7 +669,7 @@ void ClipView::mousePressEvent( QMouseEvent * me ) setInitialPos( me->pos() ); setInitialOffsets(); - if( m_clip->getAutoResize() ) + if (!m_clip->getResizable() && !knifeMode) { // Always move clips that can't be manually resized m_action = Action::Move; setCursor( Qt::SizeAllCursor ); @@ -677,12 +679,12 @@ void ClipView::mousePressEvent( QMouseEvent * me ) m_action = Action::Resize; setCursor( Qt::SizeHorCursor ); } - else if( me->x() < RESIZE_GRIP_WIDTH && (sClip || pClip) ) + else if (me->x() < RESIZE_GRIP_WIDTH) { m_action = Action::ResizeLeft; setCursor( Qt::SizeHorCursor ); } - else if( sClip && knifeMode ) + else if (knifeMode) { m_action = Action::Split; setCursor( m_cursorKnife ); @@ -725,9 +727,21 @@ void ClipView::mousePressEvent( QMouseEvent * me ) } delete m_hint; - QString hint = m_action == Action::Move || m_action == Action::MoveSelection - ? tr( "Press <%1> and drag to make a copy." ) - : tr( "Press <%1> for free resizing." ); + QString hint; + if (m_action == Action::Move || m_action == Action::MoveSelection) + { + hint = tr("Press <%1> and drag to make a copy."); + } + else if (m_action == Action::Split) + { + hint = dynamic_cast(this) + ? tr("Press <%1> or for unquantized splitting.\nPress for destructive splitting.") + : tr("Press <%1> or for unquantized splitting."); + } + else + { + hint = tr("Press <%1> or for unquantized resizing."); + } m_hint = TextFloat::displayMessage( tr( "Hint" ), hint.arg(UI_COPY_KEY), embed::getIconPixmap( "hint" ), 0 ); } @@ -745,12 +759,8 @@ void ClipView::mousePressEvent( QMouseEvent * me ) if (m_action == Action::Split) { m_action = Action::None; - auto sClip = dynamic_cast(m_clip); - if (sClip) - { - setMarkerEnabled( false ); - update(); - } + setMarkerEnabled(false); + update(); } } else if( me->button() == Qt::MiddleButton ) @@ -893,6 +903,7 @@ void ClipView::mouseMoveEvent( QMouseEvent * me ) setInitialPos( m_initialMousePos ); // Don't resize to less than 1 tick m_clip->changeLength( qMax( 1, l ) ); + m_clip->setAutoResize(false); } else if ( me->modifiers() & Qt::ShiftModifier ) { // If shift is held, quantize clip's end position @@ -901,6 +912,7 @@ void ClipView::mouseMoveEvent( QMouseEvent * me ) TimePos min = m_initialClipPos.quantize( snapSize ); if ( min <= m_initialClipPos ) min += snapLength; m_clip->changeLength( qMax(min - m_initialClipPos, end - m_initialClipPos) ); + m_clip->setAutoResize(false); } else { // Otherwise, resize in fixed increments @@ -910,66 +922,70 @@ void ClipView::mouseMoveEvent( QMouseEvent * me ) auto min = TimePos(initialLength % snapLength); if (min < 1) min += snapLength; m_clip->changeLength( qMax( min, initialLength + offset) ); + m_clip->setAutoResize(false); } } else { - auto sClip = dynamic_cast(m_clip); auto pClip = dynamic_cast(m_clip); - if( sClip || pClip ) + + const int x = mapToParent( me->pos() ).x() - m_initialMousePos.x(); + + TimePos t = qMax( 0, (int) + m_trackView->trackContainerView()->currentPosition() + + static_cast( x * TimePos::ticksPerBar() / ppb ) ); + + if (!isResizableBeforeStart()) { - const int x = mapToParent( me->pos() ).x() - m_initialMousePos.x(); + t = std::max(t, static_cast(m_clip->startPosition() + m_clip->startTimeOffset())); + } - TimePos t = qMax( 0, (int) - m_trackView->trackContainerView()->currentPosition() + - static_cast( x * TimePos::ticksPerBar() / ppb ) ); + if( unquantizedModHeld(me) ) + { // We want to preserve this adjusted offset, + // even if the user switches to snapping later + setInitialPos( m_initialMousePos ); + //Don't resize to less than 1 tick + t = qMin( m_initialClipEnd - 1, t); + } + else if( me->modifiers() & Qt::ShiftModifier ) + { // If shift is held, quantize clip's start position + // Don't let the start position move past the end position + TimePos max = m_initialClipEnd.quantize( snapSize ); + if ( max >= m_initialClipEnd ) max -= snapLength; + t = qMin( max, t.quantize( snapSize ) ); + } + else + { // Otherwise, resize in fixed increments + // Don't resize to less than 1 tick + TimePos initialLength = m_initialClipEnd - m_initialClipPos; + auto minLength = TimePos(initialLength % snapLength); + if (minLength < 1) minLength += snapLength; + TimePos offset = TimePos(t - m_initialClipPos).quantize( snapSize ); + t = qMin( m_initialClipEnd - minLength, m_initialClipPos + offset ); + } - if( unquantizedModHeld(me) ) - { // We want to preserve this adjusted offset, - // even if the user switches to snapping later - setInitialPos( m_initialMousePos ); - //Don't resize to less than 1 tick - t = qMin( m_initialClipEnd - 1, t); - } - else if( me->modifiers() & Qt::ShiftModifier ) - { // If shift is held, quantize clip's start position - // Don't let the start position move past the end position - TimePos max = m_initialClipEnd.quantize( snapSize ); - if ( max >= m_initialClipEnd ) max -= snapLength; - t = qMin( max, t.quantize( snapSize ) ); + TimePos positionOffset = m_clip->startPosition() - t; + if (m_clip->length() + positionOffset >= 1) + { + m_clip->movePosition(t); + m_clip->changeLength(m_clip->length() + positionOffset); + if (pClip) + { + // Modulus the start time offset as we need it only for offsets + // inside the pattern length. This is done to prevent a value overflow. + // The start time offset may still become larger than the pattern length + // whenever the pattern length decreases without a clip resize following. + // To deal safely with it, always modulus before use. + tick_t patternLength = Engine::patternStore()->lengthOfPattern(pClip->patternIndex()) + * TimePos::ticksPerBar(); + TimePos position = (pClip->startTimeOffset() + positionOffset) % patternLength; + pClip->setStartTimeOffset(position); } else - { // Otherwise, resize in fixed increments - // Don't resize to less than 1 tick - TimePos initialLength = m_initialClipEnd - m_initialClipPos; - auto minLength = TimePos(initialLength % snapLength); - if (minLength < 1) minLength += snapLength; - TimePos offset = TimePos(t - m_initialClipPos).quantize( snapSize ); - t = qMin( m_initialClipEnd - minLength, m_initialClipPos + offset ); - } - - TimePos positionOffset = m_clip->startPosition() - t; - if (m_clip->length() + positionOffset >= 1) { - m_clip->movePosition(t); - m_clip->changeLength(m_clip->length() + positionOffset); - if (sClip) - { - sClip->setStartTimeOffset(sClip->startTimeOffset() + positionOffset); - } - else if (pClip) - { - // Modulus the start time offset as we need it only for offsets - // inside the pattern length. This is done to prevent a value overflow. - // The start time offset may still become larger than the pattern length - // whenever the pattern length decreases without a clip resize following. - // To deal safely with it, always modulus before use. - tick_t patternLength = Engine::patternStore()->lengthOfPattern(pClip->patternIndex()) - * TimePos::ticksPerBar(); - TimePos position = (pClip->startTimeOffset() + positionOffset) % patternLength; - pClip->setStartTimeOffset(position); - } + m_clip->setStartTimeOffset(m_clip->startTimeOffset() + positionOffset); } + m_clip->setAutoResize(false); } } s_textFloat->setText( tr( "%1:%2 (%3:%4 to %5:%6)" ). @@ -986,11 +1002,8 @@ void ClipView::mouseMoveEvent( QMouseEvent * me ) } else if( m_action == Action::Split ) { - auto sClip = dynamic_cast(m_clip); - if (sClip) { - setCursor( m_cursorKnife ); - setMarkerPos( knifeMarkerPos( me ) ); - } + setCursor(m_cursorKnife); + setMarkerPos(knifeMarkerPos(me)); update(); } // None of the actions above, we will just handle the cursor @@ -1027,10 +1040,15 @@ void ClipView::mouseReleaseEvent( QMouseEvent * me ) { const float ppb = m_trackView->trackContainerView()->pixelsPerBar(); const TimePos relPos = me->pos().x() * TimePos::ticksPerBar() / ppb; - splitClip(unquantizedModHeld(me) ? - relPos : - quantizeSplitPos(relPos, me->modifiers() & Qt::ShiftModifier) - ); + if (me->modifiers() & Qt::ShiftModifier) + { + destructiveSplitClip(unquantizedModHeld(me) ? relPos : quantizeSplitPos(relPos)); + } + else + { + splitClip(unquantizedModHeld(me) ? relPos : quantizeSplitPos(relPos)); + } + setMarkerEnabled(false); } m_action = Action::None; @@ -1083,15 +1101,6 @@ void ClipView::contextMenuEvent( QContextMenuEvent * cme ) ? tr("Cut") : tr("Cut selection"), [this](){ contextMenuAction( ContextMenuAction::Cut ); } ); - - if (canMergeSelection(selectedClips)) - { - contextMenu.addAction( - embed::getIconPixmap("edit_merge"), - tr("Merge Selection"), - [this]() { contextMenuAction(ContextMenuAction::Merge); } - ); - } } contextMenu.addAction( @@ -1124,6 +1133,12 @@ void ClipView::contextMenuEvent( QContextMenuEvent * cme ) colorMenu.addAction(tr("Pick random"), this, SLOT(randomizeColor())); contextMenu.addMenu(&colorMenu); + contextMenu.addAction( + m_clip->getAutoResize() ? embed::getIconPixmap("auto_resize_disable") : embed::getIconPixmap("auto_resize"), + m_clip->getAutoResize() ? tr("Disable auto-resize") : tr("Enable auto-resize"), + this, &ClipView::toggleSelectedAutoResize + ); + constructContextMenu( &contextMenu ); contextMenu.exec( QCursor::pos() ); @@ -1152,9 +1167,6 @@ void ClipView::contextMenuAction( ContextMenuAction action ) case ContextMenuAction::Mute: toggleMute( active ); break; - case ContextMenuAction::Merge: - mergeClips(active); - break; } } @@ -1238,102 +1250,19 @@ void ClipView::toggleMute( QVector clipvs ) } } -bool ClipView::canMergeSelection(QVector clipvs) +void ClipView::toggleSelectedAutoResize() { - // Can't merge a single Clip - if (clipvs.size() < 2) { return false; } - - // We check if the owner of the first Clip is an Instrument Track - bool isInstrumentTrack = dynamic_cast(clipvs.at(0)->getTrackView()); - - // Then we create a set with all the Clips owners - std::set ownerTracks; - for (auto clipv: clipvs) { ownerTracks.insert(clipv->getTrackView()); } - - // Can merge if there's only one owner track and it's an Instrument Track - return isInstrumentTrack && ownerTracks.size() == 1; + const bool newState = !m_clip->getAutoResize(); + std::set journaledTracks; + for (auto clipv: getClickedClips()) + { + Clip* clip = clipv->getClip(); + if (journaledTracks.insert(clip->getTrack()).second) { clip->getTrack()->addJournalCheckPoint(); } + clip->setAutoResize(newState); + clip->updateLength(); + } } -void ClipView::mergeClips(QVector clipvs) -{ - // Get the track that we are merging Clips in - auto track = dynamic_cast(clipvs.at(0)->getTrackView()->getTrack()); - - if (!track) - { - qWarning("Warning: Couldn't retrieve InstrumentTrack in mergeClips()"); - return; - } - - // For Undo/Redo - track->addJournalCheckPoint(); - track->saveJournallingState(false); - - // Find the earliest position of all the selected ClipVs - const auto earliestClipV = std::min_element(clipvs.constBegin(), clipvs.constEnd(), - [](ClipView* a, ClipView* b) - { - return a->getClip()->startPosition() < - b->getClip()->startPosition(); - } - ); - - const TimePos earliestPos = (*earliestClipV)->getClip()->startPosition(); - - // Create a clip where all notes will be added - auto newMidiClip = dynamic_cast(track->createClip(earliestPos)); - if (!newMidiClip) - { - qWarning("Warning: Failed to convert Clip to MidiClip on mergeClips"); - return; - } - - newMidiClip->saveJournallingState(false); - - // Add the notes and remove the Clips that are being merged - for (auto clipv: clipvs) - { - // Convert ClipV to MidiClipView - auto mcView = dynamic_cast(clipv); - - if (!mcView) - { - qWarning("Warning: Non-MidiClip Clip on InstrumentTrack"); - continue; - } - - const NoteVector& currentClipNotes = mcView->getMidiClip()->notes(); - TimePos mcViewPos = mcView->getMidiClip()->startPosition(); - - for (Note* note: currentClipNotes) - { - Note* newNote = newMidiClip->addNote(*note, false); - TimePos originalNotePos = newNote->pos(); - newNote->setPos(originalNotePos + (mcViewPos - earliestPos)); - } - - // We disable the journalling system before removing, so the - // removal doesn't get added to the undo/redo history - clipv->getClip()->saveJournallingState(false); - // No need to check for nullptr because we check while building the clipvs QVector - clipv->remove(); - } - - // Update length since we might have moved notes beyond the end of the MidiClip length - newMidiClip->updateLength(); - // Rearrange notes because we might have moved them - newMidiClip->rearrangeAllNotes(); - // Restore journalling states now that the operation is finished - newMidiClip->restoreJournallingState(); - track->restoreJournallingState(); - // Update song - Engine::getSong()->setModified(); - getGUI()->songEditor()->update(); -} - - - - /*! \brief How many pixels a bar takes for this ClipView. * * \return the number of pixels per bar. @@ -1440,7 +1369,7 @@ int ClipView::knifeMarkerPos( QMouseEvent * me ) const float ppb = m_trackView->trackContainerView()->pixelsPerBar(); TimePos midiPos = markerPos * TimePos::ticksPerBar() / ppb; //2: Snap to the correct position, based on modifier keys - midiPos = quantizeSplitPos( midiPos, me->modifiers() & Qt::ShiftModifier ); + midiPos = quantizeSplitPos(midiPos); //3: Convert back to a pixel position return midiPos * ppb / TimePos::ticksPerBar(); } @@ -1449,23 +1378,20 @@ int ClipView::knifeMarkerPos( QMouseEvent * me ) -TimePos ClipView::quantizeSplitPos( TimePos midiPos, bool shiftMode ) +TimePos ClipView::quantizeSplitPos(TimePos midiPos) { const float snapSize = getGUI()->songEditor()->m_editor->getSnapSize(); - if ( shiftMode ) - { //If shift is held we quantize the length of the new left clip... - const TimePos leftPos = midiPos.quantize( snapSize ); - //...or right clip... - const TimePos rightOff = m_clip->length() - midiPos; - const TimePos rightPos = m_clip->length() - rightOff.quantize( snapSize ); - //...whichever gives a position closer to the cursor - if (std::abs(leftPos - midiPos) < std::abs(rightPos - midiPos)) { return leftPos; } - else { return rightPos; } - } - else - { - return TimePos(midiPos + m_initialClipPos).quantize( snapSize ) - m_initialClipPos; - } + // quantize the length of the new left clip... + const TimePos leftPos = midiPos.quantize(snapSize); + //...or right clip... + const TimePos rightOff = m_clip->length() - midiPos; + const TimePos rightPos = m_clip->length() - rightOff.quantize(snapSize); + //...or the global gridlines + const TimePos globalPos = TimePos(midiPos + m_initialClipPos).quantize(snapSize) - m_initialClipPos; + //...whichever gives a position closer to the cursor + if (abs(leftPos - midiPos) <= abs(rightPos - midiPos) && abs(leftPos - midiPos) <= abs(globalPos - midiPos)) { return leftPos; } + else if (abs(rightPos - midiPos) <= abs(leftPos - midiPos) && abs(rightPos - midiPos) <= abs(globalPos - midiPos)) { return rightPos; } + else { return globalPos; } } @@ -1515,4 +1441,30 @@ auto ClipView::hasCustomColor() const -> bool return m_clip->color().has_value() || m_clip->getTrack()->color().has_value(); } +bool ClipView::splitClip(const TimePos pos) +{ + const TimePos splitPos = m_initialClipPos + pos; + + // Don't split if we slid off the Clip or if we're on the clip's start/end + // Cutting at exactly the start/end position would create a zero length + // clip (bad), and a clip the same length as the original one (pointless). + if (splitPos <= m_initialClipPos || splitPos >= m_initialClipEnd) { return false; } + + m_clip->getTrack()->addJournalCheckPoint(); + m_clip->getTrack()->saveJournallingState(false); + + auto rightClip = m_clip->clone(); + + m_clip->changeLength(splitPos - m_initialClipPos); + m_clip->setAutoResize(false); + + rightClip->movePosition(splitPos); + rightClip->changeLength(m_initialClipEnd - splitPos); + rightClip->setStartTimeOffset(m_clip->startTimeOffset() - m_clip->length()); + rightClip->setAutoResize(false); + + m_clip->getTrack()->restoreJournallingState(); + return true; +} + } // namespace lmms::gui diff --git a/src/gui/clips/MidiClipView.cpp b/src/gui/clips/MidiClipView.cpp index b735913e4..70f41eb89 100644 --- a/src/gui/clips/MidiClipView.cpp +++ b/src/gui/clips/MidiClipView.cpp @@ -33,14 +33,18 @@ #include #include #include +#include #include "AutomationEditor.h" #include "ConfigManager.h" #include "DeprecationHelper.h" #include "GuiApplication.h" +#include "InstrumentTrackView.h" #include "MidiClip.h" #include "PianoRoll.h" #include "RenameDialog.h" +#include "SongEditor.h" +#include "TrackContainerView.h" #include "TrackView.h" namespace lmms::gui @@ -215,6 +219,18 @@ void MidiClipView::constructContextMenu( QMenu * _cm ) _cm->addAction( embed::getIconPixmap( "edit_erase" ), tr( "Clear all notes" ), m_clip, SLOT(clear())); + + if (canMergeSelection(getClickedClips())) + { + _cm->addAction( + embed::getIconPixmap("edit_merge"), + tr("Merge Selection"), + [this]() { mergeClips(getClickedClips()); } + ); + } + + _cm->addAction(embed::getIconPixmap("clear_notes_out_of_bounds"), tr("Clear notes out of bounds"), [this]() { bulkClearNotesOutOfBounds(getClickedClips()); }); + if (!isBeat) { _cm->addAction(embed::getIconPixmap("scale"), tr("Transpose"), this, &MidiClipView::transposeSelection); @@ -242,6 +258,168 @@ void MidiClipView::constructContextMenu( QMenu * _cm ) +bool MidiClipView::canMergeSelection(QVector clipvs) +{ + // Can't merge a single Clip + if (clipvs.size() < 2) { return false; } + + // We check if the owner of the first Clip is an Instrument Track + bool isInstrumentTrack = dynamic_cast(clipvs.at(0)->getTrackView()); + + // Then we create a set with all the Clips owners + std::set ownerTracks; + for (auto clipv: clipvs) { ownerTracks.insert(clipv->getTrackView()); } + + // Can merge if there's only one owner track and it's an Instrument Track + return isInstrumentTrack && ownerTracks.size() == 1; +} + +void MidiClipView::mergeClips(QVector clipvs) +{ + // Get the track that we are merging Clips in + auto track = dynamic_cast(clipvs.at(0)->getTrackView()->getTrack()); + + if (!track) + { + qWarning("Warning: Couldn't retrieve InstrumentTrack in mergeClips()"); + return; + } + + // For Undo/Redo + track->addJournalCheckPoint(); + track->saveJournallingState(false); + + // Find the earliest position of all the selected ClipVs + const auto earliestClipV = std::min_element(clipvs.constBegin(), clipvs.constEnd(), + [](ClipView* a, ClipView* b) + { + return a->getClip()->startPosition() < + b->getClip()->startPosition(); + } + ); + const TimePos earliestPos = (*earliestClipV)->getClip()->startPosition(); + + // Find the latest position of all the selected ClipVs + const auto latestClipV = std::max_element(clipvs.constBegin(), clipvs.constEnd(), + [](ClipView* a, ClipView* b) + { + return a->getClip()->endPosition() < + b->getClip()->endPosition(); + } + ); + const TimePos latestPos = (*latestClipV)->getClip()->endPosition(); + + + // Create a clip where all notes will be added + auto newMidiClip = dynamic_cast(track->createClip(earliestPos)); + if (!newMidiClip) + { + qWarning("Warning: Failed to convert Clip to MidiClip on mergeClips"); + return; + } + + newMidiClip->saveJournallingState(false); + + // Add the notes and remove the Clips that are being merged + for (auto clipv: clipvs) + { + // Convert ClipV to MidiClipView + auto mcView = dynamic_cast(clipv); + + if (!mcView) + { + qWarning("Warning: Non-MidiClip Clip on InstrumentTrack"); + continue; + } + + const NoteVector& currentClipNotes = mcView->getMidiClip()->notes(); + TimePos mcViewPos = mcView->getMidiClip()->startPosition() + mcView->getMidiClip()->startTimeOffset(); + + const TimePos clipStartTime = -mcView->getMidiClip()->startTimeOffset(); + const TimePos clipEndTime = mcView->getMidiClip()->length() - mcView->getMidiClip()->startTimeOffset(); + + for (Note* note: currentClipNotes) + { + const TimePos newNoteStart = std::max(note->pos(), clipStartTime); + const TimePos newNoteEnd = std::min(note->endPos(), clipEndTime); + const TimePos newLength = newNoteEnd - newNoteStart; + if (newLength > 0) + { + Note* newNote = newMidiClip->addNote(*note, false); + newNote->setPos(newNoteStart + (mcViewPos - earliestPos)); + newNote->setLength(newLength); + } + } + + // We disable the journalling system before removing, so the + // removal doesn't get added to the undo/redo history + clipv->getClip()->saveJournallingState(false); + // No need to check for nullptr because we check while building the clipvs QVector + clipv->remove(); + } + + // Update length to extend from the start of the first clip to the end of the last clip + newMidiClip->changeLength(latestPos - earliestPos); + newMidiClip->setAutoResize(false); + // Rearrange notes because we might have moved them + newMidiClip->rearrangeAllNotes(); + // Restore journalling states now that the operation is finished + newMidiClip->restoreJournallingState(); + track->restoreJournallingState(); + // Update song + Engine::getSong()->setModified(); + getGUI()->songEditor()->update(); +} + +void MidiClipView::clearNotesOutOfBounds() +{ + m_clip->getTrack()->addJournalCheckPoint(); + m_clip->getTrack()->saveJournallingState(false); + + auto newClip = new MidiClip(static_cast(m_clip->getTrack())); + newClip->setAutoResize(m_clip->getAutoResize()); + newClip->movePosition(m_clip->startPosition()); + + TimePos startBound = -m_clip->startTimeOffset(); + TimePos endBound = m_clip->length() - m_clip->startTimeOffset(); + + for (Note const* note: m_clip->m_notes) + { + const TimePos newNoteStart = std::max(note->pos(), startBound) - startBound; + const TimePos newNoteEnd = std::min(note->endPos(), endBound) - startBound; + const TimePos newLength = newNoteEnd - newNoteStart; + if (newLength > 0) + { + Note newNote = Note{*note}; + newNote.setPos(newNoteStart); + newNote.setLength(newLength); + newClip->addNote(newNote, false); + } + } + newClip->changeLength(m_clip->length()); + newClip->updateLength(); + + remove(); + m_clip->getTrack()->restoreJournallingState(); +} + +void MidiClipView::bulkClearNotesOutOfBounds(QVector clipvs) +{ + for (auto clipv: clipvs) + { + // Convert ClipV to MidiClipView + auto mcView = dynamic_cast(clipv); + if (!mcView) + { + qWarning("Warning: Non-MidiClip Clip on InstrumentTrack"); + continue; + } + mcView->clearNotesOutOfBounds(); + } + Engine::getSong()->setModified(); + getGUI()->songEditor()->update(); +} + void MidiClipView::mousePressEvent( QMouseEvent * _me ) { @@ -299,6 +477,8 @@ void MidiClipView::mousePressEvent( QMouseEvent * _me ) void MidiClipView::mouseDoubleClickEvent(QMouseEvent *_me) { + if (m_trackView->trackContainerView()->knifeMode()) { return; } + if( _me->button() != Qt::LeftButton ) { _me->ignore(); @@ -442,11 +622,12 @@ void MidiClipView::paintEvent( QPaintEvent * ) // Compute pixels per bar const int baseWidth = fixedClips() ? parentWidget()->width() - 2 * BORDER_WIDTH : width() - BORDER_WIDTH; - const float pixelsPerBar = baseWidth / (float) m_clip->length().getBar(); + const float pixelsPerBar = 1.0f * baseWidth / m_clip->length() * TimePos::ticksPerBar(); + + const int offset = m_clip->startTimeOffset(); // Length of one bar/beat in the [0,1] x [0,1] coordinate system - const float barLength = 1. / m_clip->length().getBar(); - const float tickLength = barLength / TimePos::ticksPerBar(); + const float tickLength = 1.0f / m_clip->length(); const int x_base = BORDER_WIDTH; @@ -608,7 +789,7 @@ void MidiClipView::paintEvent( QPaintEvent * ) int mappedNoteKey = currentNote->key() - minKey; int invertedMappedNoteKey = adjustedNoteRange - mappedNoteKey - 1; - float const noteStartX = currentNote->pos() * tickLength; + float const noteStartX = (currentNote->pos() + offset) * tickLength; float const noteLength = currentNote->length() * tickLength; float const noteStartY = invertedMappedNoteKey * noteHeight; @@ -633,14 +814,15 @@ void MidiClipView::paintEvent( QPaintEvent * ) const int lineSize = 3; p.setPen( c.darker( 200 ) ); - for( bar_t t = 1; t < m_clip->length().getBar(); ++t ) + for(float t = (offset % TimePos::ticksPerBar()) * pixelsPerBar / TimePos::ticksPerBar(); t < m_clip->length(); t += pixelsPerBar) { - p.drawLine( x_base + static_cast( pixelsPerBar * t ) - 1, - BORDER_WIDTH, x_base + static_cast( - pixelsPerBar * t ) - 1, BORDER_WIDTH + lineSize ); - p.drawLine( x_base + static_cast( pixelsPerBar * t ) - 1, + p.drawLine( x_base + t - 1, + BORDER_WIDTH, + x_base + t - 1, + BORDER_WIDTH + lineSize ); + p.drawLine( x_base + t - 1, rect().bottom() - ( lineSize + BORDER_WIDTH ), - x_base + static_cast( pixelsPerBar * t ) - 1, + x_base + t - 1, rect().bottom() - BORDER_WIDTH ); } @@ -670,9 +852,80 @@ void MidiClipView::paintEvent( QPaintEvent * ) p.drawPixmap( spacing, height() - ( size + spacing ), embed::getIconPixmap( "muted", size, size ) ); } + + if (m_marker) + { + p.setPen(markerColor()); + p.drawLine(m_markerPos, rect().bottom(), m_markerPos, rect().top()); + } painter.drawPixmap( 0, 0, m_paintPixmap ); } +bool MidiClipView::destructiveSplitClip(const TimePos pos) +{ + const TimePos splitPos = m_initialClipPos + pos; + const TimePos internalSplitPos = pos - m_clip->startTimeOffset(); + + // Don't split if we slid off the Clip or if we're on the clip's start/end + // Cutting at exactly the start/end position would create a zero length + // clip (bad), and a clip the same length as the original one (pointless). + if (splitPos <= m_initialClipPos || splitPos >= m_initialClipEnd) { return false; } + + m_clip->getTrack()->addJournalCheckPoint(); + m_clip->getTrack()->saveJournallingState(false); + + auto leftClip = m_clip->clone(); + leftClip->clearNotes(); + auto rightClip = m_clip->clone(); + rightClip->clearNotes(); + + for (Note const* note : m_clip->m_notes) + { + if (note->pos() >= internalSplitPos) + { + auto movedNote = Note{*note}; + movedNote.setPos(note->pos() - internalSplitPos); + rightClip->addNote(movedNote, false); + } + else if (note->endPos() > internalSplitPos) + { + auto movedNote = Note{*note}; + movedNote.setPos(0); + movedNote.setLength(note->endPos() - internalSplitPos); + rightClip->addNote(movedNote, false); + } + } + + for (Note const* note : m_clip->m_notes) + { + if (note->endPos() <= internalSplitPos) + { + leftClip->addNote(*note, false); + } + else if (note->pos() < internalSplitPos) + { + auto movedNote = Note{*note}; + movedNote.setLength(internalSplitPos - note->pos()); + leftClip->addNote(movedNote, false); + } + } + + leftClip->movePosition(m_initialClipPos); + leftClip->setAutoResize(false); + leftClip->changeLength(splitPos - m_initialClipPos); + leftClip->setStartTimeOffset(m_clip->startTimeOffset()); + + rightClip->movePosition(splitPos); + rightClip->setAutoResize(false); + rightClip->changeLength(m_initialClipEnd - splitPos); + rightClip->setStartTimeOffset(0); + + remove(); + m_clip->getTrack()->restoreJournallingState(); + return true; +} + + } // namespace lmms::gui diff --git a/src/gui/clips/PatternClipView.cpp b/src/gui/clips/PatternClipView.cpp index bf12440c7..e30c26c1e 100644 --- a/src/gui/clips/PatternClipView.cpp +++ b/src/gui/clips/PatternClipView.cpp @@ -34,6 +34,8 @@ #include "PatternClip.h" #include "PatternStore.h" #include "RenameDialog.h" +#include "TrackContainerView.h" +#include "TrackView.h" namespace lmms::gui { @@ -70,6 +72,8 @@ void PatternClipView::constructContextMenu(QMenu* _cm) void PatternClipView::mouseDoubleClickEvent(QMouseEvent*) { + if (m_trackView->trackContainerView()->knifeMode()) { return; } + openInPatternEditor(); } @@ -155,6 +159,12 @@ void PatternClipView::paintEvent(QPaintEvent*) embed::getIconPixmap( "muted", size, size ) ); } + if (m_marker) + { + p.setPen(markerColor()); + p.drawLine(m_markerPos, rect().bottom(), m_markerPos, rect().top()); + } + p.end(); painter.drawPixmap( 0, 0, m_paintPixmap ); @@ -195,5 +205,4 @@ void PatternClipView::update() ClipView::update(); } - } // namespace lmms::gui diff --git a/src/gui/clips/SampleClipView.cpp b/src/gui/clips/SampleClipView.cpp index a420d271a..b281e5304 100644 --- a/src/gui/clips/SampleClipView.cpp +++ b/src/gui/clips/SampleClipView.cpp @@ -37,6 +37,7 @@ #include "SampleThumbnail.h" #include "Song.h" #include "StringPairDrag.h" +#include "TrackContainerView.h" #include "TrackView.h" namespace lmms::gui @@ -185,6 +186,8 @@ void SampleClipView::mouseReleaseEvent(QMouseEvent *_me) void SampleClipView::mouseDoubleClickEvent( QMouseEvent * ) { + if (m_trackView->trackContainerView()->knifeMode()) { return; } + const QString selectedAudioFile = SampleLoader::openAudioFile(); if (selectedAudioFile.isEmpty()) { return; } @@ -327,6 +330,7 @@ void SampleClipView::paintEvent( QPaintEvent * pe ) if ( m_marker ) { + p.setPen(markerColor()); p.drawLine(m_markerPos, rect().bottom(), m_markerPos, rect().top()); } // recording sample tracks is not possible at the moment @@ -371,35 +375,4 @@ void SampleClipView::setAutomationGhost() aEditor->setFocus(); } -//! Split this Clip. -/*! \param pos the position of the split, relative to the start of the clip */ -bool SampleClipView::splitClip( const TimePos pos ) -{ - setMarkerEnabled( false ); - - const TimePos splitPos = m_initialClipPos + pos; - - //Don't split if we slid off the Clip or if we're on the clip's start/end - //Cutting at exactly the start/end position would create a zero length - //clip (bad), and a clip the same length as the original one (pointless). - if ( splitPos > m_initialClipPos && splitPos < m_initialClipEnd ) - { - m_clip->getTrack()->addJournalCheckPoint(); - m_clip->getTrack()->saveJournallingState( false ); - - auto rightClip = new SampleClip(*m_clip); - - m_clip->changeLength( splitPos - m_initialClipPos ); - - rightClip->movePosition( splitPos ); - rightClip->changeLength( m_initialClipEnd - splitPos ); - rightClip->setStartTimeOffset( m_clip->startTimeOffset() - m_clip->length() ); - - m_clip->getTrack()->restoreJournallingState(); - return true; - } - else { return false; } -} - - } // namespace lmms::gui diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index c90f45a86..c3aa4a8ec 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -102,7 +102,8 @@ AutomationEditor::AutomationEditor() : m_scaleColor(Qt::SolidPattern), m_crossColor(0, 0, 0), m_backgroundShade(0, 0, 0), - m_ghostNoteColor(0, 0, 0) + m_ghostNoteColor(0, 0, 0), + m_outOfBoundsShade(0, 0, 0, 128) { connect( this, SIGNAL(currentClipChanged()), this, SLOT(updateAfterClipChange()), @@ -182,6 +183,7 @@ void AutomationEditor::setCurrentClip(AutomationClip * new_clip ) if (m_clip != nullptr) { connect(m_clip, SIGNAL(dataChanged()), this, SLOT(update())); + connect(m_clip, &AutomationClip::lengthChanged, this, qOverload<>(&QWidget::update)); } emit currentClipChanged(); @@ -1381,6 +1383,22 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) drawAutomationTangents(p, it); } } + + // draw clip bounds overlay + p.fillRect( + xCoordOfTick(m_clip->length() - m_clip->startTimeOffset()), + TOP_MARGIN, + width() - 10, + grid_bottom, + m_outOfBoundsShade + ); + p.fillRect( + 0, + TOP_MARGIN, + xCoordOfTick(-m_clip->startTimeOffset()), + grid_bottom, + m_outOfBoundsShade + ); } else { @@ -1397,7 +1415,7 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) } // TODO: Get this out of paint event - int l = validClip() ? (int) m_clip->length() : 0; + int l = validClip() ? (int) m_clip->length() - m_clip->startTimeOffset() : 0; // reset scroll-range if( m_leftRightScroll->maximum() != l ) diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index db8ae6144..05892da3f 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -211,6 +211,7 @@ PianoRoll::PianoRoll() : m_noteBorders( true ), m_ghostNoteBorders( true ), m_backgroundShade( 0, 0, 0 ), + m_outOfBoundsShade(0, 0, 0, 128), m_whiteKeyWidth(WHITE_KEY_WIDTH), m_blackKeyWidth(BLACK_KEY_WIDTH) { @@ -868,7 +869,8 @@ void PianoRoll::setCurrentMidiClip( MidiClip* newMidiClip ) return; } - m_leftRightScroll->setValue( 0 ); + // Scroll horizontally to the start of the clip, minus a bar for aesthetics. + m_leftRightScroll->setValue(std::max(0, -m_midiClip->startTimeOffset() - TimePos::ticksPerBar())); // determine the central key so that we can scroll to it int central_key = 0; @@ -888,6 +890,13 @@ void PianoRoll::setCurrentMidiClip( MidiClip* newMidiClip ) m_startKey = qBound(0, central_key, NumKeys); } + // Make sure the playhead position isn't out of the clip bounds. + Engine::getSong()->getPlayPos(Song::PlayMode::MidiClip).setTicks(std::clamp( + Engine::getSong()->getPlayPos(Song::PlayMode::MidiClip).getTicks(), + std::max(0, -m_midiClip->startTimeOffset()), + m_midiClip->length() - m_midiClip->startTimeOffset() + )); + // resizeEvent() does the rest for us (scrolling, range-checking // of start-notes and so on...) resizeEvent( nullptr ); @@ -905,6 +914,7 @@ void PianoRoll::setCurrentMidiClip( MidiClip* newMidiClip ) connect(m_midiClip->instrumentTrack()->microtuner()->keymapModel(), SIGNAL(dataChanged()), this, SLOT(update())); connect(m_midiClip->instrumentTrack()->microtuner()->keyRangeImportModel(), SIGNAL(dataChanged()), this, SLOT(update())); + connect(m_midiClip, &MidiClip::lengthChanged, this, qOverload<>(&QWidget::update)); update(); emit currentMidiClipChanged(); @@ -3209,6 +3219,12 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) // G-1 is one of the widest; plus one pixel margin for the shadow QRect const boundingRect = fontMetrics.boundingRect(QString("G-1")) + QMargins(0, 0, 1, 0); + auto xCoordOfTick = [this](int tick) { + return m_whiteKeyWidth + ( + (tick - m_currentPosition) * m_ppb / TimePos::ticksPerBar() + ); + }; + // Order of drawing // - vertical quantization lines // - piano roll + horizontal key lines @@ -3283,11 +3299,7 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) // allow quantization grid up to 1/32 for normal notes else if (q < 6) { q = 6; } } - auto xCoordOfTick = [this](int tick) { - return m_whiteKeyWidth + ( - (tick - m_currentPosition) * m_ppb / TimePos::ticksPerBar() - ); - }; + p.setPen(m_lineColor); for (tick = m_currentPosition - m_currentPosition % q, x = xCoordOfTick(tick); @@ -3717,13 +3729,25 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) } } + // draw clip bounds + p.fillRect( + xCoordOfTick(m_midiClip->length() - m_midiClip->startTimeOffset()), + PR_TOP_MARGIN, + width() - 10, + noteEditBottom(), + m_outOfBoundsShade + ); + p.fillRect( + 0, + PR_TOP_MARGIN, + xCoordOfTick(-m_midiClip->startTimeOffset()), + noteEditBottom(), + m_outOfBoundsShade + ); + // -- Knife tool (draw cut line) if (m_action == Action::Knife && m_knifeDown) { - auto xCoordOfTick = [this](int tick) { - return m_whiteKeyWidth + ( - (tick - m_currentPosition) * m_ppb / TimePos::ticksPerBar()); - }; int x1 = xCoordOfTick(m_knifeStartTickPos); int y1 = y_base - (m_knifeStartKey - m_startKey + 1) * m_keyLineHeight; int x2 = xCoordOfTick(m_knifeEndTickPos); @@ -3802,7 +3826,7 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) p.drawRect(x + m_whiteKeyWidth, y, w, h); // TODO: Get this out of paint event - int l = ( hasValidMidiClip() )? (int) m_midiClip->length() : 0; + int l = ( hasValidMidiClip() )? (int) m_midiClip->length() - m_midiClip->startTimeOffset() : 0; // reset scroll-range if( m_leftRightScroll->maximum() != l ) diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index 72ee28bc8..49f620b68 100644 --- a/src/gui/editors/SongEditor.cpp +++ b/src/gui/editors/SongEditor.cpp @@ -959,7 +959,7 @@ SongEditorWindow::SongEditorWindow(Song* song) : m_editModeGroup = new ActionGroup(this); m_drawModeAction = m_editModeGroup->addAction(embed::getIconPixmap("edit_draw"), tr("Draw mode")); - m_knifeModeAction = m_editModeGroup->addAction(embed::getIconPixmap("edit_knife"), tr("Knife mode (split sample clips)")); + m_knifeModeAction = m_editModeGroup->addAction(embed::getIconPixmap("edit_knife"), tr("Knife mode (split clips)")); m_selectModeAction = m_editModeGroup->addAction(embed::getIconPixmap("edit_select"), tr("Edit mode (select and move)")); m_drawModeAction->setChecked(true); diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 0d97d500c..8370807ae 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -751,7 +751,7 @@ bool InstrumentTrack::play( const TimePos & _start, const fpp_t _frames, TimePos cur_start = _start; if( _clip_num < 0 ) { - cur_start -= c->startPosition(); + cur_start -= c->startPosition() + c->startTimeOffset(); } // get all notes from the given clip... @@ -762,25 +762,33 @@ bool InstrumentTrack::play( const TimePos & _start, const fpp_t _frames, // very effective algorithm for playing notes that are // posated within the current sample-frame - if( cur_start > 0 ) { - // skip notes which are posated before start-bar - while( nit != notes.end() && ( *nit )->pos() < cur_start ) + // skip notes which end before start-bar + while( nit != notes.end() && ( *nit )->endPos() < cur_start ) { ++nit; } } - while (nit != notes.end() && (*nit)->pos() == cur_start) + while (nit != notes.end() && (*nit)->pos() < c->length() - c->startTimeOffset()) { const auto currentNote = *nit; + // Skip any notes note at the current time pos or not overlapping with the start. + if (!(currentNote->pos() == cur_start + || (cur_start == -c->startTimeOffset() && (*nit)->pos() < cur_start && (*nit)->endPos() > cur_start))) + { + ++nit; + continue; + } + // Calculate the overlap of the note over the clip end. + const auto noteOverlap = std::max(0, currentNote->endPos() - (c->length() - c->startTimeOffset())); // If the note is a Step Note, frames will be 0 so the NotePlayHandle // plays for the whole length of the sample const auto noteFrames = currentNote->type() == Note::Type::Step ? 0 - : currentNote->length().frames(frames_per_tick); + : (currentNote->endPos() - cur_start - noteOverlap) * frames_per_tick; NotePlayHandle* notePlayHandle = NotePlayHandleManager::acquire(this, _offset, noteFrames, *currentNote); notePlayHandle->setPatternTrack(pattern_track); @@ -789,7 +797,7 @@ bool InstrumentTrack::play( const TimePos & _start, const fpp_t _frames, { // then set song-global offset of clip in order to // properly perform the note detuning - notePlayHandle->setSongGlobalParentOffset( c->startPosition() ); + notePlayHandle->setSongGlobalParentOffset( c->startPosition() + c->startTimeOffset()); } Engine::audioEngine()->addPlayHandle( notePlayHandle ); diff --git a/src/tracks/MidiClip.cpp b/src/tracks/MidiClip.cpp index 0d3e5a5b4..ca17b6898 100644 --- a/src/tracks/MidiClip.cpp +++ b/src/tracks/MidiClip.cpp @@ -48,16 +48,20 @@ MidiClip::MidiClip( InstrumentTrack * _instrument_track ) : if (_instrument_track->trackContainer() == Engine::patternStore()) { resizeToFirstTrack(); + setResizable(false); + } + else + { + setResizable(true); } init(); - setAutoResize( true ); } MidiClip::MidiClip( const MidiClip& other ) : - Clip( other.m_instrumentTrack ), + Clip(other), m_instrumentTrack( other.m_instrumentTrack ), m_clipType( other.m_clipType ), m_steps( other.m_steps ) @@ -71,13 +75,13 @@ MidiClip::MidiClip( const MidiClip& other ) : switch( getTrack()->trackContainer()->type() ) { case TrackContainer::Type::Pattern: - setAutoResize( true ); + setResizable(false); break; case TrackContainer::Type::Song: // move down default: - setAutoResize( false ); + setResizable(true); break; } } @@ -145,18 +149,24 @@ void MidiClip::updateLength() return; } - tick_t max_length = TimePos::ticksPerBar(); - - for (const auto& note : m_notes) + // If the clip has already been manually resized, don't automatically resize it. + // Unless we are in a pattern, where you can't resize stuff manually + if (getAutoResize() || !getResizable()) { - if (note->length() > 0) + tick_t max_length = TimePos::ticksPerBar(); + + for (const auto& note : m_notes) { - max_length = std::max(max_length, note->endPos()); + if (note->length() > 0) + { + max_length = std::max(max_length, note->endPos()); + } } + changeLength( TimePos( max_length ).nextFullBar() * + TimePos::ticksPerBar() ); + setStartTimeOffset(TimePos(0)); + updatePatternTrack(); } - changeLength( TimePos( max_length ).nextFullBar() * - TimePos::ticksPerBar() ); - updatePatternTrack(); } @@ -435,6 +445,8 @@ void MidiClip::saveSettings( QDomDocument & _doc, QDomElement & _this ) { _this.setAttribute( "type", static_cast(m_clipType) ); _this.setAttribute( "name", name() ); + _this.setAttribute("autoresize", QString::number(getAutoResize())); + _this.setAttribute("off", startTimeOffset()); if (const auto& c = color()) { @@ -454,6 +466,7 @@ void MidiClip::saveSettings( QDomDocument & _doc, QDomElement & _this ) } _this.setAttribute( "muted", isMuted() ); _this.setAttribute( "steps", m_steps ); + _this.setAttribute( "len", length() ); // now save settings of all notes for (auto& note : m_notes) @@ -507,7 +520,20 @@ void MidiClip::loadSettings( const QDomElement & _this ) } checkType(); - updateLength(); + + int len = _this.attribute("len").toInt(); + if (len <= 0) + { + // TODO: Handle with an upgrade method + updateLength(); + } + else + { + changeLength(len); + } + + setAutoResize(_this.attribute("autoresize").toInt()); + setStartTimeOffset(_this.attribute("off").toInt()); emit dataChanged(); } From 928b72ab36a4b924c792d3ac304fb1c6d00ee6e7 Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Wed, 30 Apr 2025 15:26:35 -0400 Subject: [PATCH 36/62] Add Windows arm64 builds (#7848) * Add Windows arm64 builds --- .github/workflows/build.yml | 65 +++++++++++++++++++++ .github/workflows/deps-msys2-clangarm64.txt | 25 ++++++++ 2 files changed, 90 insertions(+) create mode 100644 .github/workflows/deps-msys2-clangarm64.txt diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9c812fd9b..5a5a25613 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -421,3 +421,68 @@ jobs: ccache --show-stats --verbose env: CCACHE_MAXSIZE: 500MB + msys2: + name: windows-arm64 + runs-on: windows-11-arm + defaults: + run: + shell: msys2 {0} + env: + CMAKE_OPTS: >- + -DCMAKE_BUILD_TYPE=RelWithDebInfo + -DUSE_COMPILE_CACHE=ON + -DCPACK_NSIS_EXECUTABLE=/clang64/bin/makensis.exe + CCACHE_MAXSIZE: 0 + CCACHE_NOCOMPRESS: 1 + MAKEFLAGS: -j2 + steps: + - name: Check out + uses: actions/checkout@v3 + with: + fetch-depth: 0 + submodules: recursive + - name: Cache msys2 dependencies + id: cache-deps + uses: actions/cache@v3 + with: + key: windows-arm64-${{ hashFiles('.github/workflows/deps-msys2-clangarm64.txt') }} + restore-keys: | + windows-arm64- + path: \msys64\var\cache\pacman\pkg + - name: Install msys2 + uses: msys2/setup-msys2@v2 + with: + msystem: CLANGARM64 + update: true + - name: Install dependencies + run: pacman --needed --noconfirm -S - < .github/workflows/deps-msys2-clangarm64.txt + - name: Cache ccache data + uses: actions/cache@v3 + with: + key: ccache-${{ github.job }}-${{ github.ref }}-${{ github.run_id }} + restore-keys: | + ccache-${{ github.job }}-${{ github.ref }}- + ccache-${{ github.job }}- + path: ~\AppData\Local\ccache + - name: Configure + run: | + /clang64/bin/ccache.exe --zero-stats + cmake -B build $CMAKE_OPTS + - name: Build + run: cmake --build build + - name: Package + run: cmake --build build --target package + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: windows-arm64 + path: build\lmms-*.exe + - name: Trim ccache and print statistics + run: | + /clang64/bin/ccache.exe --cleanup + echo "[ccache config]" + /clang64/bin/ccache.exe --show-config + echo "[ccache stats]" + /clang64/bin/ccache.exe --show-stats --verbose + env: + CCACHE_MAXSIZE: 500MB diff --git a/.github/workflows/deps-msys2-clangarm64.txt b/.github/workflows/deps-msys2-clangarm64.txt new file mode 100644 index 000000000..c3feb0264 --- /dev/null +++ b/.github/workflows/deps-msys2-clangarm64.txt @@ -0,0 +1,25 @@ +base +base-devel +filesystem +git +mingw-w64-clang-aarch64-SDL2 +mingw-w64-clang-aarch64-ccache +mingw-w64-clang-aarch64-clang +mingw-w64-clang-aarch64-cmake +mingw-w64-clang-aarch64-fftw +mingw-w64-clang-aarch64-fltk +mingw-w64-clang-aarch64-fluidsynth +mingw-w64-clang-aarch64-libgig +mingw-w64-clang-aarch64-libsamplerate +mingw-w64-clang-aarch64-libsndfile +mingw-w64-clang-aarch64-lilv +mingw-w64-clang-aarch64-lv2 +mingw-w64-clang-aarch64-qt5-base +mingw-w64-clang-aarch64-qt5-svg +mingw-w64-clang-aarch64-suil +mingw-w64-clang-aarch64-stk +mingw-w64-clang-x86_64-nsis +mingw-w64-clang-x86_64-ccache +msys2-runtime +perl-List-MoreUtils +perl-XML-Parser \ No newline at end of file From 41093efcbe62ba0a8752f6e82a8b7295fbea2a11 Mon Sep 17 00:00:00 2001 From: cyberrumor <46626673+cyberrumor@users.noreply.github.com> Date: Fri, 2 May 2025 08:04:47 -0700 Subject: [PATCH 37/62] Revert "Fix PeakController attack/decay (#7566)" (#7871) Prior to commit b5de1d50e, audible zipper artifacts were present when using the PeakController on a bus to control the volume of another bus for sidechain compression. The bus that had its volume reduced was the one which produced audible artifacts. Commit b5de1d50e introduced LERP to PeakController to smooth out its signal, which eliminated the zipper artifacts. However, this had the side effect of increased latency even when both attack and decay settings were set to zero. Until a more robust solution is implemented, reverting this change eliminates the latency by eliminating the lerp, but reintroduces audible zipper artifacts. --- include/PeakController.h | 3 ++- .../PeakControllerEffect/PeakControllerEffect.cpp | 12 +----------- src/core/PeakController.cpp | 13 +++++++++++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/include/PeakController.h b/include/PeakController.h index a22257374..de9da3b1c 100644 --- a/include/PeakController.h +++ b/include/PeakController.h @@ -78,7 +78,8 @@ private: static int m_loadCount; static bool m_buggedFile; - float m_coeff; + float m_attackCoeff; + float m_decayCoeff; bool m_coeffNeedsUpdate; } ; diff --git a/plugins/PeakControllerEffect/PeakControllerEffect.cpp b/plugins/PeakControllerEffect/PeakControllerEffect.cpp index b6d053257..394a80efd 100644 --- a/plugins/PeakControllerEffect/PeakControllerEffect.cpp +++ b/plugins/PeakControllerEffect/PeakControllerEffect.cpp @@ -132,18 +132,8 @@ Effect::ProcessStatus PeakControllerEffect::processImpl(SampleFrame* buf, const float curRMS = sqrt_neg(sum / frames); const float tres = c.m_tresholdModel.value(); const float amount = c.m_amountModel.value() * c.m_amountMultModel.value(); - const float attack = 1.0f - c.m_attackModel.value(); - const float decay = 1.0f - c.m_decayModel.value(); - curRMS = qAbs( curRMS ) < tres ? 0.0f : curRMS; - float target = c.m_baseModel.value() + amount * curRMS; - // Use decay when the volume is decreasing, attack otherwise. - // Since direction can change as often as every sampleBuffer, it's difficult - // to witness attack/decay working in isolation unless using large buffer sizes. - const float t = target < m_lastSample ? decay : attack; - // Set m_lastSample to the interpolation between itself and target. - // When t is 1.0, m_lastSample snaps to target. When t is 0.0, m_lastSample shouldn't change. - m_lastSample = std::clamp(m_lastSample + t * (target - m_lastSample), 0.0f, 1.0f); + m_lastSample = qBound( 0.0f, c.m_baseModel.value() + amount * curRMS, 1.0f ); return ProcessStatus::Continue; } diff --git a/src/core/PeakController.cpp b/src/core/PeakController.cpp index 1b819982a..1c38cf4cb 100644 --- a/src/core/PeakController.cpp +++ b/src/core/PeakController.cpp @@ -80,7 +80,9 @@ void PeakController::updateValueBuffer() { if( m_coeffNeedsUpdate ) { - m_coeff = 100.0f / Engine::audioEngine()->outputSampleRate(); + const float ratio = 44100.0f / Engine::audioEngine()->outputSampleRate(); + m_attackCoeff = 1.0f - powf( 2.0f, -0.3f * ( 1.0f - m_peakEffect->attackModel()->value() ) * ratio ); + m_decayCoeff = 1.0f - powf( 2.0f, -0.3f * ( 1.0f - m_peakEffect->decayModel()->value() ) * ratio ); m_coeffNeedsUpdate = false; } @@ -95,7 +97,14 @@ void PeakController::updateValueBuffer() for( f_cnt_t f = 0; f < frames; ++f ) { const float diff = ( targetSample - m_currentSample ); - m_currentSample += diff * m_coeff; + if( m_currentSample < targetSample ) // going up... + { + m_currentSample += diff * m_attackCoeff; + } + else if( m_currentSample > targetSample ) // going down + { + m_currentSample += diff * m_decayCoeff; + } values[f] = m_currentSample; } } From 1c626386968ff277812bed871b044cf8f74bf18e Mon Sep 17 00:00:00 2001 From: regulus79 <117475203+regulus79@users.noreply.github.com> Date: Sun, 4 May 2025 15:50:33 -0400 Subject: [PATCH 38/62] Fix issue with Mixer key events and Track Label Button focus (#7870) A follow up to #7762 to fix various issues involving key events and focus grabbing problems. --- src/gui/MixerChannelView.cpp | 4 ++++ src/gui/SampleTrackWindow.cpp | 2 +- src/gui/instrument/InstrumentTrackWindow.cpp | 4 ++-- src/gui/tracks/TrackLabelButton.cpp | 1 + 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/gui/MixerChannelView.cpp b/src/gui/MixerChannelView.cpp index 13b18ce4b..3391affce 100644 --- a/src/gui/MixerChannelView.cpp +++ b/src/gui/MixerChannelView.cpp @@ -235,6 +235,10 @@ void MixerChannelView::keyPressEvent(QKeyEvent* ke) { m_fader->adjustByDialog(); } + else + { + ke->ignore(); + } } void MixerChannelView::setChannelIndex(int index) diff --git a/src/gui/SampleTrackWindow.cpp b/src/gui/SampleTrackWindow.cpp index eb09a2097..921ab3fce 100644 --- a/src/gui/SampleTrackWindow.cpp +++ b/src/gui/SampleTrackWindow.cpp @@ -267,7 +267,7 @@ void SampleTrackWindow::closeEvent(QCloseEvent* ce) hide(); } - m_stv->m_tlb->setFocus(); + m_stv->setFocus(); m_stv->m_tlb->setChecked(false); } diff --git a/src/gui/instrument/InstrumentTrackWindow.cpp b/src/gui/instrument/InstrumentTrackWindow.cpp index 0446e857e..f6a0d6955 100644 --- a/src/gui/instrument/InstrumentTrackWindow.cpp +++ b/src/gui/instrument/InstrumentTrackWindow.cpp @@ -539,8 +539,8 @@ void InstrumentTrackWindow::closeEvent( QCloseEvent* event ) hide(); } - m_itv->m_tlb->setFocus(); - m_itv->m_tlb->setChecked( false ); + m_itv->setFocus(); + m_itv->m_tlb->setChecked(false); } diff --git a/src/gui/tracks/TrackLabelButton.cpp b/src/gui/tracks/TrackLabelButton.cpp index c203fad8c..12b4dc4b4 100644 --- a/src/gui/tracks/TrackLabelButton.cpp +++ b/src/gui/tracks/TrackLabelButton.cpp @@ -47,6 +47,7 @@ TrackLabelButton::TrackLabelButton( TrackView * _tv, QWidget * _parent ) : m_iconName() { setAcceptDrops( true ); + setFocusPolicy(Qt::NoFocus); setCursor( QCursor( embed::getIconPixmap( "hand" ), 3, 3 ) ); setToolButtonStyle( Qt::ToolButtonTextBesideIcon ); From 06d897bc4d766a069a04949a1503ff6370c6ebc5 Mon Sep 17 00:00:00 2001 From: regulus79 <117475203+regulus79@users.noreply.github.com> Date: Sun, 4 May 2025 15:59:16 -0400 Subject: [PATCH 39/62] Fix crash when playing Pattern Editor without Pattern Track (#7862) Fixes a crash that occurs when attempting to play within the Pattern Editor with no Pattern Track created. --------- Co-authored-by: Sotonye Atemie --- src/core/Song.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/Song.cpp b/src/core/Song.cpp index ad689107b..b052c775a 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -366,7 +366,7 @@ void Song::processAutomations(const TrackList &tracklist, TimePos timeStart, fpp break; case PlayMode::Pattern: { - Q_ASSERT(tracklist.size() == 1); + if (tracklist.empty()) { return; } Q_ASSERT(tracklist.at(0)->type() == Track::Type::Pattern); auto patternTrack = dynamic_cast(tracklist.at(0)); container = Engine::patternStore(); From 61736a97b6ee880f0d118b1659ffe1639afbf384 Mon Sep 17 00:00:00 2001 From: regulus79 <117475203+regulus79@users.noreply.github.com> Date: Sun, 4 May 2025 16:42:43 -0400 Subject: [PATCH 40/62] Ensure clips have auto-resizing enabled by default (#7874) A follow up to #7477, which ensures clips have auto-resizing enabled by default. This is needed because auto-resizing was the default behavior, but the code tried to use this newly added attribute with a default of 0 (i.e., auto resizing is not enabled). --- src/core/AutomationClip.cpp | 2 +- src/core/PatternClip.cpp | 2 +- src/core/SampleClip.cpp | 2 +- src/tracks/MidiClip.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/AutomationClip.cpp b/src/core/AutomationClip.cpp index ba2ffe1f3..56ba8e9cf 100644 --- a/src/core/AutomationClip.cpp +++ b/src/core/AutomationClip.cpp @@ -865,7 +865,7 @@ void AutomationClip::loadSettings( const QDomElement & _this ) "prog" ).toInt() ) ); setTension( _this.attribute( "tens" ) ); setMuted(_this.attribute( "mute", QString::number( false ) ).toInt() ); - setAutoResize(_this.attribute("autoresize").toInt()); + setAutoResize(_this.attribute("autoresize", "1").toInt()); setStartTimeOffset(_this.attribute("off").toInt()); for( QDomNode node = _this.firstChild(); !node.isNull(); diff --git a/src/core/PatternClip.cpp b/src/core/PatternClip.cpp index 01bd48336..abc8a65fd 100644 --- a/src/core/PatternClip.cpp +++ b/src/core/PatternClip.cpp @@ -80,7 +80,7 @@ void PatternClip::loadSettings(const QDomElement& element) movePosition( element.attribute( "pos" ).toInt() ); } changeLength( element.attribute( "len" ).toInt() ); - setAutoResize(element.attribute("autoresize").toInt()); + setAutoResize(element.attribute("autoresize", "1").toInt()); setStartTimeOffset(element.attribute("off").toInt()); if (static_cast(element.attribute("muted").toInt()) != isMuted()) { diff --git a/src/core/SampleClip.cpp b/src/core/SampleClip.cpp index 06e1af99d..5c04287c4 100644 --- a/src/core/SampleClip.cpp +++ b/src/core/SampleClip.cpp @@ -356,7 +356,7 @@ void SampleClip::loadSettings( const QDomElement & _this ) changeLength( _this.attribute( "len" ).toInt() ); setMuted( _this.attribute( "muted" ).toInt() ); setStartTimeOffset( _this.attribute( "off" ).toInt() ); - setAutoResize(_this.attribute("autoresize").toInt()); + setAutoResize(_this.attribute("autoresize", "1").toInt()); if (_this.hasAttribute("color")) { diff --git a/src/tracks/MidiClip.cpp b/src/tracks/MidiClip.cpp index ca17b6898..6c2244e6a 100644 --- a/src/tracks/MidiClip.cpp +++ b/src/tracks/MidiClip.cpp @@ -532,7 +532,7 @@ void MidiClip::loadSettings( const QDomElement & _this ) changeLength(len); } - setAutoResize(_this.attribute("autoresize").toInt()); + setAutoResize(_this.attribute("autoresize", "1").toInt()); setStartTimeOffset(_this.attribute("off").toInt()); emit dataChanged(); From e50f31281862ab0d58a6b398d46a757c6727a056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petar=20Kati=C4=87?= Date: Tue, 6 May 2025 01:03:53 +0200 Subject: [PATCH 41/62] Add option to clear all notes in SlicerT (#7850) --- plugins/SlicerT/SlicerTView.cpp | 27 +++++++++++++++++++--- plugins/SlicerT/SlicerTView.h | 2 ++ plugins/SlicerT/clear_slices_active.png | Bin 0 -> 1162 bytes plugins/SlicerT/clear_slices_inactive.png | Bin 0 -> 1180 bytes 4 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 plugins/SlicerT/clear_slices_active.png create mode 100644 plugins/SlicerT/clear_slices_inactive.png diff --git a/plugins/SlicerT/SlicerTView.cpp b/plugins/SlicerT/SlicerTView.cpp index 7af2db143..1fc3effe7 100644 --- a/plugins/SlicerT/SlicerTView.cpp +++ b/plugins/SlicerT/SlicerTView.cpp @@ -71,6 +71,12 @@ SlicerTView::SlicerTView(SlicerT* instrument, QWidget* parent) m_syncToggle->setToolTip(tr("Enable BPM sync")); m_syncToggle->setModel(&m_slicerTParent->m_enableSync); + m_clearButton = new PixmapButton(this, tr("Clear all slices")); + m_clearButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("clear_slices_active")); + m_clearButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("clear_slices_inactive")); + m_clearButton->setToolTip(tr("Clear all slices")); + connect(m_clearButton, &PixmapButton::clicked, this, &SlicerTView::clearSlices); + m_bpmBox = new LcdSpinBox(3, "19purple", this); m_bpmBox->setToolTip(tr("Original sample BPM")); m_bpmBox->setModel(&m_slicerTParent->m_originalBPM); @@ -111,6 +117,19 @@ Knob* SlicerTView::createStyledKnob() return newKnob; } +// Clear all notes +void SlicerTView::clearSlices() +{ + m_slicerTParent->m_slicePoints.clear(); + + // Points are added to the start (0) and end (1) of the sample, + // so the whole sample can still be copied using MIDI. + m_slicerTParent->m_slicePoints.emplace_back(0); + m_slicerTParent->m_slicePoints.emplace_back(1); + + emit m_slicerTParent->dataChanged(); +} + // copied from piano roll void SlicerTView::exportMidi() { @@ -261,7 +280,7 @@ void SlicerTView::resizeEvent(QResizeEvent* re) { m_y1 = height() - s_bottomBoxOffset; - // left box + // Left box m_noteThresholdKnob->move(s_x1 - 25, m_y1); m_fadeOutKnob->move(s_x2 - 25, m_y1); @@ -271,8 +290,10 @@ void SlicerTView::resizeEvent(QResizeEvent* re) m_bpmBox->move(s_x5 - 13, m_y1 + 4); m_snapSetting->move(s_x6 - 8, m_y1 + 3); - // right box - m_syncToggle->move((width() - 100), m_y1 + 5); + // Right box + // For explanation on the choice of constants, look at #7850 + m_syncToggle->move((width() - 100), m_y1 - 7); + m_clearButton->move((width() - 100), m_y1 + 17); m_folderButton->move(width() - 20, height() - s_bottomBoxHeight - s_sampleBoxHeight + 1); diff --git a/plugins/SlicerT/SlicerTView.h b/plugins/SlicerT/SlicerTView.h index f24246621..c4d0da959 100644 --- a/plugins/SlicerT/SlicerTView.h +++ b/plugins/SlicerT/SlicerTView.h @@ -49,6 +49,7 @@ class SlicerTView : public InstrumentView public slots: void exportMidi(); void openFiles(); + void clearSlices(); public: SlicerTView(SlicerT* instrument, QWidget* parent); @@ -87,6 +88,7 @@ private: LcdSpinBox* m_bpmBox; ComboBox* m_snapSetting; PixmapButton* m_syncToggle; + PixmapButton* m_clearButton; PixmapButton* m_folderButton; QPushButton* m_resetButton; diff --git a/plugins/SlicerT/clear_slices_active.png b/plugins/SlicerT/clear_slices_active.png new file mode 100644 index 0000000000000000000000000000000000000000..36b7f0f7e84e60d66ce1476e543eb9ccef0c92b4 GIT binary patch literal 1162 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 Date: Thu, 15 May 2025 22:45:17 -0400 Subject: [PATCH 42/62] Fix Crash when Reversing Empty MIDI Clip (#7893) --- src/tracks/MidiClip.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tracks/MidiClip.cpp b/src/tracks/MidiClip.cpp index 6c2244e6a..391a311ab 100644 --- a/src/tracks/MidiClip.cpp +++ b/src/tracks/MidiClip.cpp @@ -326,6 +326,8 @@ void MidiClip::setStep( int step, bool enabled ) void MidiClip::reverseNotes(const NoteVector& notes) { + if (notes.empty()) { return; } + addJournalCheckPoint(); // Find the very first start position and the very last end position of all the notes. From cb84fce599dfb657cdc9070216e36b2d30dbbb5e Mon Sep 17 00:00:00 2001 From: Dalton Messmer Date: Tue, 20 May 2025 12:04:46 -0400 Subject: [PATCH 43/62] Upgrade to Windows 2022 / Visual Studio 2022 (#7897) Upgrade to Windows 2022 build runner and Visual Studio 2022 for MSVC builds. Continue installing Qt 5.15.2 with the Visual Studio 2019 option since it doesn't provide a VS 2022 option. --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5a5a25613..f6560a23e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -339,7 +339,7 @@ jobs: CCACHE_MAXSIZE: 500M msvc: name: msvc-x64 - runs-on: windows-2019 + runs-on: windows-2022 env: CCACHE_MAXSIZE: 0 CCACHE_NOCOMPRESS: 1 From 7e02795f47b647c02450cb0b8c8a07f4e06e07a1 Mon Sep 17 00:00:00 2001 From: regulus79 <117475203+regulus79@users.noreply.github.com> Date: Wed, 28 May 2025 19:28:13 -0400 Subject: [PATCH 44/62] Don't copy DetuningHelper in Note copy constructor (#7888) Reworks how note detuning copying works so as not to perform a clip duplication and allocation by default in the constructors --------- Co-authored-by: Dalton Messmer --- include/InlineAutomation.h | 1 - include/Note.h | 13 ++-- include/shared_object.h | 89 ------------------------ src/core/Note.cpp | 31 ++++----- src/core/NotePlayHandle.cpp | 4 +- src/tracks/MidiClip.cpp | 4 +- tests/src/tracks/AutomationTrackTest.cpp | 3 +- 7 files changed, 26 insertions(+), 119 deletions(-) delete mode 100644 include/shared_object.h diff --git a/include/InlineAutomation.h b/include/InlineAutomation.h index 5928b2db4..32241d451 100644 --- a/include/InlineAutomation.h +++ b/include/InlineAutomation.h @@ -27,7 +27,6 @@ #include "AutomationNode.h" #include "AutomationClip.h" -#include "shared_object.h" namespace lmms { diff --git a/include/Note.h b/include/Note.h index 7d79eeecd..7f81f7d13 100644 --- a/include/Note.h +++ b/include/Note.h @@ -104,12 +104,15 @@ public: int key = DefaultKey, volume_t volume = DefaultVolume, panning_t panning = DefaultPanning, - DetuningHelper * detuning = nullptr ); + std::shared_ptr detuning = nullptr); Note( const Note & note ); ~Note() override; Note& operator=(const Note& note); + //! Performs a deep copy and returns an owning raw pointer + Note* clone() const; + // Note types enum class Type { @@ -237,10 +240,8 @@ public: static TimePos quantized( const TimePos & m, const int qGrid ); - DetuningHelper * detuning() const - { - return m_detuning.get(); - } + const std::shared_ptr& detuning() const { return m_detuning; } + bool hasDetuningInfo() const; bool withinRange(int tickStart, int tickEnd) const; @@ -265,7 +266,7 @@ private: panning_t m_panning; TimePos m_length; TimePos m_pos; - std::unique_ptr m_detuning; + std::shared_ptr m_detuning; Type m_type = Type::Regular; }; diff --git a/include/shared_object.h b/include/shared_object.h deleted file mode 100644 index e9fdb57a3..000000000 --- a/include/shared_object.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * shared_object.h - class sharedObject for use among other objects - * - * Copyright (c) 2006-2007 Javier Serrano Polo - * Copyright (c) 2008-2014 Tobias Doerffel - * - * This file is part of LMMS - https://lmms.io - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program (see COPYING); if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - */ - -#ifndef LMMS_SHARED_OBJECT_H -#define LMMS_SHARED_OBJECT_H - -#include - -namespace lmms -{ - -class sharedObject -{ -public: - sharedObject() : - m_referenceCount(1) - { - } - - virtual ~sharedObject() = default; - - template - static T* ref( T* object ) - { - // Incrementing an atomic reference count can be relaxed since no action - // is ever taken as a result of increasing the count. - // Other loads and stores can be reordered around this without consequence. - object->m_referenceCount.fetch_add(1, std::memory_order_relaxed); - return object; - } - - template - static void unref( T* object ) - { - // When decrementing an atomic reference count, we need to provide - // two ordering guarantees: - // 1. All reads and writes to the referenced object occur before - // the count reaches zero. - // 2. Deletion occurs after the count reaches zero. - // - // To accomplish this, each decrement must be store-released, - // and the final thread (which is deleting the referenced data) - // must load-acquire those stores. - // The simplest way to do this to give the decrement acquire-release - // semantics. - // - // See https://www.boost.org/doc/libs/1_67_0/doc/html/atomic/usage_examples.html - // for further discussion, along with a slightly more complicated - // (but possibly more performant on weakly-ordered hardware like ARM) - // approach. - const bool deleteObject = - object->m_referenceCount.fetch_sub(1, std::memory_order_acq_rel) == 1; - - if ( deleteObject ) - { - object->deleteLater(); - } - } - -private: - std::atomic_int m_referenceCount; -} ; - - -} // namespace lmms - -#endif // LMMS_SHARED_OBJECT_H diff --git a/src/core/Note.cpp b/src/core/Note.cpp index 167d75f30..de57f63d2 100644 --- a/src/core/Note.cpp +++ b/src/core/Note.cpp @@ -36,7 +36,7 @@ namespace lmms Note::Note( const TimePos & length, const TimePos & pos, int key, volume_t volume, panning_t panning, - DetuningHelper * detuning ) : + std::shared_ptr detuning ) : m_selected( false ), m_oldKey(std::clamp(key, 0, NumKeys)), m_oldPos( pos ), @@ -46,13 +46,10 @@ Note::Note( const TimePos & length, const TimePos & pos, m_volume(std::clamp(volume, MinVolume, MaxVolume)), m_panning(std::clamp(panning, PanningLeft, PanningRight)), m_length( length ), - m_pos( pos ) + m_pos(pos), + m_detuning(std::move(detuning)) { - if (detuning) - { - m_detuning = std::make_unique(*detuning); - } - else + if (!detuning) { createDetuning(); } @@ -73,12 +70,9 @@ Note::Note( const Note & note ) : m_panning( note.m_panning ), m_length( note.m_length ), m_pos( note.m_pos ), + m_detuning(note.m_detuning), m_type(note.m_type) { - if (note.m_detuning) - { - m_detuning = std::make_unique(*note.m_detuning); - } } Note& Note::operator=(const Note& note) @@ -94,16 +88,19 @@ Note& Note::operator=(const Note& note) m_length = note.m_length; m_pos = note.m_pos; m_type = note.m_type; - - if (note.m_detuning) - { - m_detuning = std::make_unique(*note.m_detuning); - } + m_detuning = note.m_detuning; return *this; } +Note* Note::clone() const +{ + Note* newNote = new Note(*this); + newNote->m_detuning = std::make_shared(*newNote->m_detuning); + return newNote; +} + Note::~Note() @@ -234,7 +231,7 @@ void Note::createDetuning() { if( m_detuning == nullptr ) { - m_detuning = std::make_unique(); + m_detuning = std::make_shared(); (void) m_detuning->automationClip(); m_detuning->setRange( -MaxDetuning, MaxDetuning, 0.5f ); m_detuning->automationClip()->setProgressionType( AutomationClip::ProgressionType::Linear ); diff --git a/src/core/NotePlayHandle.cpp b/src/core/NotePlayHandle.cpp index 0c27529df..01e78fe2e 100644 --- a/src/core/NotePlayHandle.cpp +++ b/src/core/NotePlayHandle.cpp @@ -54,7 +54,7 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack, int midiEventChannel, Origin origin ) : PlayHandle( PlayHandle::Type::NotePlayHandle, _offset ), - Note( n.length(), n.pos(), n.key(), n.getVolume(), n.getPanning(), n.detuning() ), + Note(n), m_pluginData( nullptr ), m_instrumentTrack( instrumentTrack ), m_frames( 0 ), @@ -84,7 +84,7 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack, lock(); if( hasParent() == false ) { - m_baseDetuning = new BaseDetuning( detuning() ); + m_baseDetuning = new BaseDetuning(detuning().get()); m_instrumentTrack->m_processHandles.push_back( this ); } else diff --git a/src/tracks/MidiClip.cpp b/src/tracks/MidiClip.cpp index 391a311ab..e9b507df5 100644 --- a/src/tracks/MidiClip.cpp +++ b/src/tracks/MidiClip.cpp @@ -68,7 +68,7 @@ MidiClip::MidiClip( const MidiClip& other ) : { for (const auto& note : other.m_notes) { - m_notes.push_back(new Note(*note)); + m_notes.push_back(note->clone()); } init(); @@ -197,7 +197,7 @@ TimePos MidiClip::beatClipLength() const Note * MidiClip::addNote( const Note & _new_note, const bool _quant_pos ) { - auto new_note = new Note(_new_note); + auto new_note = _new_note.clone(); if (_quant_pos && gui::getGUI()->pianoRoll()) { new_note->quantizePos(gui::getGUI()->pianoRoll()->quantization()); diff --git a/tests/src/tracks/AutomationTrackTest.cpp b/tests/src/tracks/AutomationTrackTest.cpp index b4f6effd9..18e981115 100644 --- a/tests/src/tracks/AutomationTrackTest.cpp +++ b/tests/src/tracks/AutomationTrackTest.cpp @@ -163,8 +163,7 @@ private slots: Note* note = midiClip.addNote(Note(TimePos(4, 0)), false); note->createDetuning(); - DetuningHelper* dh = note->detuning(); - auto clip = dh->automationClip(); + auto clip = note->detuning()->automationClip(); clip->setProgressionType( AutomationClip::ProgressionType::Linear ); clip->putValue(TimePos(0, 0), 0.0); clip->putValue(TimePos(4, 0), 1.0); From b6d6509068c97691d7fa7f30d317bb852da35247 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Thu, 29 May 2025 19:35:48 +0200 Subject: [PATCH 45/62] Individual knob labels rendered using the widget's font size (#7525) Adjust the `Knob` class so that it defaults to taking the font size of the knob's font into account when rendering its label. This allows to use labels of different sizes for different knobs. Previously all knob labels throughout the whole application were rendered with the same fixed font size. Hence it was not possible to adjust the label size for a single knob because this would have affected all other knobs as well. The new implementation also allows the knobs to pick up CSS rules. To be able to control the knob behavior two new constructors have been added to the `Knob` class. Both constructors are concerned with creating knobs with labels and therefore they directly take the label text as a parameter. This removes numerous explicit calls to `setLabel` in the code. There is only one constructor that allows to switch between the new behavior of taking the widget's font size into account and the old legacy behavior of always rendering with the same fixed font size of `SMALL_FONT_SIZE`. The parameter was modelled as an enum to make it easier to find the remaining knob instances that use the legacy behavior. This makes it easier to find them in case they should be removed as well. In that case the string `LegacyFixedFontSize` can be searched. The other new constructor allows to directly set the knob's (and therefore the label's) font size to a pixel value. Corresponding constructors have been added to `TempoSyncKnob`. The constructors of `KnobControl` and `CustomTextKnob` have been adjusted to take advantage of the new constructors. An usused constructor was removed in `CustomTextKnob`. The method `Knob::setLabel` has been made protected because labels should now be set through the new constructors. A new property called `m_fixedFontSizeLabelRendering` was added to the `Knob` class. It controls how the labels are rendered. Fixed font size legacy rendering can be activated by calling the protected method `setFixedFontSizeLabelRendering`. The current setting can be queried via `fixedFontSizeLabelRendering`. ## Changes in the plugins Some plugins have been switched to using layouts to organize their widgets so that they can accommodate for knobs with label sizes set by the application font. The fixed font size (legacy) rendering mode is still used in the following places: * EnvelopeAndLfoView * InstrumentSoundShapingView * InstrumentFunctionViews * EffectView * InstrumentTrackView * SampleTrackView * Delay plugin * Carla plugin # individual commit messages What follows are the individual commit messages of the commits that have been squashed into one commit. They might help in case of more detailed investigations of how things came to be. * Knob with correct label rendering Enable the knob to render the label correctly at arbitrary sizes if it's configured to do so. Otherwise it will render like before. The used mode is determined when a label is set for the knob because as long as the label is not set a knob does not have one anyway. The painting code now always renders the label with the font that's set for the widget. The are now two methods to set the label text. The new method `setLabelLegacy` renders the label as before albeit in a slightly adjusted implementation. It now sets the widget font to a fixed pixel size font and then calculates the new widget size as before, i.e. not really taking the size of the font into account. This might lead to overlaps if the font of the knob is large. The method `setLabel` now has an additional (temporary) parameter called `legacyMode`. It is by default set to `true` so that all knobs still render like they did before. This is implemented by delegating to `setLabelLegacy` if it's set to `true`. Otherwise the method calculates the new size of the widget by taking the pixmap and the label with the current font into account. Please note that as of now you must set the knob font before calling any of the methods that sets the label. This is because the new size is only calculated via these code paths. However, this is already much better than only being able to use one hard-coded label size for all knobs. * Switch from `setLabel` to `setLabelLegacy` Switch all callers of `setLabel` to `setLabelLegacy` so that it becomes obvious in which places the old knob implementation is used. * Remove parameter `legacyMode` from `setLabel` Remove the parameter `legacyMode` from `setLabel`. Add the member `m_legacyMode` as it is needed in `Knob::changeEvent` so that we do not switch the behavior when the knob is enabled/disabled. * Extract methods Extract `setLegacyMode` and `updateFixedSize`. Also add the getter `legacyMode`. * Introduce legacy knob builders Introduce legacy knob builders and apply them to the plugins. There are three new methods which encapsulate how to create a corresponding legacy knob: * `Knob::buildLegacyKnob` * `CustomTextKnob::buildLegacyKnob` * `TempoSyncKnob::buildLegacyKnob` These methods set the knob they build to legacy mode and also set a label in legacy mode. The idea is to concentrate the relevant legacy code in these methods. They will later also be useful to quickly find all the places in the application where legacy knobs are used. The three methods are applied to the plugins directory. Most plugins use the build methods to build their knobs which also enables the removal of the explicit calls to `setLabelLegacy` from their code. For some plugins their implementations were adjusted so that they can deal with showing the labels in the applicaiton font, i.e. in the font size of the widget their are contained in. Most of the times this involved removing the fixed size and putting the elements in a layout (while also removing move calls). The following LMMS plugins use the application font now and are thus better readable: * Amplifier * BassBooster * Dispersion * Flanger * Peak Controller * ReverbSC * StereoEnhancer Effect The Vectorscope now shows the "Persist." label in the same size as the label of the check boxes. Setting an empty label was removed for Lb302. * Legacy knob builders in GUI Apply the legacy knob builders in the GUI components. Most components use the legacy knobs until they can be redesigned: * Effect view ("W/D", "DECAY", "GATE") * LFO Controller * Instrument window Everything related to the instrument window is for now kept to use the legacy knobs and should be adjusted at a later point to be more flexible: * Envelope and LFO * Functions * Sound Shaping The Instrument and sample track both use the legacy knobs for the "VOL" and "PAN" knobs. This might be adjusted later. The following components now render the labels of their knobs with the application font size: * MIDI CC Rack * The class `LadspaControlView`, which is not in used anymore Some vertical spacing was added to the MIDI CC Rack for slightly improved separation of the elements. The knobs are center aligned in the layout so that the transition between element under and over "CC 100" is cleaner. Setting the models in an explicit loop was removed and is now done when the knobs are created. ## Technical details Extend `Knob::buildLegacyKnob` with the option to also set the name of the knob. This is needed for some changes in this PR. The method `KnobControl::setText` now needs to distinguish between legacy mode and non-legacy mode. * Remove `Knob::setLabelLegacy` Remove `Knob::setLabelLegacy`. Instead make sure that the `Knob` updates its size in the following situations: * The label is set. * The font changes. * Legacy mode is set or unset (already implemented). The handling of font changes has been added to `Knob::changeEvent`. The update in case of a changed label is added to `Knob::setLabel`. Every client that called `setLabelLegacy` now also sets the legacy font in size `SMALL_FONT_SIZE` as this was previously done in `setLabelLegacy`. The label is set via `setLabel` now. Both actions should result in an up-to-date size. The method `KnobControl::setText` now only sets the label via `setLabel`, assuming that the wrapped knob was already configured correctly to either be a legacy knob or not. * Use descent to calculate base line Use the descent of the font to calculate the distance of the base line from the bottom of the knob widget if we are not in legacy mode. In legacy mode we still assume the descent to have a value of 2, i.e. the base line will always have a distance of 2 from the bottom of the knob widget regardless of the used font. Also refactor the code a bit to make it more manageable. * Extract `Knob::drawLabel` Extract the method `Knob::drawLabel` which draws the label. It is called from `paintEvent`. * Use non-legacy knobs for instrument and sample track Use non-legacy knobs for the "VOL" and "PAN" knobs of the instrument and sample track. This gives a bit more separation between the knob and the label but to make this work the font size had to be decreased by one pixel. * Introduce `buildKnobWithSmallPixelFont` Introduce the builder method `buildKnobWithSmallPixelFont` in `Knob` and `TempoSyncKnob`. It creates a non-legacy knob with a small pixel sized font, i.e. it still uses the small font but with a corrected size computation and corrected space between the knob and the label. It is mostly used in places with manual layouts where there's enough space to have the bit of extra space between the knob and the label. The following plugins use these knobs: * Bitcrush * Crossover EQ * Dual Filter * Dynamics Processor * Multitap Echo * Spectrum analyzer * Mallets * Waveshaper * ZynAddSubFx The "IN" and "OUT" label of the Bitcrush plugin use the default fixed font size now because the plugin uses a pixel based layout. Using the point based application font looked off. They are also used in the following component: * Effect view, i.e. the "W/D", "DECAY", "GATE" knobs of an effect * LFO Controller * Non-legacy knobs for VSTs Use non-legacy knobs with small pixel fonts for the parameter lists of VST instruments and effects. This is accomplished by renaming `CustomTextKnob::buildLegacyKnob` to `buildKnobWithSmallPixelFont` and removing the call to `setLegacyMode`. * Fix styled knobs Fix the handling of styled knobs which are created in non-legacy mode. Styled knobs do not use pixmaps and have no labels. Their size is set from the outside and they are painted within these limits. Hence we should not compute a new size from a pixmap and/or label in `Knob::updateFixedSize`. This fixes the following plugins: * FreeBoy * Kicker * Monstro * Nescaline * Opulenz * Organic * Sf2 Player * sfxr * SID * SlicerT * Triple * Watsyn * Xpressive The functionality broke with commit defa8c0180e. An alternative would have been to check for a styled knob in the contructor or `initUI` method and to set the legacy flag for these. The best solution would likely be to create an own class for styled knobs and to pull that functionality out of `Knob` because they somewhat clash in their handling. * Code review changes Parameter whitespaces in the builder methods of `Knob`. Use `adjustedToPixelSize` in `InstrumentTrackView` and `SampleTrackView`. * Code review changes Make the code that computes the new fixed size in legacy more readable even if it is just legacy code that's was not touched. Add some code documentation. Other cosmetic changes: * Whitespace adjustments * Remove unused parameter in `paintEvent` * Rename `knob_num` to `knobNum` * Add documentation for legacy mode Add some documentation which explains what the effects of legacy mode are. * Code review Remove unnecessary dereference. Also remove unncessary code repetition by introducing `currentParamModel`. * Decrease the label size of some knobs Decrease the size of the following knob labels to 8 pixels: * "VOL" and "PAN" in the instrument and sample track views * "W/D", "DECAY" and "GATE" in the effect view Technically this is accomplished by introducing `Knob::buildKnobWithFixedPixelFont` and `TempoSyncKnob::buildKnobWithFixedPixelFont`. Both versions of `buildKnobWithSmallPixelFont` now also delegate to the new methods. * Adjustments to CrossoverEQControlDialog Commit the adjustments that were done to `CrossoverEQControlDialog` which I had forgotten to add after fixing the merge. * Fix formatting of CrossoverEQControlDialog Fix the formatting of `CrossoverEQControlDialog` which got messed up after copying the code from the current version on GitHub. * Code review changes Use `std::max` instead of `qMax`. Remove some unnecessary whitespace. * Protected legacy mode methods Make `legacyMode` and `setLegacyMode` protected to ensure that legacy knobs can only be built using the factory method `buildLegacyKnob`. In the long term legacy mode should be removed. * Code review: remove indexed access The original request in the code review was to use `size_t` instead of `uint32_t` in the for-loop. However it is possible to completely remove the indexed access and to turn it into a simple iterated for-loop. Also remove code repetition in the calculation of the maximum knob width of the group. Use std::max instead of manual management. * Fix u_int16_t to uint16_t This should hopefully fix the WIndows builds. * Fix AudioFileProcessor knobs Fix a problem with the `AudioFileProcessorWaveView::knob` which is caused by the fact the this knob uses the pixmap based knob type `Bright26` without a label. Most other knobs that inherit from `Knob` set their knob type to `Styled` which means that no pixmap is used to render the knob. In the specific case the knob instance is created and the contructor runs. In the constructor the AFP knob is set to a fixed size of (37,47). However, at a later point the method `Knob::changeEvent` is triggered by Qt due to a font change. This in turn calls `Knob::updateFixedSize` which then recomputes the fixed size and effectively changes the width of the knob to the width of the pixmap which is 27. Because the knob previously was rendered centered with a width of 37 this means that the knob is now effectively shifted by five pixels to the left. This commit counters this effect by moving the affected AFP knobs five pixels to the right. A visual difference between the fixed version and the current master showed no differences. So this should fix the problem. Because setting the knob to a fixed size of (37,47) does not have any lasting effect anyway the code is removed from the constructor of the AFP knob. * Use legacy knobs in EffectView * Legacy knobs for instrument & sample Use legacy knobs for the instrument and sample track view ("VOL", "PAN"). * Add documentation to Knob builder methods Add some documentation to the `Knob` builder methods. Mark `buildLegacyKnob` as deprecated and note that it should not be used in new code. * Ensure legancy rendering for legacy knobs Ensure that legacy knobs are always rendered at a size of 12 pixels, i.e. `SMALL_FONT_SIZE`. The previous implementation used the font metrics of the knob's current font to compute the new fixed size and to render the label. It assumed that the knob was created using `Knob::buildLegacyKnob` and that therefore the font is set to 12 pixels. However, this meant that legacy knobs can still be affected by style sheet settings. The following CSS rule for example resulted in legacy knobs with a larger font size: ``` * { font-size: 18px; } ``` The fix is to use a font with a size of `SMALL_FONT_SIZE` when calculating the fixed size of the `Knob` widget and when rendering it if we are in legacy mode. This ensures that a legacy knob is unaffected by CSS rules. The non-legacy knob still uses the widget's font size and therefore it is affected by CSS rules. However, this is a feature and not a bug because when for example using a rule like the one above the knob does exactly what it's asked to do. * Remove unused constructor Remove an unused constructor from CustomTextKnob * Remove Knob::buildKnobWithSmallPixelFont Remove the builder method `Knob::buildKnobWithSmallPixelFont` and replace it with an equivalent new contstructor which takes the same arguments as the build method. * Remove Knob::buildKnobWithFixedPixelFont Remove `Knob::buildKnobWithFixedPixelFont` as it is not used anymore. Previously it was delegated to by the now removed method `Knob::buildKnobWithSmallPixelFont`. * Constructor for knobs with pixel size labels Remove `TempoSyncKnob::TempoSyncKnob` and add an equivalent constructor. Make `buildKnobWithSmallPixelFont` use the new constructor. * Remove TempoSyncKnob::buildKnobWithSmallPixelFont Remove `TempoSyncKnob::buildKnobWithSmallPixelFont` and make clients use the new constructor. * Remove CustomTextKnob::buildKnobWithSmallPixelFont Remove `CustomTextKnob::buildKnobWithSmallPixelFont` and extend the constructor so that it also takes a label. Previously all constructions went through the build method and now all constructions use the extended constructor. * Knob constructors whichKnob constructors which take labels Add constructors for `Knob` and `TempoSyncKnob` which also take the label text. Make setLabel protected as most knobs should know their labels at construction time. This prevents "chatty" code like the following example: ``` Knob* knob = new Knob(KnobType::Bright26, this); knob->setLabel("My label"); ``` This now becomes a simple a one-liner: ``` Knob* knob = new Knob(KnobType::Bright26, "My label", this); ``` The constructor of `KnobControl` also had to be extended with the label text because it cannot access the setLabel of the Knob that it manages. However, it can pass the text during construction. Its implementation of the virtual method `Control::setText` becomes empty due to this. The `KnobControl` is currently only used in `Lv2ViewProc::Lv2ViewProc`. Here the `KnobControl` is created by passing the port name into the constructor. However, the virtual method `setText` is still called in line 91 for all other implementations. Add documentation for the constructors. * Remove Knob::buildLegacyKnob Remove `Knob::buildLegacyKnob` by extending the very similar constructor with an enum that indicates whether the constructed `Knob` should be in legacy mode or not. The default is to build a non-legacy `Knob`. Wherever `Knob::buildLegacyKnob` was called the constructor is now called with `Knob::Mode::Legacy` as the parameter. In some places where the onject name is set `Knob::Mode::NonLegacy` has to be added explicitly. * Remove TempoSyncKnob::buildLegacyKnob Remove `TempoSyncKnob::buildLegacyKnob` by extending the very similar constructor with an enum that indicates whether the constructed `TempoSyncKnob` should be in legacy mode or not. The default is to build a non-legacy `TempoSyncKnob`. Wherever `TempoSyncKnob::buildLegacyKnob` was called the constructor is now called with `Knob::Mode::Legacy` as the parameter. * Vertical spacing for Peak Controller Add a vertical spacing of 10 pixel between the knobs of the Peak Controller. * Peak Controller: use default margins Remove the specific call to `setContentsMargins` from the Peak Controller so that the main layout uses Qt's default margins. Also remove the spacing again. * Rename the enum `Knob::Mode` Rename the enum `Knob::Mode` and its values so that they better describe what they influence. `Knob::Mode` is renamed to `Knob::LabelRendering` to indicate that its value affects the label rendering. The value `NonLegacy` is now called `WidgetFont` to indicate that the knob uses the font settings of the widget when rendering the label. The value `Legacy` is now called `LegacyFixedFontSize` to indicate that it's a legacy behavior which uses a fixed font size that does not adhere to the font size that's set for the widget's font. Adjust all callers accordingly. * Add TempoSyncKnob documentation Document the constructor of `TempoSyncKnob` that can be used to set the label rendering to lecacy mode. * Name adjustments and parameter removal Rename `m_legacyMode` to `m_fixedFontSizeLabelRendering`. Rename the method `legacyMode` to `fixedFontSizeLabelRendering`. Rename `setLegacyMode` to `setFixedFontSizeLabelRendering`. Also remove the boolean parameter from the method as it was only called with `true` anyway. --- include/Controls.h | 2 +- include/CustomTextKnob.h | 4 +- include/Knob.h | 90 ++++++++++++++- include/TempoSyncKnob.h | 23 ++++ plugins/Amplifier/AmplifierControlDialog.cpp | 20 ++-- .../AudioFileProcessorView.cpp | 6 +- .../AudioFileProcessorWaveView.h | 1 - .../BassBooster/BassBoosterControlDialog.cpp | 10 +- plugins/Bitcrush/BitcrushControlDialog.cpp | 26 ++--- plugins/CarlaBase/Carla.cpp | 34 +++--- .../CrossoverEQ/CrossoverEQControlDialog.cpp | 4 +- plugins/Delay/DelayControlsDialog.cpp | 12 +- .../Dispersion/DispersionControlDialog.cpp | 25 ++-- .../DualFilter/DualFilterControlDialog.cpp | 4 +- .../DynamicsProcessorControlDialog.cpp | 13 +-- plugins/Flanger/FlangerControlsDialog.cpp | 44 ++++--- plugins/Lb302/Lb302.cpp | 6 - .../MultitapEchoControlDialog.cpp | 10 +- .../PeakControllerEffectControlDialog.cpp | 20 +--- plugins/ReverbSC/ReverbSCControlDialog.cpp | 26 ++--- plugins/SpectrumAnalyzer/SaControlsDialog.cpp | 25 ++-- .../StereoEnhancerControlDialog.cpp | 3 +- plugins/Stk/Mallets/Mallets.cpp | 49 +++----- plugins/Vestige/Vestige.cpp | 8 +- plugins/VstEffect/VstEffectControls.cpp | 8 +- .../WaveShaper/WaveShaperControlDialog.cpp | 7 +- plugins/ZynAddSubFx/ZynAddSubFx.cpp | 21 ++-- src/gui/Controls.cpp | 10 +- src/gui/EffectView.cpp | 10 +- src/gui/LadspaControlView.cpp | 5 +- src/gui/LfoControllerDialog.cpp | 13 +-- src/gui/Lv2ViewBase.cpp | 2 +- src/gui/MidiCCRackView.cpp | 18 ++- src/gui/instrument/EnvelopeAndLfoView.cpp | 6 +- .../instrument/InstrumentFunctionViews.cpp | 36 ++---- .../instrument/InstrumentSoundShapingView.cpp | 6 +- src/gui/tracks/InstrumentTrackView.cpp | 9 +- src/gui/tracks/SampleTrackView.cpp | 10 +- src/gui/widgets/CustomTextKnob.cpp | 13 ++- src/gui/widgets/Knob.cpp | 109 +++++++++++++++--- src/gui/widgets/TempoSyncKnob.cpp | 21 +++- 41 files changed, 437 insertions(+), 332 deletions(-) diff --git a/include/Controls.h b/include/Controls.h index 5ed19027e..243f77690 100644 --- a/include/Controls.h +++ b/include/Controls.h @@ -80,7 +80,7 @@ public: FloatModel* model() override; AutomatableModelView* modelView() override; - KnobControl(QWidget* parent = nullptr); + KnobControl(const QString& text, QWidget* parent = nullptr); ~KnobControl() override = default; }; diff --git a/include/CustomTextKnob.h b/include/CustomTextKnob.h index 31a58415e..34434f3ae 100644 --- a/include/CustomTextKnob.h +++ b/include/CustomTextKnob.h @@ -36,9 +36,7 @@ class LMMS_EXPORT CustomTextKnob : public Knob protected: inline void setHintText( const QString & _txt_before, const QString & _txt_after ) {} // inaccessible public: - CustomTextKnob( KnobType _knob_num, QWidget * _parent = nullptr, const QString & _name = QString(), const QString & _value_text = QString() ); - - CustomTextKnob( QWidget * _parent = nullptr, const QString & _name = QString(), const QString & _value_text = QString() ); //!< default ctor + CustomTextKnob( KnobType _knob_num, const QString& label, QWidget * _parent = nullptr, const QString & _name = QString(), const QString & _value_text = QString() ); CustomTextKnob( const Knob& other ) = delete; diff --git a/include/Knob.h b/include/Knob.h index 3c3339a6f..5ec8fd70f 100644 --- a/include/Knob.h +++ b/include/Knob.h @@ -77,11 +77,65 @@ class LMMS_EXPORT Knob : public FloatModelEditorBase void onKnobNumUpdated(); //!< to be called when you updated @a m_knobNum public: + /** + * @brief Determines how the label of the knob is rendered. + * + * Labels can be rendered using the font that is set for the knob or using a + * font with a fixed size which is determined by SMALL_FONT_SIZE. + */ + enum class LabelRendering + { + /** + * @brief Renders the label using the font that is set for the widget. + * + * The space that's needed for the label is determined using the font metrics of the knob's font. + */ + WidgetFont, + + /** + * @brief Renders the labels in legacy mode. This uses a fixed font size and does not adhere + * to the font size that's set for the widget's font. + * + * @deprecated Do not use this mode in new code as it is considered deprecated and might be removed in the future. + */ + LegacyFixedFontSize + }; + + /** + * @brief Construct a Knob with the given style and no label. + * + * @param _knob_num Style of the knob + * @param _parent Parent widget + * @param _name Object name of the widget + */ Knob( KnobType _knob_num, QWidget * _parent = nullptr, const QString & _name = QString() ); + + /** + * @brief Construct a Knob with the given style and label text. + * + * @param knobNum Style of the knob + * @param labelText Text for the label + * @param parent Parent widget + * @param labelRendering Determines if the label uses the widget font or a font with a fixed size of 12 pixels (LegacyFixedFontSize). The default is to use the widget font. + * @param name Object name of the widget + */ + Knob(KnobType knobNum, const QString& labelText, QWidget* parent = nullptr, LabelRendering labelRendering = LabelRendering::WidgetFont, const QString& name = QString()); + + /** + * @brief Constructs a knob with a label font in the pixel size. + * + * @param knobNum Style of the knob + * @param labelText Text for the label + * @param labelPixelSize Pixel size for the label + * @param parent Parent widget + * @param name Object name of the widget + */ + Knob(KnobType knobNum, const QString& labelText, int labelPixelSize, QWidget* parent, const QString& name = QString()); + Knob( QWidget * _parent = nullptr, const QString & _name = QString() ); //!< default ctor + Knob( const Knob& other ) = delete; - void setLabel( const QString & txt ); void setHtmlLabel( const QString &htmltxt ); void setTotalAngle( float angle ); @@ -113,15 +167,44 @@ public: protected: - void paintEvent( QPaintEvent * _me ) override; + void setLabel(const QString& txt); + + void paintEvent(QPaintEvent*) override; void changeEvent(QEvent * ev) override; + /*! + * Affects how the label of the knob is rendered. + * + * The default mode returns false. The height of the label text is taken into account when a new fixed + * size is computed for the Knob. When the label text is painted the descent of the font is used to + * compute the base line. The default mode returns false. + * + * Enabling fixed font size rendering mode leads to the following behavior: + * * The height of the label is not taken into account when the new fixed height of the Knob is computed. + * Instead a fixed size of 10 is added for the label. + * * When the knob is painted the baseline of the font is always set to 2 pixels away from the lower side + * of the Knob's rectangle. + * * The label is always rendered with a size of SMALL_FONT_SIZE. + */ + bool fixedFontSizeLabelRendering() const { return m_fixedFontSizeLabelRendering; } + + /*! + * Set the button to legacy rendering mode which uses a fixed font size and that does not take the size + * of the widget's font into account. + * + * This can be thought of as a legacy mode which reinstates the old behavior of the knob. + * + * @see fixedFontSizeLabelRendering(). + */ + void setFixedFontSizeLabelRendering(); + private: QLineF calculateLine( const QPointF & _mid, float _radius, float _innerRadius = 1) const; void drawKnob( QPainter * _p ); + void drawLabel(QPainter& p); bool updateAngle(); int angleFromValue( float value, float minValue, float maxValue, float totalAngle ) const @@ -129,7 +212,10 @@ private: return static_cast( ( value - 0.5 * ( minValue + maxValue ) ) / ( maxValue - minValue ) * m_totalAngle ) % 360; } + void updateFixedSize(); + QString m_label; + bool m_fixedFontSizeLabelRendering = false; bool m_isHtmlLabel; QTextDocument* m_tdRenderer; diff --git a/include/TempoSyncKnob.h b/include/TempoSyncKnob.h index b86320d13..656f145ab 100644 --- a/include/TempoSyncKnob.h +++ b/include/TempoSyncKnob.h @@ -42,6 +42,29 @@ class LMMS_EXPORT TempoSyncKnob : public Knob Q_OBJECT public: TempoSyncKnob( KnobType knobNum, QWidget* parent = nullptr, const QString& name = QString() ); + + /** + * @brief Construct a TempoSyncKnob with the given style and label text. + * + * @param knobNum Style of the knob + * @param labelText Text for the label + * @param parent Parent widget + * @param labelRendering Determines if the label uses the widget font or a font with a fixed size of 12 pixels (LegacyFixedFontSize). The default is to use the widget font. + * @param name Object name of the widget + */ + TempoSyncKnob(KnobType knobNum, const QString& labelText, QWidget* parent = nullptr, LabelRendering labelRendering = LabelRendering::WidgetFont, const QString& name = QString()); + + /** + * @brief Constructs a tempo sync knob with a label font in the pixel size. + * + * @param knobNum Style of the knob + * @param labelText Text for the label + * @param labelPixelSize Pixel size for the label + * @param parent Parent widget + * @param name Object name of the widget + */ + TempoSyncKnob(KnobType knobNum, const QString& labelText, int labelPixelSize, QWidget* parent, const QString& name = QString()); + ~TempoSyncKnob() override; const QString & syncDescription(); diff --git a/plugins/Amplifier/AmplifierControlDialog.cpp b/plugins/Amplifier/AmplifierControlDialog.cpp index 7d303d0d1..9bf0bb649 100644 --- a/plugins/Amplifier/AmplifierControlDialog.cpp +++ b/plugins/Amplifier/AmplifierControlDialog.cpp @@ -28,6 +28,9 @@ #include "embed.h" #include "Knob.h" +#include + + namespace lmms::gui { @@ -38,23 +41,22 @@ AmplifierControlDialog::AmplifierControlDialog(AmplifierControls* controls) : QPalette pal; pal.setBrush(backgroundRole(), PLUGIN_NAME::getIconPixmap("artwork")); setPalette(pal); - setFixedSize(100, 110); + + QGridLayout* gridLayout = new QGridLayout(this); - auto makeKnob = [this](int x, int y, const QString& label, const QString& hintText, const QString& unit, FloatModel* model, bool isVolume) + auto makeKnob = [this](const QString& label, const QString& hintText, const QString& unit, FloatModel* model, bool isVolume) { - Knob* newKnob = new Knob(KnobType::Bright26, this); - newKnob->move(x, y); + Knob* newKnob = new Knob(KnobType::Bright26, label, this); newKnob->setModel(model); - newKnob->setLabel(label); newKnob->setHintText(hintText, unit); newKnob->setVolumeKnob(isVolume); return newKnob; }; - makeKnob(16, 10, tr("VOL"), tr("Volume:"), "%", &controls->m_volumeModel, true); - makeKnob(57, 10, tr("PAN"), tr("Panning:"), "%", &controls->m_panModel, false); - makeKnob(16, 65, tr("LEFT"), tr("Left gain:"), "%", &controls->m_leftModel, true); - makeKnob(57, 65, tr("RIGHT"), tr("Right gain:"), "%", &controls->m_rightModel, true); + gridLayout->addWidget(makeKnob(tr("VOL"), tr("Volume:"), "%", &controls->m_volumeModel, true), 0, 0, Qt::AlignHCenter); + gridLayout->addWidget(makeKnob(tr("PAN"), tr("Panning:"), "%", &controls->m_panModel, false), 0, 1, Qt::AlignHCenter); + gridLayout->addWidget(makeKnob(tr("LEFT"), tr("Left gain:"), "%", &controls->m_leftModel, true), 1, 0, Qt::AlignHCenter); + gridLayout->addWidget(makeKnob(tr("RIGHT"), tr("Right gain:"), "%", &controls->m_rightModel, true), 1, 1, Qt::AlignHCenter); } } // namespace lmms::gui diff --git a/plugins/AudioFileProcessor/AudioFileProcessorView.cpp b/plugins/AudioFileProcessor/AudioFileProcessorView.cpp index 298e79c5e..02098c540 100644 --- a/plugins/AudioFileProcessor/AudioFileProcessorView.cpp +++ b/plugins/AudioFileProcessor/AudioFileProcessorView.cpp @@ -120,15 +120,15 @@ AudioFileProcessorView::AudioFileProcessorView(Instrument* instrument, m_ampKnob->setHintText(tr("Amplify:"), "%"); m_startKnob = new AudioFileProcessorWaveView::knob(this); - m_startKnob->move(45, 108); + m_startKnob->move(50, 108); m_startKnob->setHintText(tr("Start point:"), ""); m_endKnob = new AudioFileProcessorWaveView::knob(this); - m_endKnob->move(125, 108); + m_endKnob->move(130, 108); m_endKnob->setHintText(tr("End point:"), ""); m_loopKnob = new AudioFileProcessorWaveView::knob(this); - m_loopKnob->move(85, 108); + m_loopKnob->move(90, 108); m_loopKnob->setHintText(tr("Loopback point:"), ""); // interpolation selector diff --git a/plugins/AudioFileProcessor/AudioFileProcessorWaveView.h b/plugins/AudioFileProcessor/AudioFileProcessorWaveView.h index 6440570e6..69dea0b1b 100644 --- a/plugins/AudioFileProcessor/AudioFileProcessorWaveView.h +++ b/plugins/AudioFileProcessor/AudioFileProcessorWaveView.h @@ -73,7 +73,6 @@ public: m_waveView(0), m_relatedKnob(0) { - setFixedSize(37, 47); } void setWaveView(const AudioFileProcessorWaveView* wv) diff --git a/plugins/BassBooster/BassBoosterControlDialog.cpp b/plugins/BassBooster/BassBoosterControlDialog.cpp index 9efa07c0d..fcdf10cc2 100644 --- a/plugins/BassBooster/BassBoosterControlDialog.cpp +++ b/plugins/BassBooster/BassBoosterControlDialog.cpp @@ -43,26 +43,22 @@ BassBoosterControlDialog::BassBoosterControlDialog( BassBoosterControls* control QPalette pal; pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap( "artwork" ) ); setPalette( pal ); - setFixedSize( 120, 60 ); auto tl = new QVBoxLayout(this); tl->addSpacing( 4 ); auto l = new QHBoxLayout; - auto freqKnob = new Knob(KnobType::Bright26, this); + auto freqKnob = new Knob(KnobType::Bright26, tr("FREQ"), this); freqKnob->setModel( &controls->m_freqModel ); - freqKnob->setLabel( tr( "FREQ" ) ); freqKnob->setHintText( tr( "Frequency:" ) , "Hz" ); - auto gainKnob = new Knob(KnobType::Bright26, this); + auto gainKnob = new Knob(KnobType::Bright26, tr("GAIN"), this); gainKnob->setModel( &controls->m_gainModel ); - gainKnob->setLabel( tr( "GAIN" ) ); gainKnob->setHintText( tr( "Gain:" ) , "" ); - auto ratioKnob = new Knob(KnobType::Bright26, this); + auto ratioKnob = new Knob(KnobType::Bright26, tr("RATIO"), this); ratioKnob->setModel( &controls->m_ratioModel ); - ratioKnob->setLabel( tr( "RATIO" ) ); ratioKnob->setHintText( tr( "Ratio:" ) , "" ); l->addWidget( freqKnob ); diff --git a/plugins/Bitcrush/BitcrushControlDialog.cpp b/plugins/Bitcrush/BitcrushControlDialog.cpp index 64c9b6361..3036c802a 100644 --- a/plugins/Bitcrush/BitcrushControlDialog.cpp +++ b/plugins/Bitcrush/BitcrushControlDialog.cpp @@ -29,6 +29,7 @@ #include "embed.h" #include "BitcrushControlDialog.h" #include "BitcrushControls.h" +#include "FontHelper.h" #include "LedCheckBox.h" #include "Knob.h" @@ -46,37 +47,37 @@ BitcrushControlDialog::BitcrushControlDialog( BitcrushControls * controls ) : setFixedSize( 181, 128 ); // labels + const auto labelFont = adjustedToPixelSize(font(), DEFAULT_FONT_SIZE); + auto inLabel = new QLabel(tr("IN"), this); + inLabel->setFont(labelFont); inLabel->move( 24, 15 ); auto outLabel = new QLabel(tr("OUT"), this); + outLabel->setFont(labelFont); outLabel->move( 139, 15 ); // input knobs - auto inGain = new Knob(KnobType::Bright26, this); + auto inGain = new Knob(KnobType::Bright26, tr("GAIN"), SMALL_FONT_SIZE, this); inGain->move( 16, 32 ); inGain->setModel( & controls->m_inGain ); - inGain->setLabel( tr( "GAIN" ) ); inGain->setHintText( tr( "Input gain:" ) , " dBFS" ); - auto inNoise = new Knob(KnobType::Bright26, this); + auto inNoise = new Knob(KnobType::Bright26, tr("NOISE"), SMALL_FONT_SIZE, this); inNoise->move( 14, 76 ); inNoise->setModel( & controls->m_inNoise ); - inNoise->setLabel( tr( "NOISE" ) ); inNoise->setHintText( tr( "Input noise:" ) , "%" ); // output knobs - auto outGain = new Knob(KnobType::Bright26, this); + auto outGain = new Knob(KnobType::Bright26, tr("GAIN"), SMALL_FONT_SIZE, this); outGain->move( 138, 32 ); outGain->setModel( & controls->m_outGain ); - outGain->setLabel( tr( "GAIN" ) ); outGain->setHintText( tr( "Output gain:" ) , " dBFS" ); - auto outClip = new Knob(KnobType::Bright26, this); + auto outClip = new Knob(KnobType::Bright26, tr("CLIP"), SMALL_FONT_SIZE, this); outClip->move( 138, 76 ); outClip->setModel( & controls->m_outClip ); - outClip->setLabel( tr( "CLIP" ) ); outClip->setHintText( tr( "Output clip:" ) , " dBFS"); @@ -94,24 +95,21 @@ BitcrushControlDialog::BitcrushControlDialog( BitcrushControls * controls ) : // rate crushing knobs - auto rate = new Knob(KnobType::Bright26, this); + auto rate = new Knob(KnobType::Bright26, tr("FREQ"), SMALL_FONT_SIZE, this); rate->move( 59, 32 ); rate->setModel( & controls->m_rate ); - rate->setLabel( tr( "FREQ" ) ); rate->setHintText( tr( "Sample rate:" ) , " Hz" ); - auto stereoDiff = new Knob(KnobType::Bright26, this); + auto stereoDiff = new Knob(KnobType::Bright26, tr("STEREO"), SMALL_FONT_SIZE, this); stereoDiff->move( 72, 76 ); stereoDiff->setModel( & controls->m_stereoDiff ); - stereoDiff->setLabel( tr( "STEREO" ) ); stereoDiff->setHintText( tr( "Stereo difference:" ) , "%" ); // depth crushing knob - auto levels = new Knob(KnobType::Bright26, this); + auto levels = new Knob(KnobType::Bright26, tr("QUANT"), SMALL_FONT_SIZE, this); levels->move( 92, 32 ); levels->setModel( & controls->m_levels ); - levels->setLabel( tr( "QUANT" ) ); levels->setHintText( tr( "Levels:" ) , "" ); } diff --git a/plugins/CarlaBase/Carla.cpp b/plugins/CarlaBase/Carla.cpp index 37cba078a..b572a13d4 100644 --- a/plugins/CarlaBase/Carla.cpp +++ b/plugins/CarlaBase/Carla.cpp @@ -1004,34 +1004,32 @@ void CarlaParamsView::refreshKnobs() QStringList groupNameList; groupNameList.reserve(m_carlaInstrument->m_paramGroupCount); - for (uint32_t i = 0; i < m_carlaInstrument->m_paramModels.size(); ++i) + for (const auto currentParamModel : m_carlaInstrument->m_paramModels) { - bool enabled = m_carlaInstrument->m_paramModels[i]->enabled(); - m_knobs.push_back(new Knob(KnobType::Dark28, m_inputScrollAreaWidgetContent)); - QString name = (*m_carlaInstrument->m_paramModels[i]).displayName(); - m_knobs[i]->setHintText(name, ""); - m_knobs[i]->setLabel(name); - m_knobs[i]->setObjectName(name); // this is being used for filtering the knobs. + bool enabled = currentParamModel->enabled(); + const QString name = currentParamModel->displayName(); + + auto currentKnob = new Knob(KnobType::Dark28, name, m_inputScrollAreaWidgetContent, Knob::LabelRendering::LegacyFixedFontSize); + currentKnob->setHintText(name, ""); + currentKnob->setObjectName(name); // this is being used for filtering the knobs. // Set the newly created model to the knob. - m_knobs[i]->setModel(m_carlaInstrument->m_paramModels[i]); - m_knobs[i]->setEnabled(enabled); + currentKnob->setModel(currentParamModel); + currentKnob->setEnabled(enabled); + + m_knobs.push_back(currentKnob); if (enabled) { // Collect group names - if (!groupNameList.contains(m_carlaInstrument->m_paramModels[i]->groupName())) + if (!groupNameList.contains(currentParamModel->groupName())) { - groupNameList.append(m_carlaInstrument->m_paramModels[i]->groupName()); + groupNameList.append(currentParamModel->groupName()); } - // Store biggest knob width per group (so we can calc how many - // knobs we can horizontaly fit) - uint8_t groupId = m_carlaInstrument->m_paramModels[i]->groupId(); - if (m_maxKnobWidthPerGroup[groupId] < m_knobs[i]->width()) - { - m_maxKnobWidthPerGroup[groupId] = m_knobs[i]->width(); - } + // Store biggest knob width per group (so we can calc how many knobs we can fit horizontally) + auto & maxGroupWidth = m_maxKnobWidthPerGroup[currentParamModel->groupId()]; + maxGroupWidth = std::max(maxGroupWidth, static_cast(currentKnob->width())); } } diff --git a/plugins/CrossoverEQ/CrossoverEQControlDialog.cpp b/plugins/CrossoverEQ/CrossoverEQControlDialog.cpp index c3a61890d..a04606601 100644 --- a/plugins/CrossoverEQ/CrossoverEQControlDialog.cpp +++ b/plugins/CrossoverEQ/CrossoverEQControlDialog.cpp @@ -28,6 +28,7 @@ #include "CrossoverEQControlDialog.h" #include "CrossoverEQControls.h" #include "embed.h" +#include "FontHelper.h" #include "LedCheckBox.h" #include "Knob.h" #include "Fader.h" @@ -60,9 +61,8 @@ CrossoverEQControlDialog::CrossoverEQControlDialog(CrossoverEQControls *controls const QString& label, const QString& txt_before ) { - auto k = new Knob(KnobType::Bright26, this); + auto k = new Knob(KnobType::Bright26, label, SMALL_FONT_SIZE, this); k->setModel(model); - k->setLabel(label); k->setHintText(txt_before, "Hz"); knobsLayout->addWidget(k, 0, Qt::AlignHCenter); }; diff --git a/plugins/Delay/DelayControlsDialog.cpp b/plugins/Delay/DelayControlsDialog.cpp index 065b3d1e4..6d3e4a3ec 100644 --- a/plugins/Delay/DelayControlsDialog.cpp +++ b/plugins/Delay/DelayControlsDialog.cpp @@ -44,32 +44,28 @@ DelayControlsDialog::DelayControlsDialog( DelayControls *controls ) : setPalette( pal ); setFixedSize( 300, 208 ); - auto sampleDelayKnob = new TempoSyncKnob(KnobType::Bright26, this); + auto sampleDelayKnob = new TempoSyncKnob(KnobType::Bright26, tr("DELAY"), this, Knob::LabelRendering::LegacyFixedFontSize); sampleDelayKnob->move( 10,14 ); sampleDelayKnob->setVolumeKnob( false ); sampleDelayKnob->setModel( &controls->m_delayTimeModel ); - sampleDelayKnob->setLabel( tr( "DELAY" ) ); sampleDelayKnob->setHintText( tr( "Delay time" ) + " ", " s" ); - auto feedbackKnob = new Knob(KnobType::Bright26, this); + auto feedbackKnob = new Knob(KnobType::Bright26, tr("FDBK"), this, Knob::LabelRendering::LegacyFixedFontSize); feedbackKnob->move( 11, 58 ); feedbackKnob->setVolumeKnob( true) ; feedbackKnob->setModel( &controls->m_feedbackModel); - feedbackKnob->setLabel( tr( "FDBK" ) ); feedbackKnob->setHintText( tr ( "Feedback amount" ) + " " , "" ); - auto lfoFreqKnob = new TempoSyncKnob(KnobType::Bright26, this); + auto lfoFreqKnob = new TempoSyncKnob(KnobType::Bright26, tr("RATE"), this, Knob::LabelRendering::LegacyFixedFontSize); lfoFreqKnob->move( 11, 119 ); lfoFreqKnob->setVolumeKnob( false ); lfoFreqKnob->setModel( &controls->m_lfoTimeModel ); - lfoFreqKnob->setLabel( tr( "RATE" ) ); lfoFreqKnob->setHintText( tr ( "LFO frequency") + " ", " s" ); - auto lfoAmtKnob = new TempoSyncKnob(KnobType::Bright26, this); + auto lfoAmtKnob = new TempoSyncKnob(KnobType::Bright26, tr("AMNT"), this, Knob::LabelRendering::LegacyFixedFontSize); lfoAmtKnob->move( 11, 159 ); lfoAmtKnob->setVolumeKnob( false ); lfoAmtKnob->setModel( &controls->m_lfoAmountModel ); - lfoAmtKnob->setLabel( tr( "AMNT" ) ); lfoAmtKnob->setHintText( tr ( "LFO amount" ) + " " , " s" ); auto outFader diff --git a/plugins/Dispersion/DispersionControlDialog.cpp b/plugins/Dispersion/DispersionControlDialog.cpp index 2879e7613..3a717d7aa 100644 --- a/plugins/Dispersion/DispersionControlDialog.cpp +++ b/plugins/Dispersion/DispersionControlDialog.cpp @@ -31,6 +31,8 @@ #include "LcdSpinBox.h" #include "PixmapButton.h" +#include + namespace lmms::gui { @@ -43,39 +45,38 @@ DispersionControlDialog::DispersionControlDialog(DispersionControls* controls) : QPalette pal; pal.setBrush(backgroundRole(), PLUGIN_NAME::getIconPixmap("artwork")); setPalette(pal); - setFixedSize(207, 50); + + auto layout = new QHBoxLayout(this); LcdSpinBox * m_amountBox = new LcdSpinBox(3, this, "Amount"); m_amountBox->setModel(&controls->m_amountModel); - m_amountBox->move(5, 10); m_amountBox->setLabel(tr("AMOUNT")); m_amountBox->setToolTip(tr("Number of all-pass filters")); - Knob * freqKnob = new Knob(KnobType::Bright26, this); - freqKnob->move(59, 8); + Knob * freqKnob = new Knob(KnobType::Bright26, tr("FREQ"), this); freqKnob->setModel(&controls->m_freqModel); - freqKnob->setLabel(tr("FREQ")); freqKnob->setHintText(tr("Frequency:") , " Hz"); - Knob * resoKnob = new Knob(KnobType::Bright26, this); - resoKnob->move(99, 8); + Knob * resoKnob = new Knob(KnobType::Bright26, tr("RESO"), this); resoKnob->setModel(&controls->m_resoModel); - resoKnob->setLabel(tr("RESO")); resoKnob->setHintText(tr("Resonance:") , " octaves"); - Knob * feedbackKnob = new Knob(KnobType::Bright26, this); - feedbackKnob->move(139, 8); + Knob * feedbackKnob = new Knob(KnobType::Bright26, tr("FEED"), this); feedbackKnob->setModel(&controls->m_feedbackModel); - feedbackKnob->setLabel(tr("FEED")); feedbackKnob->setHintText(tr("Feedback:") , ""); PixmapButton * dcButton = new PixmapButton(this, tr("DC Offset Removal")); - dcButton->move(176, 16); dcButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("dc_active")); dcButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("dc_inactive")); dcButton->setCheckable(true); dcButton->setModel(&controls->m_dcModel); dcButton->setToolTip(tr("Remove DC Offset")); + + layout->addWidget(m_amountBox); + layout->addWidget(freqKnob); + layout->addWidget(resoKnob); + layout->addWidget(feedbackKnob); + layout->addWidget(dcButton); } diff --git a/plugins/DualFilter/DualFilterControlDialog.cpp b/plugins/DualFilter/DualFilterControlDialog.cpp index a674a4a42..6d051b256 100644 --- a/plugins/DualFilter/DualFilterControlDialog.cpp +++ b/plugins/DualFilter/DualFilterControlDialog.cpp @@ -26,6 +26,7 @@ #include "DualFilterControlDialog.h" #include "DualFilterControls.h" +#include "FontHelper.h" #include "Knob.h" #include "LedCheckBox.h" #include "ComboBox.h" @@ -35,10 +36,9 @@ namespace lmms::gui #define makeknob( name, x, y, model, label, hint, unit ) \ - Knob * name = new Knob( KnobType::Bright26, this); \ + Knob * name = new Knob(KnobType::Bright26, label, SMALL_FONT_SIZE, this); \ (name) -> move( x, y ); \ (name) ->setModel( &controls-> model ); \ - (name) ->setLabel( label ); \ (name) ->setHintText( hint, unit ); diff --git a/plugins/DynamicsProcessor/DynamicsProcessorControlDialog.cpp b/plugins/DynamicsProcessor/DynamicsProcessorControlDialog.cpp index bd076b946..62838d5f4 100644 --- a/plugins/DynamicsProcessor/DynamicsProcessorControlDialog.cpp +++ b/plugins/DynamicsProcessor/DynamicsProcessorControlDialog.cpp @@ -28,6 +28,7 @@ #include "DynamicsProcessorControlDialog.h" #include "DynamicsProcessorControls.h" #include "embed.h" +#include "FontHelper.h" #include "Graph.h" #include "Knob.h" #include "PixmapButton.h" @@ -58,32 +59,28 @@ DynProcControlDialog::DynProcControlDialog( waveGraph->setGraphColor( QColor( 85, 204, 145 ) ); waveGraph -> setMaximumSize( 204, 205 ); - auto inputKnob = new Knob(KnobType::Bright26, this); + auto inputKnob = new Knob(KnobType::Bright26, tr("INPUT"), SMALL_FONT_SIZE, this); inputKnob -> setVolumeKnob( true ); inputKnob -> setVolumeRatio( 1.0 ); inputKnob -> move( 26, 223 ); inputKnob->setModel( &_controls->m_inputModel ); - inputKnob->setLabel( tr( "INPUT" ) ); inputKnob->setHintText( tr( "Input gain:" ) , "" ); - auto outputKnob = new Knob(KnobType::Bright26, this); + auto outputKnob = new Knob(KnobType::Bright26, tr("OUTPUT"), SMALL_FONT_SIZE, this); outputKnob -> setVolumeKnob( true ); outputKnob -> setVolumeRatio( 1.0 ); outputKnob -> move( 76, 223 ); outputKnob->setModel( &_controls->m_outputModel ); - outputKnob->setLabel( tr( "OUTPUT" ) ); outputKnob->setHintText( tr( "Output gain:" ) , "" ); - auto attackKnob = new Knob(KnobType::Bright26, this); + auto attackKnob = new Knob(KnobType::Bright26, tr("ATTACK"), SMALL_FONT_SIZE, this); attackKnob -> move( 24, 268 ); attackKnob->setModel( &_controls->m_attackModel ); - attackKnob->setLabel( tr( "ATTACK" ) ); attackKnob->setHintText( tr( "Peak attack time:" ) , "ms" ); - auto releaseKnob = new Knob(KnobType::Bright26, this); + auto releaseKnob = new Knob(KnobType::Bright26, tr("RELEASE"), SMALL_FONT_SIZE, this); releaseKnob -> move( 74, 268 ); releaseKnob->setModel( &_controls->m_releaseModel ); - releaseKnob->setLabel( tr( "RELEASE" ) ); releaseKnob->setHintText( tr( "Peak release time:" ) , "ms" ); //wavegraph control buttons diff --git a/plugins/Flanger/FlangerControlsDialog.cpp b/plugins/Flanger/FlangerControlsDialog.cpp index 3ac5dc9c6..d30c1fe60 100644 --- a/plugins/Flanger/FlangerControlsDialog.cpp +++ b/plugins/Flanger/FlangerControlsDialog.cpp @@ -29,6 +29,8 @@ #include "LedCheckBox.h" #include "TempoSyncKnob.h" +#include + namespace lmms::gui { @@ -40,55 +42,51 @@ FlangerControlsDialog::FlangerControlsDialog( FlangerControls *controls ) : QPalette pal; pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap( "artwork" ) ); setPalette( pal ); - setFixedSize( 233, 75 ); - auto delayKnob = new Knob(KnobType::Bright26, this); - delayKnob->move( 10,10 ); + auto mainLayout = new QVBoxLayout(this); + auto knobLayout = new QHBoxLayout(); + mainLayout->addLayout(knobLayout); + + auto delayKnob = new Knob(KnobType::Bright26, tr("DELAY"), this); delayKnob->setVolumeKnob( false ); delayKnob->setModel( &controls->m_delayTimeModel ); - delayKnob->setLabel( tr( "DELAY" ) ); delayKnob->setHintText( tr( "Delay time:" ) + " ", "s" ); - auto lfoFreqKnob = new TempoSyncKnob(KnobType::Bright26, this); - lfoFreqKnob->move( 48,10 ); + auto lfoFreqKnob = new TempoSyncKnob(KnobType::Bright26, tr("RATE"), this); lfoFreqKnob->setVolumeKnob( false ); lfoFreqKnob->setModel( &controls->m_lfoFrequencyModel ); - lfoFreqKnob->setLabel( tr( "RATE" ) ); lfoFreqKnob->setHintText( tr( "Period:" ) , " Sec" ); - auto lfoAmtKnob = new Knob(KnobType::Bright26, this); - lfoAmtKnob->move( 85,10 ); + auto lfoAmtKnob = new Knob(KnobType::Bright26, tr("AMNT"), this); lfoAmtKnob->setVolumeKnob( false ); lfoAmtKnob->setModel( &controls->m_lfoAmountModel ); - lfoAmtKnob->setLabel( tr( "AMNT" ) ); lfoAmtKnob->setHintText( tr( "Amount:" ) , "" ); - auto lfoPhaseKnob = new Knob(KnobType::Bright26, this); - lfoPhaseKnob->move( 123,10 ); + auto lfoPhaseKnob = new Knob(KnobType::Bright26, tr("PHASE"), this); lfoPhaseKnob->setVolumeKnob( false ); lfoPhaseKnob->setModel( &controls->m_lfoPhaseModel ); - lfoPhaseKnob->setLabel( tr( "PHASE" ) ); lfoPhaseKnob->setHintText( tr( "Phase:" ) , " degrees" ); - auto feedbackKnob = new Knob(KnobType::Bright26, this); - feedbackKnob->move( 160,10 ); + auto feedbackKnob = new Knob(KnobType::Bright26, tr("FDBK"), this); feedbackKnob->setVolumeKnob( true) ; feedbackKnob->setModel( &controls->m_feedbackModel ); - feedbackKnob->setLabel( tr( "FDBK" ) ); feedbackKnob->setHintText( tr( "Feedback amount:" ) , "" ); - auto whiteNoiseKnob = new Knob(KnobType::Bright26, this); - whiteNoiseKnob->move( 196,10 ); + auto whiteNoiseKnob = new Knob(KnobType::Bright26, tr("NOISE"), this); whiteNoiseKnob->setVolumeKnob( true) ; whiteNoiseKnob->setModel( &controls->m_whiteNoiseAmountModel ); - whiteNoiseKnob->setLabel( tr( "NOISE" ) ); whiteNoiseKnob->setHintText( tr( "White noise amount:" ) , "" ); + knobLayout->addWidget(delayKnob); + knobLayout->addWidget(lfoFreqKnob); + knobLayout->addWidget(lfoAmtKnob); + knobLayout->addWidget(lfoPhaseKnob); + knobLayout->addWidget(feedbackKnob); + knobLayout->addWidget(whiteNoiseKnob); + auto invertCb = new LedCheckBox(tr("Invert"), this); - invertCb->move( 10,53 ); - - - + + mainLayout->addWidget(invertCb, 0, Qt::AlignLeft); } diff --git a/plugins/Lb302/Lb302.cpp b/plugins/Lb302/Lb302.cpp index 5a085df4d..ab910d134 100644 --- a/plugins/Lb302/Lb302.cpp +++ b/plugins/Lb302/Lb302.cpp @@ -831,22 +831,18 @@ Lb302SynthView::Lb302SynthView( Instrument * _instrument, QWidget * _parent ) : m_vcfCutKnob = new Knob( KnobType::Bright26, this ); m_vcfCutKnob->move( 75, 130 ); m_vcfCutKnob->setHintText( tr( "Cutoff Freq:" ), "" ); - m_vcfCutKnob->setLabel( "" ); m_vcfResKnob = new Knob( KnobType::Bright26, this ); m_vcfResKnob->move( 120, 130 ); m_vcfResKnob->setHintText( tr( "Resonance:" ), "" ); - m_vcfResKnob->setLabel( "" ); m_vcfModKnob = new Knob( KnobType::Bright26, this ); m_vcfModKnob->move( 165, 130 ); m_vcfModKnob->setHintText( tr( "Env Mod:" ), "" ); - m_vcfModKnob->setLabel( "" ); m_vcfDecKnob = new Knob( KnobType::Bright26, this ); m_vcfDecKnob->move( 210, 130 ); m_vcfDecKnob->setHintText( tr( "Decay:" ), "" ); - m_vcfDecKnob->setLabel( "" ); m_slideToggle = new LedCheckBox( "", this ); m_slideToggle->move( 10, 180 ); @@ -867,12 +863,10 @@ Lb302SynthView::Lb302SynthView( Instrument * _instrument, QWidget * _parent ) : m_slideDecKnob = new Knob( KnobType::Bright26, this ); m_slideDecKnob->move( 210, 75 ); m_slideDecKnob->setHintText( tr( "Slide Decay:" ), "" ); - m_slideDecKnob->setLabel( ""); m_distKnob = new Knob( KnobType::Bright26, this ); m_distKnob->move( 210, 190 ); m_distKnob->setHintText( tr( "DIST:" ), "" ); - m_distKnob->setLabel( tr( "")); // Shapes diff --git a/plugins/MultitapEcho/MultitapEchoControlDialog.cpp b/plugins/MultitapEcho/MultitapEchoControlDialog.cpp index e89137bf0..8a7c40a37 100644 --- a/plugins/MultitapEcho/MultitapEchoControlDialog.cpp +++ b/plugins/MultitapEcho/MultitapEchoControlDialog.cpp @@ -27,6 +27,7 @@ #include "MultitapEchoControlDialog.h" #include "MultitapEchoControls.h" #include "embed.h" +#include "FontHelper.h" #include "Graph.h" #include "LedCheckBox.h" #include "Knob.h" @@ -78,22 +79,19 @@ MultitapEchoControlDialog::MultitapEchoControlDialog( MultitapEchoControls * con // knobs - auto stepLength = new TempoSyncKnob(KnobType::Bright26, this); + auto stepLength = new TempoSyncKnob(KnobType::Bright26, tr("Length"), SMALL_FONT_SIZE, this); stepLength->move( 100, 245 ); stepLength->setModel( & controls->m_stepLength ); - stepLength->setLabel( tr( "Length" ) ); stepLength->setHintText( tr( "Step length:" ) , " ms" ); - auto dryGain = new Knob(KnobType::Bright26, this); + auto dryGain = new Knob(KnobType::Bright26, tr("Dry"), SMALL_FONT_SIZE, this); dryGain->move( 150, 245 ); dryGain->setModel( & controls->m_dryGain ); - dryGain->setLabel( tr( "Dry" ) ); dryGain->setHintText( tr( "Dry gain:" ) , " dBFS" ); - auto stages = new Knob(KnobType::Bright26, this); + auto stages = new Knob(KnobType::Bright26, tr("Stages"), SMALL_FONT_SIZE, this); stages->move( 200, 245 ); stages->setModel( & controls->m_stages ); - stages->setLabel( tr( "Stages" ) ); stages->setHintText( tr( "Low-pass stages:" ) , "x" ); // switch led diff --git a/plugins/PeakControllerEffect/PeakControllerEffectControlDialog.cpp b/plugins/PeakControllerEffect/PeakControllerEffectControlDialog.cpp index e44d5bcc2..7ce05bc45 100644 --- a/plugins/PeakControllerEffect/PeakControllerEffectControlDialog.cpp +++ b/plugins/PeakControllerEffect/PeakControllerEffectControlDialog.cpp @@ -47,35 +47,28 @@ PeakControllerEffectControlDialog::PeakControllerEffectControlDialog( QPalette pal; pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap( "artwork" ) ); setPalette( pal ); - setFixedSize( 240, 80 ); - m_baseKnob = new Knob( KnobType::Bright26, this ); - m_baseKnob->setLabel( tr( "BASE" ) ); + m_baseKnob = new Knob(KnobType::Bright26, tr("BASE"), this); m_baseKnob->setModel( &_controls->m_baseModel ); m_baseKnob->setHintText( tr( "Base:" ) , "" ); - m_amountKnob = new Knob( KnobType::Bright26, this ); - m_amountKnob->setLabel( tr( "AMNT" ) ); + m_amountKnob = new Knob(KnobType::Bright26, tr("AMNT"), this); m_amountKnob->setModel( &_controls->m_amountModel ); m_amountKnob->setHintText( tr( "Modulation amount:" ) , "" ); - m_amountMultKnob = new Knob( KnobType::Bright26, this ); - m_amountMultKnob->setLabel( tr( "MULT" ) ); + m_amountMultKnob = new Knob(KnobType::Bright26, tr("MULT"), this); m_amountMultKnob->setModel( &_controls->m_amountMultModel ); m_amountMultKnob->setHintText( tr( "Amount multiplicator:" ) , "" ); - m_attackKnob = new Knob( KnobType::Bright26, this ); - m_attackKnob->setLabel( tr( "ATCK" ) ); + m_attackKnob = new Knob(KnobType::Bright26, tr("ATCK"), this); m_attackKnob->setModel( &_controls->m_attackModel ); m_attackKnob->setHintText( tr( "Attack:" ) , "" ); - m_decayKnob = new Knob( KnobType::Bright26, this ); - m_decayKnob->setLabel( tr( "DCAY" ) ); + m_decayKnob = new Knob(KnobType::Bright26, tr("DCAY"), this); m_decayKnob->setModel( &_controls->m_decayModel ); m_decayKnob->setHintText( tr( "Release:" ) , "" ); - m_tresholdKnob = new Knob( KnobType::Bright26, this ); - m_tresholdKnob->setLabel( tr( "TRSH" ) ); + m_tresholdKnob = new Knob(KnobType::Bright26, tr("TRSH"), this); m_tresholdKnob->setModel( &_controls->m_tresholdModel ); m_tresholdKnob->setHintText( tr( "Treshold:" ) , "" ); @@ -99,7 +92,6 @@ PeakControllerEffectControlDialog::PeakControllerEffectControlDialog( ledLayout->addWidget( m_muteLed ); ledLayout->addWidget( m_absLed ); - mainLayout->setContentsMargins( 3, 10, 0, 0 ); mainLayout->addLayout( knobLayout ); mainLayout->addLayout( ledLayout ); diff --git a/plugins/ReverbSC/ReverbSCControlDialog.cpp b/plugins/ReverbSC/ReverbSCControlDialog.cpp index 615d3823e..3be156396 100644 --- a/plugins/ReverbSC/ReverbSCControlDialog.cpp +++ b/plugins/ReverbSC/ReverbSCControlDialog.cpp @@ -29,6 +29,8 @@ #include "Knob.h" #include "ReverbSCControls.h" +#include + namespace lmms::gui { @@ -40,31 +42,29 @@ ReverbSCControlDialog::ReverbSCControlDialog( ReverbSCControls* controls ) : QPalette pal; pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap( "artwork" ) ); setPalette( pal ); - setFixedSize( 185, 55 ); - auto inputGainKnob = new Knob(KnobType::Bright26, this); - inputGainKnob -> move( 16, 10 ); + auto knobLayout = new QHBoxLayout(this); + + auto inputGainKnob = new Knob(KnobType::Bright26, tr("Input"), this); inputGainKnob->setModel( &controls->m_inputGainModel ); - inputGainKnob->setLabel( tr( "Input" ) ); inputGainKnob->setHintText( tr( "Input gain:" ) , "dB" ); - auto sizeKnob = new Knob(KnobType::Bright26, this); - sizeKnob -> move( 57, 10 ); + auto sizeKnob = new Knob(KnobType::Bright26, tr("Size"), this); sizeKnob->setModel( &controls->m_sizeModel ); - sizeKnob->setLabel( tr( "Size" ) ); sizeKnob->setHintText( tr( "Size:" ) , "" ); - auto colorKnob = new Knob(KnobType::Bright26, this); - colorKnob -> move( 98, 10 ); + auto colorKnob = new Knob(KnobType::Bright26, tr("Color"), this); colorKnob->setModel( &controls->m_colorModel ); - colorKnob->setLabel( tr( "Color" ) ); colorKnob->setHintText( tr( "Color:" ) , "" ); - auto outputGainKnob = new Knob(KnobType::Bright26, this); - outputGainKnob -> move( 139, 10 ); + auto outputGainKnob = new Knob(KnobType::Bright26, tr("Output"), this); outputGainKnob->setModel( &controls->m_outputGainModel ); - outputGainKnob->setLabel( tr( "Output" ) ); outputGainKnob->setHintText( tr( "Output gain:" ) , "dB" ); + + knobLayout->addWidget(inputGainKnob); + knobLayout->addWidget(sizeKnob); + knobLayout->addWidget(colorKnob); + knobLayout->addWidget(outputGainKnob); } diff --git a/plugins/SpectrumAnalyzer/SaControlsDialog.cpp b/plugins/SpectrumAnalyzer/SaControlsDialog.cpp index 8b2c0f35e..9645f7b83 100644 --- a/plugins/SpectrumAnalyzer/SaControlsDialog.cpp +++ b/plugins/SpectrumAnalyzer/SaControlsDialog.cpp @@ -32,6 +32,7 @@ #include "ComboBox.h" #include "ComboBoxModel.h" +#include "FontHelper.h" #include "Knob.h" #include "LedCheckBox.h" #include "PixmapButton.h" @@ -236,41 +237,36 @@ SaControlsDialog::SaControlsDialog(SaControls *controls, SaProcessor *processor) controls_layout->setStretchFactor(advanced_widget, 10); // Peak envelope resolution - auto envelopeResolutionKnob = new Knob(KnobType::Small17, this); + auto envelopeResolutionKnob = new Knob(KnobType::Small17, tr("Envelope res."), SMALL_FONT_SIZE, this); envelopeResolutionKnob->setModel(&controls->m_envelopeResolutionModel); - envelopeResolutionKnob->setLabel(tr("Envelope res.")); envelopeResolutionKnob->setToolTip(tr("Increase envelope resolution for better details, decrease for better GUI performance.")); envelopeResolutionKnob->setHintText(tr("Maximum number of envelope points drawn per pixel:"), ""); advanced_layout->addWidget(envelopeResolutionKnob, 0, 0, 1, 1, Qt::AlignCenter); // Spectrum graph resolution - auto spectrumResolutionKnob = new Knob(KnobType::Small17, this); + auto spectrumResolutionKnob = new Knob(KnobType::Small17, tr("Spectrum res."), SMALL_FONT_SIZE, this); spectrumResolutionKnob->setModel(&controls->m_spectrumResolutionModel); - spectrumResolutionKnob->setLabel(tr("Spectrum res.")); spectrumResolutionKnob->setToolTip(tr("Increase spectrum resolution for better details, decrease for better GUI performance.")); spectrumResolutionKnob->setHintText(tr("Maximum number of spectrum points drawn per pixel:"), ""); advanced_layout->addWidget(spectrumResolutionKnob, 1, 0, 1, 1, Qt::AlignCenter); // Peak falloff speed - auto peakDecayFactorKnob = new Knob(KnobType::Small17, this); + auto peakDecayFactorKnob = new Knob(KnobType::Small17, tr("Falloff factor"), SMALL_FONT_SIZE, this); peakDecayFactorKnob->setModel(&controls->m_peakDecayFactorModel); - peakDecayFactorKnob->setLabel(tr("Falloff factor")); peakDecayFactorKnob->setToolTip(tr("Decrease to make peaks fall faster.")); peakDecayFactorKnob->setHintText(tr("Multiply buffered value by"), ""); advanced_layout->addWidget(peakDecayFactorKnob, 0, 1, 1, 1, Qt::AlignCenter); // Averaging weight - auto averagingWeightKnob = new Knob(KnobType::Small17, this); + auto averagingWeightKnob = new Knob(KnobType::Small17, tr("Averaging weight"), SMALL_FONT_SIZE, this); averagingWeightKnob->setModel(&controls->m_averagingWeightModel); - averagingWeightKnob->setLabel(tr("Averaging weight")); averagingWeightKnob->setToolTip(tr("Decrease to make averaging slower and smoother.")); averagingWeightKnob->setHintText(tr("New sample contributes"), ""); advanced_layout->addWidget(averagingWeightKnob, 1, 1, 1, 1, Qt::AlignCenter); // Waterfall history size - auto waterfallHeightKnob = new Knob(KnobType::Small17, this); + auto waterfallHeightKnob = new Knob(KnobType::Small17, tr("Waterfall height"), SMALL_FONT_SIZE, this); waterfallHeightKnob->setModel(&controls->m_waterfallHeightModel); - waterfallHeightKnob->setLabel(tr("Waterfall height")); waterfallHeightKnob->setToolTip(tr("Increase to get slower scrolling, decrease to see fast transitions better. Warning: medium CPU usage.")); waterfallHeightKnob->setHintText(tr("Number of lines to keep:"), ""); advanced_layout->addWidget(waterfallHeightKnob, 0, 2, 1, 1, Qt::AlignCenter); @@ -278,25 +274,22 @@ SaControlsDialog::SaControlsDialog(SaControls *controls, SaProcessor *processor) connect(&controls->m_waterfallHeightModel, &FloatModel::dataChanged, [=] {processor->reallocateBuffers();}); // Waterfall gamma correction - auto waterfallGammaKnob = new Knob(KnobType::Small17, this); + auto waterfallGammaKnob = new Knob(KnobType::Small17, tr("Waterfall gamma"), SMALL_FONT_SIZE, this); waterfallGammaKnob->setModel(&controls->m_waterfallGammaModel); - waterfallGammaKnob->setLabel(tr("Waterfall gamma")); waterfallGammaKnob->setToolTip(tr("Decrease to see very weak signals, increase to get better contrast.")); waterfallGammaKnob->setHintText(tr("Gamma value:"), ""); advanced_layout->addWidget(waterfallGammaKnob, 1, 2, 1, 1, Qt::AlignCenter); // FFT window overlap - auto windowOverlapKnob = new Knob(KnobType::Small17, this); + auto windowOverlapKnob = new Knob(KnobType::Small17, tr("Window overlap"), SMALL_FONT_SIZE, this); windowOverlapKnob->setModel(&controls->m_windowOverlapModel); - windowOverlapKnob->setLabel(tr("Window overlap")); windowOverlapKnob->setToolTip(tr("Increase to prevent missing fast transitions arriving near FFT window edges. Warning: high CPU usage.")); windowOverlapKnob->setHintText(tr("Number of times each sample is processed:"), ""); advanced_layout->addWidget(windowOverlapKnob, 0, 3, 1, 1, Qt::AlignCenter); // FFT zero padding - auto zeroPaddingKnob = new Knob(KnobType::Small17, this); + auto zeroPaddingKnob = new Knob(KnobType::Small17, tr("Zero padding"), SMALL_FONT_SIZE, this); zeroPaddingKnob->setModel(&controls->m_zeroPaddingModel); - zeroPaddingKnob->setLabel(tr("Zero padding")); zeroPaddingKnob->setToolTip(tr("Increase to get smoother-looking spectrum. Warning: high CPU usage.")); zeroPaddingKnob->setHintText(tr("Processing buffer is"), tr(" steps larger than input block")); advanced_layout->addWidget(zeroPaddingKnob, 1, 3, 1, 1, Qt::AlignCenter); diff --git a/plugins/StereoEnhancer/StereoEnhancerControlDialog.cpp b/plugins/StereoEnhancer/StereoEnhancerControlDialog.cpp index 05c78616e..1440a4be5 100644 --- a/plugins/StereoEnhancer/StereoEnhancerControlDialog.cpp +++ b/plugins/StereoEnhancer/StereoEnhancerControlDialog.cpp @@ -40,9 +40,8 @@ StereoEnhancerControlDialog::StereoEnhancerControlDialog( { auto l = new QHBoxLayout(this); - auto widthKnob = new Knob(KnobType::Bright26, this); + auto widthKnob = new Knob(KnobType::Bright26, tr("WIDTH"), this); widthKnob->setModel( &_controls->m_widthModel ); - widthKnob->setLabel( tr( "WIDTH" ) ); widthKnob->setHintText( tr( "Width:" ) , " samples" ); l->addWidget( widthKnob ); diff --git a/plugins/Stk/Mallets/Mallets.cpp b/plugins/Stk/Mallets/Mallets.cpp index fecb15a76..5c9c11cca 100644 --- a/plugins/Stk/Mallets/Mallets.cpp +++ b/plugins/Stk/Mallets/Mallets.cpp @@ -37,6 +37,7 @@ #include "AudioEngine.h" #include "ConfigManager.h" #include "Engine.h" +#include "FontHelper.h" #include "GuiApplication.h" #include "InstrumentTrack.h" @@ -454,13 +455,11 @@ MalletsInstrumentView::MalletsInstrumentView( MalletsInstrument * _instrument, connect( &_instrument->m_presetsModel, SIGNAL( dataChanged() ), this, SLOT( changePreset() ) ); - m_spreadKnob = new Knob( KnobType::Vintage32, this ); - m_spreadKnob->setLabel( tr( "Spread" ) ); + m_spreadKnob = new Knob(KnobType::Vintage32, tr("Spread"), SMALL_FONT_SIZE, this); m_spreadKnob->move( 190, 140 ); m_spreadKnob->setHintText( tr( "Spread:" ), "" ); - m_randomKnob = new Knob(KnobType::Vintage32, this); - m_randomKnob->setLabel(tr("Random")); + m_randomKnob = new Knob(KnobType::Vintage32, tr("Random"), SMALL_FONT_SIZE, this); m_randomKnob->move(190, 190); m_randomKnob->setHintText(tr("Random:"), ""); @@ -495,28 +494,23 @@ QWidget * MalletsInstrumentView::setupModalBarControls( QWidget * _parent ) auto widget = new QWidget(_parent); widget->setFixedSize( 250, 250 ); - m_hardnessKnob = new Knob( KnobType::Vintage32, widget ); - m_hardnessKnob->setLabel( tr( "Hardness" ) ); + m_hardnessKnob = new Knob(KnobType::Vintage32, tr("Hardness"), SMALL_FONT_SIZE, widget); m_hardnessKnob->move( 30, 90 ); m_hardnessKnob->setHintText( tr( "Hardness:" ), "" ); - m_positionKnob = new Knob( KnobType::Vintage32, widget ); - m_positionKnob->setLabel( tr( "Position" ) ); + m_positionKnob = new Knob(KnobType::Vintage32, tr("Position"), SMALL_FONT_SIZE, widget); m_positionKnob->move( 110, 90 ); m_positionKnob->setHintText( tr( "Position:" ), "" ); - m_vibratoGainKnob = new Knob( KnobType::Vintage32, widget ); - m_vibratoGainKnob->setLabel( tr( "Vibrato gain" ) ); + m_vibratoGainKnob = new Knob(KnobType::Vintage32, tr("Vibrato gain"), SMALL_FONT_SIZE, widget); m_vibratoGainKnob->move( 30, 140 ); m_vibratoGainKnob->setHintText( tr( "Vibrato gain:" ), "" ); - m_vibratoFreqKnob = new Knob( KnobType::Vintage32, widget ); - m_vibratoFreqKnob->setLabel( tr( "Vibrato frequency" ) ); + m_vibratoFreqKnob = new Knob(KnobType::Vintage32, tr("Vibrato frequency"), SMALL_FONT_SIZE, widget); m_vibratoFreqKnob->move( 110, 140 ); m_vibratoFreqKnob->setHintText( tr( "Vibrato frequency:" ), "" ); - m_stickKnob = new Knob( KnobType::Vintage32, widget ); - m_stickKnob->setLabel( tr( "Stick mix" ) ); + m_stickKnob = new Knob(KnobType::Vintage32, tr("Stick mix"), SMALL_FONT_SIZE, widget); m_stickKnob->move( 190, 90 ); m_stickKnob->setHintText( tr( "Stick mix:" ), "" ); @@ -531,28 +525,23 @@ QWidget * MalletsInstrumentView::setupTubeBellControls( QWidget * _parent ) auto widget = new QWidget(_parent); widget->setFixedSize( 250, 250 ); - m_modulatorKnob = new Knob( KnobType::Vintage32, widget ); - m_modulatorKnob->setLabel( tr( "Modulator" ) ); + m_modulatorKnob = new Knob(KnobType::Vintage32, tr("Modulator"), SMALL_FONT_SIZE, widget); m_modulatorKnob->move( 30, 90 ); m_modulatorKnob->setHintText( tr( "Modulator:" ), "" ); - m_crossfadeKnob = new Knob( KnobType::Vintage32, widget ); - m_crossfadeKnob->setLabel( tr( "Crossfade" ) ); + m_crossfadeKnob = new Knob(KnobType::Vintage32, tr("Crossfade"), SMALL_FONT_SIZE, widget); m_crossfadeKnob->move( 110, 90 ); m_crossfadeKnob->setHintText( tr( "Crossfade:" ), "" ); - m_lfoSpeedKnob = new Knob( KnobType::Vintage32, widget ); - m_lfoSpeedKnob->setLabel( tr( "LFO speed" ) ); + m_lfoSpeedKnob = new Knob(KnobType::Vintage32, tr("LFO speed"), SMALL_FONT_SIZE, widget); m_lfoSpeedKnob->move( 30, 140 ); m_lfoSpeedKnob->setHintText( tr( "LFO speed:" ), "" ); - m_lfoDepthKnob = new Knob( KnobType::Vintage32, widget ); - m_lfoDepthKnob->setLabel( tr( "LFO depth" ) ); + m_lfoDepthKnob = new Knob(KnobType::Vintage32, tr("LFO depth"), SMALL_FONT_SIZE, widget); m_lfoDepthKnob->move( 110, 140 ); m_lfoDepthKnob->setHintText( tr( "LFO depth:" ), "" ); - m_adsrKnob = new Knob( KnobType::Vintage32, widget ); - m_adsrKnob->setLabel( tr( "ADSR" ) ); + m_adsrKnob = new Knob(KnobType::Vintage32, tr("ADSR"), SMALL_FONT_SIZE, widget); m_adsrKnob->move( 190, 90 ); m_adsrKnob->setHintText( tr( "ADSR:" ), "" ); @@ -571,23 +560,19 @@ QWidget * MalletsInstrumentView::setupBandedWGControls( QWidget * _parent ) /* m_strikeLED = new LedCheckBox( tr( "Bowed" ), widget ); m_strikeLED->move( 138, 25 );*/ - m_pressureKnob = new Knob( KnobType::Vintage32, widget ); - m_pressureKnob->setLabel( tr( "Pressure" ) ); + m_pressureKnob = new Knob(KnobType::Vintage32, tr("Pressure"), SMALL_FONT_SIZE, widget); m_pressureKnob->move( 30, 90 ); m_pressureKnob->setHintText( tr( "Pressure:" ), "" ); -/* m_motionKnob = new Knob( KnobType::Vintage32, widget ); - m_motionKnob->setLabel( tr( "Motion" ) ); +/* m_motionKnob = new Knob(KnobType::Vintage32, tr("Motion"), SMALL_FONT_SIZE, widget); m_motionKnob->move( 110, 90 ); m_motionKnob->setHintText( tr( "Motion:" ), "" );*/ - m_velocityKnob = new Knob( KnobType::Vintage32, widget ); - m_velocityKnob->setLabel( tr( "Speed" ) ); + m_velocityKnob = new Knob(KnobType::Vintage32, tr("Speed"), SMALL_FONT_SIZE, widget); m_velocityKnob->move( 30, 140 ); m_velocityKnob->setHintText( tr( "Speed:" ), "" ); -/* m_vibratoKnob = new Knob( KnobType::Vintage32, widget, tr( "Vibrato" ) ); - m_vibratoKnob->setLabel( tr( "Vibrato" ) ); +/* m_vibratoKnob = new Knob(KnobType::Vintage32, tr("Vibrato"), SMALL_FONT_SIZE, widget); m_vibratoKnob->move( 110, 140 ); m_vibratoKnob->setHintText( tr( "Vibrato:" ), "" );*/ diff --git a/plugins/Vestige/Vestige.cpp b/plugins/Vestige/Vestige.cpp index e37292400..1bf9f257c 100644 --- a/plugins/Vestige/Vestige.cpp +++ b/plugins/Vestige/Vestige.cpp @@ -998,9 +998,11 @@ ManageVestigeInstrumentView::ManageVestigeInstrumentView( Instrument * _instrume std::snprintf(paramStr.data(), paramStr.size(), "param%d", i); s_dumpValues = dump[paramStr.data()].split(":"); - vstKnobs[ i ] = new CustomTextKnob( KnobType::Bright26, this, s_dumpValues.at( 1 ) ); - vstKnobs[ i ]->setDescription( s_dumpValues.at( 1 ) + ":" ); - vstKnobs[ i ]->setLabel( s_dumpValues.at( 1 ).left( 15 ) ); + const auto & description = s_dumpValues.at(1); + + auto knob = new CustomTextKnob(KnobType::Bright26, description.left(15), this, description); + knob->setDescription(description + ":"); + vstKnobs[i] = knob; if( !hasKnobModel ) { diff --git a/plugins/VstEffect/VstEffectControls.cpp b/plugins/VstEffect/VstEffectControls.cpp index ef8bd38d0..c01aef0ce 100644 --- a/plugins/VstEffect/VstEffectControls.cpp +++ b/plugins/VstEffect/VstEffectControls.cpp @@ -389,9 +389,11 @@ ManageVSTEffectView::ManageVSTEffectView( VstEffect * _eff, VstEffectControls * std::snprintf(paramStr.data(), paramStr.size(), "param%d", i); s_dumpValues = dump[paramStr.data()].split(":"); - vstKnobs[ i ] = new CustomTextKnob( KnobType::Bright26, widget, s_dumpValues.at( 1 ) ); - vstKnobs[ i ]->setDescription( s_dumpValues.at( 1 ) + ":" ); - vstKnobs[ i ]->setLabel( s_dumpValues.at( 1 ).left( 15 ) ); + const auto & description = s_dumpValues.at(1); + + auto knob = new CustomTextKnob(KnobType::Bright26, description.left(15), widget, description); + knob->setDescription(description + ":"); + vstKnobs[i] = knob; if( !hasKnobModel ) { diff --git a/plugins/WaveShaper/WaveShaperControlDialog.cpp b/plugins/WaveShaper/WaveShaperControlDialog.cpp index 045f84763..17955fe28 100644 --- a/plugins/WaveShaper/WaveShaperControlDialog.cpp +++ b/plugins/WaveShaper/WaveShaperControlDialog.cpp @@ -28,6 +28,7 @@ #include "WaveShaperControlDialog.h" #include "WaveShaperControls.h" #include "embed.h" +#include "FontHelper.h" #include "Graph.h" #include "Knob.h" #include "PixmapButton.h" @@ -59,20 +60,18 @@ WaveShaperControlDialog::WaveShaperControlDialog( waveGraph->setGraphColor( QColor( 85, 204, 145 ) ); waveGraph -> setMaximumSize( 204, 205 ); - auto inputKnob = new Knob(KnobType::Bright26, this); + auto inputKnob = new Knob(KnobType::Bright26, tr("INPUT"), SMALL_FONT_SIZE, this); inputKnob -> setVolumeKnob( true ); inputKnob -> setVolumeRatio( 1.0 ); inputKnob -> move( 26, 225 ); inputKnob->setModel( &_controls->m_inputModel ); - inputKnob->setLabel( tr( "INPUT" ) ); inputKnob->setHintText( tr( "Input gain:" ) , "" ); - auto outputKnob = new Knob(KnobType::Bright26, this); + auto outputKnob = new Knob(KnobType::Bright26, tr("OUTPUT"), SMALL_FONT_SIZE, this); outputKnob -> setVolumeKnob( true ); outputKnob -> setVolumeRatio( 1.0 ); outputKnob -> move( 76, 225 ); outputKnob->setModel( &_controls->m_outputModel ); - outputKnob->setLabel( tr( "OUTPUT" ) ); outputKnob->setHintText( tr( "Output gain:" ), "" ); auto resetButton = new PixmapButton(this, tr("Reset wavegraph")); diff --git a/plugins/ZynAddSubFx/ZynAddSubFx.cpp b/plugins/ZynAddSubFx/ZynAddSubFx.cpp index ef4225967..27e286b66 100644 --- a/plugins/ZynAddSubFx/ZynAddSubFx.cpp +++ b/plugins/ZynAddSubFx/ZynAddSubFx.cpp @@ -507,33 +507,26 @@ ZynAddSubFxView::ZynAddSubFxView( Instrument * _instrument, QWidget * _parent ) l->setVerticalSpacing( 16 ); l->setHorizontalSpacing( 10 ); - m_portamento = new Knob( KnobType::Bright26, this ); + m_portamento = new Knob(KnobType::Bright26, tr("PORT"), SMALL_FONT_SIZE, this); m_portamento->setHintText( tr( "Portamento:" ), "" ); - m_portamento->setLabel( tr( "PORT" ) ); - m_filterFreq = new Knob( KnobType::Bright26, this ); + m_filterFreq = new Knob(KnobType::Bright26, tr("FREQ"), SMALL_FONT_SIZE, this); m_filterFreq->setHintText( tr( "Filter frequency:" ), "" ); - m_filterFreq->setLabel( tr( "FREQ" ) ); - m_filterQ = new Knob( KnobType::Bright26, this ); + m_filterQ = new Knob(KnobType::Bright26, tr("RES"), SMALL_FONT_SIZE, this); m_filterQ->setHintText( tr( "Filter resonance:" ), "" ); - m_filterQ->setLabel( tr( "RES" ) ); - m_bandwidth = new Knob( KnobType::Bright26, this ); + m_bandwidth = new Knob(KnobType::Bright26, tr("BW"), SMALL_FONT_SIZE, this); m_bandwidth->setHintText( tr( "Bandwidth:" ), "" ); - m_bandwidth->setLabel( tr( "BW" ) ); - m_fmGain = new Knob( KnobType::Bright26, this ); + m_fmGain = new Knob(KnobType::Bright26, tr("FM GAIN"), SMALL_FONT_SIZE, this); m_fmGain->setHintText( tr( "FM gain:" ), "" ); - m_fmGain->setLabel( tr( "FM GAIN" ) ); - m_resCenterFreq = new Knob( KnobType::Bright26, this ); + m_resCenterFreq = new Knob(KnobType::Bright26, tr("RES CF"), SMALL_FONT_SIZE, this); m_resCenterFreq->setHintText( tr( "Resonance center frequency:" ), "" ); - m_resCenterFreq->setLabel( tr( "RES CF" ) ); - m_resBandwidth = new Knob( KnobType::Bright26, this ); + m_resBandwidth = new Knob(KnobType::Bright26, tr("RES BW"), SMALL_FONT_SIZE, this); m_resBandwidth->setHintText( tr( "Resonance bandwidth:" ), "" ); - m_resBandwidth->setLabel( tr( "RES BW" ) ); m_forwardMidiCC = new LedCheckBox( tr( "Forward MIDI control changes" ), this ); diff --git a/src/gui/Controls.cpp b/src/gui/Controls.cpp index 209b0fce1..d75da6a7d 100644 --- a/src/gui/Controls.cpp +++ b/src/gui/Controls.cpp @@ -38,7 +38,11 @@ namespace lmms::gui { -void KnobControl::setText(const QString &text) { m_knob->setLabel(text); } +void KnobControl::setText(const QString& text) +{ + // For KnobControls the text is set in the constructor + // so we do nothing here +} QWidget *KnobControl::topWidget() { return m_knob; } @@ -51,8 +55,8 @@ FloatModel *KnobControl::model() { return m_knob->model(); } AutomatableModelView* KnobControl::modelView() { return m_knob; } -KnobControl::KnobControl(QWidget *parent) : - m_knob(new Knob(parent)) {} +KnobControl::KnobControl(const QString& text, QWidget *parent) : + m_knob(new Knob(KnobType::Bright26, text, parent)) {} void ComboControl::setText(const QString &text) { m_label->setText(text); } diff --git a/src/gui/EffectView.cpp b/src/gui/EffectView.cpp index dee9eb136..a77fc1d96 100644 --- a/src/gui/EffectView.cpp +++ b/src/gui/EffectView.cpp @@ -63,23 +63,19 @@ EffectView::EffectView( Effect * _model, QWidget * _parent ) : m_bypass->setToolTip(tr("On/Off")); - - m_wetDry = new Knob( KnobType::Bright26, this ); - m_wetDry->setLabel( tr( "W/D" ) ); + m_wetDry = new Knob(KnobType::Bright26, tr("W/D"), this, Knob::LabelRendering::LegacyFixedFontSize); m_wetDry->move( 40 - m_wetDry->width() / 2, 5 ); m_wetDry->setEnabled( isEnabled ); m_wetDry->setHintText( tr( "Wet Level:" ), "" ); - m_autoQuit = new TempoSyncKnob( KnobType::Bright26, this ); - m_autoQuit->setLabel( tr( "DECAY" ) ); + m_autoQuit = new TempoSyncKnob(KnobType::Bright26, tr("DECAY"), this, Knob::LabelRendering::LegacyFixedFontSize); m_autoQuit->move( 78 - m_autoQuit->width() / 2, 5 ); m_autoQuit->setEnabled( isEnabled && !effect()->m_autoQuitDisabled ); m_autoQuit->setHintText( tr( "Time:" ), "ms" ); - m_gate = new Knob( KnobType::Bright26, this ); - m_gate->setLabel( tr( "GATE" ) ); + m_gate = new Knob(KnobType::Bright26, tr("GATE"), this, Knob::LabelRendering::LegacyFixedFontSize); m_gate->move( 116 - m_gate->width() / 2, 5 ); m_gate->setEnabled( isEnabled && !effect()->m_autoQuitDisabled ); m_gate->setHintText( tr( "Gate:" ), "" ); diff --git a/src/gui/LadspaControlView.cpp b/src/gui/LadspaControlView.cpp index dbc3b8059..294500b75 100644 --- a/src/gui/LadspaControlView.cpp +++ b/src/gui/LadspaControlView.cpp @@ -81,11 +81,11 @@ LadspaControlView::LadspaControlView( QWidget * _parent, case BufferDataType::Integer: case BufferDataType::Enum: case BufferDataType::Floating: - knb = new Knob( KnobType::Bright26, this, m_ctl->port()->name ); + knb = new Knob(KnobType::Bright26, m_ctl->port()->name, this, Knob::LabelRendering::WidgetFont, m_ctl->port()->name); break; case BufferDataType::Time: - knb = new TempoSyncKnob( KnobType::Bright26, this, m_ctl->port()->name ); + knb = new TempoSyncKnob(KnobType::Bright26, m_ctl->port()->name, this, Knob::LabelRendering::WidgetFont, m_ctl->port()->name); break; default: @@ -102,7 +102,6 @@ LadspaControlView::LadspaControlView( QWidget * _parent, { knb->setModel( m_ctl->tempoSyncKnobModel() ); } - knb->setLabel( m_ctl->port()->name ); knb->setHintText( tr( "Value:" ), "" ); layout->addWidget( knb ); if( link != nullptr ) diff --git a/src/gui/LfoControllerDialog.cpp b/src/gui/LfoControllerDialog.cpp index 559ac1336..4a0ff158f 100644 --- a/src/gui/LfoControllerDialog.cpp +++ b/src/gui/LfoControllerDialog.cpp @@ -27,6 +27,7 @@ #include "embed.h" +#include "FontHelper.h" #include "LfoController.h" #include "Knob.h" #include "TempoSyncKnob.h" @@ -62,23 +63,19 @@ LfoControllerDialog::LfoControllerDialog( Controller * _model, QWidget * _parent setWindowIcon( embed::getIconPixmap( "controller" ) ); setFixedSize( 240, 58 ); - m_baseKnob = new Knob( KnobType::Bright26, this ); - m_baseKnob->setLabel( tr( "BASE" ) ); + m_baseKnob = new Knob(KnobType::Bright26, tr("BASE"), SMALL_FONT_SIZE, this); m_baseKnob->move( CD_LFO_BASE_CD_KNOB_X, CD_LFO_CD_KNOB_Y ); m_baseKnob->setHintText( tr( "Base:" ), "" ); - m_speedKnob = new TempoSyncKnob( KnobType::Bright26, this ); - m_speedKnob->setLabel( tr( "FREQ" ) ); + m_speedKnob = new TempoSyncKnob(KnobType::Bright26, tr("FREQ"), SMALL_FONT_SIZE, this); m_speedKnob->move( CD_LFO_SPEED_CD_KNOB_X, CD_LFO_CD_KNOB_Y ); m_speedKnob->setHintText( tr( "LFO frequency:" ), "" ); - m_amountKnob = new Knob( KnobType::Bright26, this ); - m_amountKnob->setLabel( tr( "AMNT" ) ); + m_amountKnob = new Knob(KnobType::Bright26, tr("AMNT"), SMALL_FONT_SIZE, this); m_amountKnob->move( CD_LFO_AMOUNT_CD_KNOB_X, CD_LFO_CD_KNOB_Y ); m_amountKnob->setHintText( tr( "Modulation amount:" ), "" ); - m_phaseKnob = new Knob( KnobType::Bright26, this ); - m_phaseKnob->setLabel( tr( "PHS" ) ); + m_phaseKnob = new Knob(KnobType::Bright26, tr("PHS"), SMALL_FONT_SIZE, this); m_phaseKnob->move( CD_LFO_PHASE_CD_KNOB_X, CD_LFO_CD_KNOB_Y ); m_phaseKnob->setHintText( tr( "Phase offset:" ) , "" + tr( " degrees" ) ); diff --git a/src/gui/Lv2ViewBase.cpp b/src/gui/Lv2ViewBase.cpp index d6a25af83..0cd6a0ee4 100644 --- a/src/gui/Lv2ViewBase.cpp +++ b/src/gui/Lv2ViewBase.cpp @@ -70,7 +70,7 @@ Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* proc, int colNum) : switch (port.m_vis) { case PortVis::Generic: - m_control = new KnobControl(m_parent); + m_control = new KnobControl(port.name(), m_parent); break; case PortVis::Integer: { diff --git a/src/gui/MidiCCRackView.cpp b/src/gui/MidiCCRackView.cpp index a0b1496fb..efeb46d46 100644 --- a/src/gui/MidiCCRackView.cpp +++ b/src/gui/MidiCCRackView.cpp @@ -79,6 +79,7 @@ MidiCCRackView::MidiCCRackView(InstrumentTrack * track) : auto knobsScrollArea = new QScrollArea(); auto knobsArea = new QWidget(); auto knobsAreaLayout = new QGridLayout(); + knobsAreaLayout->setVerticalSpacing(10); knobsArea->setLayout(knobsAreaLayout); knobsScrollArea->setWidget(knobsArea); @@ -86,24 +87,21 @@ MidiCCRackView::MidiCCRackView(InstrumentTrack * track) : knobsGroupBoxLayout->addWidget(knobsScrollArea); - // Adds the controller knobs + // Adds the controller knobs and sets their models for (int i = 0; i < MidiControllerCount; ++i) { - m_controllerKnob[i] = new Knob(KnobType::Bright26); - m_controllerKnob[i]->setLabel(tr("CC %1").arg(i)); - knobsAreaLayout->addWidget(m_controllerKnob[i], i/4, i%4); + auto knob = new Knob(KnobType::Bright26, tr("CC %1").arg(i), this); + knob->setModel(m_track->m_midiCCModel[i].get()); + knobsAreaLayout->addWidget(knob, i/4, i%4, Qt::AlignHCenter); + + // TODO It seems that this is not really used/needed? + m_controllerKnob[i] = knob; } // Set all the models // Set the LED button to enable/disable the track midi cc m_midiCCGroupBox->setModel(m_track->m_midiCCEnable.get()); - // Set the model for each Knob - for (int i = 0; i < MidiControllerCount; ++i) - { - m_controllerKnob[i]->setModel(m_track->m_midiCCModel[i].get()); - } - // Connection to update the name of the track on the label connect(m_track, SIGNAL(nameChanged()), this, SLOT(renameWindow())); diff --git a/src/gui/instrument/EnvelopeAndLfoView.cpp b/src/gui/instrument/EnvelopeAndLfoView.cpp index 90e2aa30e..02dd014f4 100644 --- a/src/gui/instrument/EnvelopeAndLfoView.cpp +++ b/src/gui/instrument/EnvelopeAndLfoView.cpp @@ -57,8 +57,7 @@ EnvelopeAndLfoView::EnvelopeAndLfoView(QWidget * parent) : // Helper lambdas for consistent repeated buiding of certain widgets auto buildKnob = [&](const QString& label, const QString& hintText) { - auto knob = new Knob(KnobType::Bright26, this); - knob->setLabel(label); + auto knob = new Knob(KnobType::Bright26, label, this, Knob::LabelRendering::LegacyFixedFontSize); knob->setHintText(hintText, ""); return knob; @@ -170,8 +169,7 @@ EnvelopeAndLfoView::EnvelopeAndLfoView(QWidget * parent) : m_lfoAttackKnob = buildKnob(tr("ATT"), tr("Attack:")); lfoKnobsLayout->addWidget(m_lfoAttackKnob); - m_lfoSpeedKnob = new TempoSyncKnob(KnobType::Bright26, this); - m_lfoSpeedKnob->setLabel(tr("SPD")); + m_lfoSpeedKnob = new TempoSyncKnob(KnobType::Bright26, tr("SPD"), this, Knob::LabelRendering::LegacyFixedFontSize); m_lfoSpeedKnob->setHintText(tr("Frequency:"), ""); lfoKnobsLayout->addWidget(m_lfoSpeedKnob); diff --git a/src/gui/instrument/InstrumentFunctionViews.cpp b/src/gui/instrument/InstrumentFunctionViews.cpp index a60fa64f9..7c0a62490 100644 --- a/src/gui/instrument/InstrumentFunctionViews.cpp +++ b/src/gui/instrument/InstrumentFunctionViews.cpp @@ -44,7 +44,7 @@ InstrumentFunctionNoteStackingView::InstrumentFunctionNoteStackingView( Instrume m_cc( cc ), m_chordsGroupBox( new GroupBox( tr( "STACKING" ) ) ), m_chordsComboBox( new ComboBox() ), - m_chordRangeKnob( new Knob( KnobType::Bright26 ) ) + m_chordRangeKnob(new Knob(KnobType::Bright26, tr("RANGE"), this, Knob::LabelRendering::LegacyFixedFontSize)) { auto topLayout = new QHBoxLayout(this); topLayout->setContentsMargins(0, 0, 0, 0); @@ -59,7 +59,6 @@ InstrumentFunctionNoteStackingView::InstrumentFunctionNoteStackingView( Instrume auto chordLabel = new QLabel(tr("Chord:")); chordLabel->setFont(adjustedToPixelSize(chordLabel->font(), DEFAULT_FONT_SIZE)); - m_chordRangeKnob->setLabel( tr( "RANGE" ) ); m_chordRangeKnob->setHintText( tr( "Chord range:" ), " " + tr( "octave(s)" ) ); mainLayout->addWidget( chordLabel, 0, 0 ); @@ -98,13 +97,13 @@ InstrumentFunctionArpeggioView::InstrumentFunctionArpeggioView( InstrumentFuncti m_a( arp ), m_arpGroupBox( new GroupBox( tr( "ARPEGGIO" ) ) ), m_arpComboBox( new ComboBox() ), - m_arpRangeKnob( new Knob( KnobType::Bright26 ) ), - m_arpRepeatsKnob( new Knob( KnobType::Bright26 ) ), - m_arpCycleKnob( new Knob( KnobType::Bright26 ) ), - m_arpSkipKnob( new Knob( KnobType::Bright26 ) ), - m_arpMissKnob( new Knob( KnobType::Bright26 ) ), - m_arpTimeKnob( new TempoSyncKnob( KnobType::Bright26 ) ), - m_arpGateKnob( new Knob( KnobType::Bright26 ) ), + m_arpRangeKnob(new Knob(KnobType::Bright26, tr("RANGE"), this, Knob::LabelRendering::LegacyFixedFontSize)), + m_arpRepeatsKnob(new Knob(KnobType::Bright26, tr("REP"), this, Knob::LabelRendering::LegacyFixedFontSize)), + m_arpCycleKnob(new Knob(KnobType::Bright26, tr("CYCLE"), this, Knob::LabelRendering::LegacyFixedFontSize)), + m_arpSkipKnob(new Knob(KnobType::Bright26, tr("SKIP"), this, Knob::LabelRendering::LegacyFixedFontSize)), + m_arpMissKnob(new Knob(KnobType::Bright26, tr("MISS"), this, Knob::LabelRendering::LegacyFixedFontSize)), + m_arpTimeKnob(new TempoSyncKnob(KnobType::Bright26, tr("TIME"), this, Knob::LabelRendering::LegacyFixedFontSize)), + m_arpGateKnob(new Knob(KnobType::Bright26, tr("GATE"), this, Knob::LabelRendering::LegacyFixedFontSize)), m_arpDirectionComboBox( new ComboBox() ), m_arpModeComboBox( new ComboBox() ) { @@ -118,31 +117,12 @@ InstrumentFunctionArpeggioView::InstrumentFunctionArpeggioView( InstrumentFuncti mainLayout->setHorizontalSpacing( 20 ); mainLayout->setVerticalSpacing( 1 ); - m_arpRangeKnob->setLabel( tr( "RANGE" ) ); m_arpRangeKnob->setHintText( tr( "Arpeggio range:" ), " " + tr( "octave(s)" ) ); - - - m_arpRepeatsKnob->setLabel( tr( "REP" ) ); m_arpRepeatsKnob->setHintText( tr( "Note repeats:" ) + " ", " " + tr( "time(s)" ) ); - - - m_arpCycleKnob->setLabel( tr( "CYCLE" ) ); m_arpCycleKnob->setHintText( tr( "Cycle notes:" ) + " ", " " + tr( "note(s)" ) ); - - - m_arpSkipKnob->setLabel( tr( "SKIP" ) ); m_arpSkipKnob->setHintText( tr( "Skip rate:" ), tr( "%" ) ); - - - m_arpMissKnob->setLabel( tr( "MISS" ) ); m_arpMissKnob->setHintText( tr( "Miss rate:" ), tr( "%" ) ); - - - m_arpTimeKnob->setLabel( tr( "TIME" ) ); m_arpTimeKnob->setHintText( tr( "Arpeggio time:" ), " " + tr( "ms" ) ); - - - m_arpGateKnob->setLabel( tr( "GATE" ) ); m_arpGateKnob->setHintText( tr( "Arpeggio gate:" ), tr( "%" ) ); auto arpChordLabel = new QLabel(tr("Chord:")); diff --git a/src/gui/instrument/InstrumentSoundShapingView.cpp b/src/gui/instrument/InstrumentSoundShapingView.cpp index e0d6a6e98..7f786867d 100644 --- a/src/gui/instrument/InstrumentSoundShapingView.cpp +++ b/src/gui/instrument/InstrumentSoundShapingView.cpp @@ -68,13 +68,11 @@ InstrumentSoundShapingView::InstrumentSoundShapingView(QWidget* parent) : m_filterComboBox = new ComboBox(m_filterGroupBox); filterLayout->addWidget(m_filterComboBox); - m_filterCutKnob = new Knob(KnobType::Bright26, m_filterGroupBox); - m_filterCutKnob->setLabel(tr("FREQ")); + m_filterCutKnob = new Knob(KnobType::Bright26, tr("FREQ"), m_filterGroupBox, Knob::LabelRendering::LegacyFixedFontSize); m_filterCutKnob->setHintText(tr("Cutoff frequency:"), " " + tr("Hz")); filterLayout->addWidget(m_filterCutKnob); - m_filterResKnob = new Knob(KnobType::Bright26, m_filterGroupBox); - m_filterResKnob->setLabel(tr("Q/RESO")); + m_filterResKnob = new Knob(KnobType::Bright26, tr("Q/RESO"), m_filterGroupBox, Knob::LabelRendering::LegacyFixedFontSize); m_filterResKnob->setHintText(tr("Q/Resonance:"), ""); filterLayout->addWidget(m_filterResKnob); diff --git a/src/gui/tracks/InstrumentTrackView.cpp b/src/gui/tracks/InstrumentTrackView.cpp index b8ca43bcd..b6681beeb 100644 --- a/src/gui/tracks/InstrumentTrackView.cpp +++ b/src/gui/tracks/InstrumentTrackView.cpp @@ -35,6 +35,7 @@ #include "ConfigManager.h" #include "Engine.h" #include "FadeButton.h" +#include "FontHelper.h" #include "Knob.h" #include "MidiCCRackView.h" #include "Mixer.h" @@ -77,19 +78,15 @@ InstrumentTrackView::InstrumentTrackView( InstrumentTrack * _it, TrackContainerV m_mixerChannelNumber = new MixerChannelLcdSpinBox(2, getTrackSettingsWidget(), tr("Mixer channel"), this); m_mixerChannelNumber->show(); - m_volumeKnob = new Knob( KnobType::Small17, getTrackSettingsWidget(), - tr( "Volume" ) ); + m_volumeKnob = new Knob(KnobType::Small17, tr("VOL"), getTrackSettingsWidget(), Knob::LabelRendering::LegacyFixedFontSize, tr("VOL")); m_volumeKnob->setVolumeKnob( true ); m_volumeKnob->setModel( &_it->m_volumeModel ); m_volumeKnob->setHintText( tr( "Volume:" ), "%" ); - m_volumeKnob->setLabel( tr( "VOL" ) ); m_volumeKnob->show(); - m_panningKnob = new Knob( KnobType::Small17, getTrackSettingsWidget(), - tr( "Panning" ) ); + m_panningKnob = new Knob(KnobType::Small17, tr("PAN"), getTrackSettingsWidget(), Knob::LabelRendering::LegacyFixedFontSize, tr("Panning")); m_panningKnob->setModel( &_it->m_panningModel ); m_panningKnob->setHintText(tr("Panning:"), "%"); - m_panningKnob->setLabel( tr( "PAN" ) ); m_panningKnob->show(); m_midiMenu = new QMenu( tr( "MIDI" ), this ); diff --git a/src/gui/tracks/SampleTrackView.cpp b/src/gui/tracks/SampleTrackView.cpp index 2c7fde8c6..34495e735 100644 --- a/src/gui/tracks/SampleTrackView.cpp +++ b/src/gui/tracks/SampleTrackView.cpp @@ -31,6 +31,7 @@ #include "embed.h" #include "Engine.h" #include "FadeButton.h" +#include "FontHelper.h" #include "Mixer.h" #include "MixerView.h" #include "GuiApplication.h" @@ -62,20 +63,15 @@ SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) : m_mixerChannelNumber = new MixerChannelLcdSpinBox(2, getTrackSettingsWidget(), tr("Mixer channel"), this); m_mixerChannelNumber->show(); - m_volumeKnob = new Knob( KnobType::Small17, getTrackSettingsWidget(), - tr( "Track volume" ) ); + m_volumeKnob = new Knob(KnobType::Small17, tr("VOL"), getTrackSettingsWidget(), Knob::LabelRendering::LegacyFixedFontSize, tr("Track volume")); m_volumeKnob->setVolumeKnob( true ); m_volumeKnob->setModel( &_t->m_volumeModel ); m_volumeKnob->setHintText( tr( "Channel volume:" ), "%" ); - - m_volumeKnob->setLabel( tr( "VOL" ) ); m_volumeKnob->show(); - m_panningKnob = new Knob( KnobType::Small17, getTrackSettingsWidget(), - tr( "Panning" ) ); + m_panningKnob = new Knob(KnobType::Small17, tr("PAN"), getTrackSettingsWidget(), Knob::LabelRendering::LegacyFixedFontSize, tr("Panning")); m_panningKnob->setModel( &_t->m_panningModel ); m_panningKnob->setHintText( tr( "Panning:" ), "%" ); - m_panningKnob->setLabel( tr( "PAN" ) ); m_panningKnob->show(); m_activityIndicator = new FadeButton( diff --git a/src/gui/widgets/CustomTextKnob.cpp b/src/gui/widgets/CustomTextKnob.cpp index a4edde47c..062359157 100644 --- a/src/gui/widgets/CustomTextKnob.cpp +++ b/src/gui/widgets/CustomTextKnob.cpp @@ -23,18 +23,19 @@ */ #include "CustomTextKnob.h" +#include "FontHelper.h" namespace lmms::gui { -CustomTextKnob::CustomTextKnob( KnobType _knob_num, QWidget * _parent, const QString & _name, const QString & _value_text ) : +CustomTextKnob::CustomTextKnob( KnobType _knob_num, const QString& label, QWidget * _parent, const QString & _name, const QString & _value_text ) : Knob( _knob_num, _parent, _name ), - m_value_text( _value_text ) {} - -CustomTextKnob::CustomTextKnob( QWidget * _parent, const QString & _name, const QString & _value_text ) : //!< default ctor - Knob( _parent, _name ), - m_value_text( _value_text ) {} + m_value_text( _value_text ) +{ + setFont(adjustedToPixelSize(font(), SMALL_FONT_SIZE)); + setLabel(label); +} QString CustomTextKnob::displayValue() const { diff --git a/src/gui/widgets/Knob.cpp b/src/gui/widgets/Knob.cpp index 25d2e3e3f..624ea287d 100644 --- a/src/gui/widgets/Knob.cpp +++ b/src/gui/widgets/Knob.cpp @@ -32,6 +32,8 @@ #include "embed.h" #include "FontHelper.h" +#include + namespace lmms::gui { @@ -49,6 +51,24 @@ Knob::Knob( KnobType _knob_num, QWidget * _parent, const QString & _name ) : initUi( _name ); } +Knob::Knob(KnobType knobNum, const QString& labelText, QWidget* parent, LabelRendering labelRendering, const QString& name) : + Knob(knobNum, parent, name) +{ + setLabel(labelText); + + if (labelRendering == LabelRendering::LegacyFixedFontSize) + { + setFixedFontSizeLabelRendering(); + } +} + +Knob::Knob(KnobType knobNum, const QString& labelText, int labelPixelSize, QWidget* parent, const QString& name) : + Knob(knobNum, parent, name) +{ + setFont(adjustedToPixelSize(font(), labelPixelSize)); + setLabel(labelText); +} + Knob::Knob( QWidget * _parent, const QString & _name ) : Knob( KnobType::Bright26, _parent, _name ) { @@ -56,7 +76,6 @@ Knob::Knob( QWidget * _parent, const QString & _name ) : - void Knob::initUi( const QString & _name ) { onKnobNumUpdated(); @@ -129,16 +148,12 @@ void Knob::onKnobNumUpdated() -void Knob::setLabel( const QString & txt ) +void Knob::setLabel(const QString& txt) { m_label = txt; m_isHtmlLabel = false; - if( m_knobPixmap ) - { - setFixedSize(qMax( m_knobPixmap->width(), - horizontalAdvance(QFontMetrics(adjustedToPixelSize(font(), SMALL_FONT_SIZE)), m_label)), - m_knobPixmap->height() + 10); - } + + updateFixedSize(); update(); } @@ -165,8 +180,49 @@ void Knob::setHtmlLabel(const QString &htmltxt) update(); } +void Knob::setFixedFontSizeLabelRendering() +{ + m_fixedFontSizeLabelRendering = true; + updateFixedSize(); + update(); +} + +void Knob::updateFixedSize() +{ + if (fixedFontSizeLabelRendering()) + { + if (m_knobPixmap) + { + // In legacy mode only the width of the label is taken into account while the height is not + const int labelWidth = horizontalAdvance(QFontMetrics(adjustedToPixelSize(font(), SMALL_FONT_SIZE)), m_label); + const int width = std::max(m_knobPixmap->width(), labelWidth); + + // Legacy mode assumes that the label will fit into 10 pixels plus some of the pixmap area + setFixedSize(width, m_knobPixmap->height() + 10); + } + } + else + { + // Styled knobs do not use pixmaps and have no labels. Their size is set from the outside and + // they are painted within these limits. Hence we should not compute a new size from a pixmap + // and/or label the case of styled knobs. + if (knobNum() == KnobType::Styled) + { + return; + } + + QSize pixmapSize = m_knobPixmap ? m_knobPixmap->size() : QSize(0, 0); + + auto fm = QFontMetrics(font()); + + const int width = std::max(pixmapSize.width(), horizontalAdvance(fm, m_label)); + const int height = pixmapSize.height() + fm.height(); + + setFixedSize(width, height); + } +} void Knob::setTotalAngle( float angle ) { @@ -447,31 +503,42 @@ void Knob::drawKnob( QPainter * _p ) _p->drawImage( 0, 0, m_cache ); } -void Knob::paintEvent( QPaintEvent * _me ) +void Knob::drawLabel(QPainter& p) { - QPainter p( this ); - - drawKnob( &p ); if( !m_label.isEmpty() ) { if (!m_isHtmlLabel) { - p.setFont(adjustedToPixelSize(p.font(), SMALL_FONT_SIZE)); + if (fixedFontSizeLabelRendering()) + { + p.setFont(adjustedToPixelSize(p.font(), SMALL_FONT_SIZE)); + } + auto fm = p.fontMetrics(); + const auto x = (width() - horizontalAdvance(fm, m_label)) / 2; + const auto descent = fixedFontSizeLabelRendering() ? 2 : fm.descent(); + const auto y = height() - descent; + p.setPen(textColor()); - p.drawText(width() / 2 - - horizontalAdvance(p.fontMetrics(), m_label) / 2, - height() - 2, m_label); + p.drawText(x, y, m_label); } else { // TODO setHtmlLabel is never called so this will never be executed. Remove functionality? - m_tdRenderer->setDefaultFont(adjustedToPixelSize(p.font(), SMALL_FONT_SIZE)); + m_tdRenderer->setDefaultFont(font()); p.translate((width() - m_tdRenderer->idealWidth()) / 2, (height() - m_tdRenderer->pageSize().height()) / 2); m_tdRenderer->drawContents(&p); } } } +void Knob::paintEvent(QPaintEvent*) +{ + QPainter p(this); + + drawKnob(&p); + drawLabel(p); +} + void Knob::changeEvent(QEvent * ev) { if (ev->type() == QEvent::EnabledChange) @@ -484,6 +551,14 @@ void Knob::changeEvent(QEvent * ev) m_cache = QImage(); update(); } + else if (ev->type() == QEvent::FontChange) + { + // The size of the label might have changed so update + // the size of this widget. + updateFixedSize(); + + update(); + } } diff --git a/src/gui/widgets/TempoSyncKnob.cpp b/src/gui/widgets/TempoSyncKnob.cpp index 473cee28c..eedefd4ad 100644 --- a/src/gui/widgets/TempoSyncKnob.cpp +++ b/src/gui/widgets/TempoSyncKnob.cpp @@ -30,6 +30,7 @@ #include "Engine.h" #include "CaptionMenu.h" #include "embed.h" +#include "FontHelper.h" #include "GuiApplication.h" #include "MainWindow.h" #include "MeterDialog.h" @@ -51,6 +52,24 @@ TempoSyncKnob::TempoSyncKnob( KnobType _knob_num, QWidget * _parent, { } +TempoSyncKnob::TempoSyncKnob(KnobType knobNum, const QString& labelText, QWidget* parent, LabelRendering labelRendering, const QString& name) : + TempoSyncKnob(knobNum, parent, name) +{ + setLabel(labelText); + + if (labelRendering == Knob::LabelRendering::LegacyFixedFontSize) + { + setFixedFontSizeLabelRendering(); + } +} + + +TempoSyncKnob::TempoSyncKnob(KnobType knobNum, const QString& labelText, int labelPixelSize, QWidget* parent, const QString& name) : + TempoSyncKnob(knobNum, parent, name) +{ + setFont(adjustedToPixelSize(font(), labelPixelSize)); + setLabel(labelText); +} @@ -63,8 +82,6 @@ TempoSyncKnob::~TempoSyncKnob() } - - void TempoSyncKnob::modelChanged() { if( model() == nullptr ) From 352ef8c25e8e26b260eafb1930ce148faa44df21 Mon Sep 17 00:00:00 2001 From: mmeeaallyynn <88937076+mmeeaallyynn@users.noreply.github.com> Date: Sat, 31 May 2025 12:49:14 +0200 Subject: [PATCH 46/62] Sample Track Recording with Jack backend (#7567) Enable the `AudioJack` to take recorded input and push it to the `AudioEngine`. This adds functionality for `AudioJack` which already exists for `AudioSdl` and `AudioPortAudio`. Note that sample track recording in the LMMS core is not completed before #7786 . This PR also removes the reading and saving of the channel number configuration in several places as it will default to `DEFAULT_CHANNELS` all the time anyway. Co-authored-by: Johannes Lorenz Co-authored-by: Michael Gregorius --- include/AudioJack.h | 12 +++---- src/core/audio/AudioJack.cpp | 65 ++++++++++++++++++++---------------- 2 files changed, 41 insertions(+), 36 deletions(-) diff --git a/include/AudioJack.h b/include/AudioJack.h index e13b4a5ef..ab87e97f6 100644 --- a/include/AudioJack.h +++ b/include/AudioJack.h @@ -53,11 +53,6 @@ namespace lmms class MidiJack; -namespace gui -{ -class LcdSpinBox; -} - class AudioJack : public QObject, public AudioDevice { @@ -82,13 +77,10 @@ public: { public: setupWidget(QWidget* parent); - ~setupWidget() override; - void saveSettings() override; private: QLineEdit* m_clientName; - gui::LcdSpinBox* m_channels; }; private slots: @@ -96,6 +88,7 @@ private slots: private: bool initJackClient(); + void resizeInputBuffer(jack_nframes_t nframes); void startProcessing() override; void stopProcessing() override; @@ -116,8 +109,11 @@ private: std::atomic m_midiClient; std::vector m_outputPorts; + std::vector m_inputPorts; jack_default_audio_sample_t** m_tempOutBufs; + std::vector m_inputFrameBuffer; SampleFrame* m_outBuf; + SampleFrame* m_inBuf; f_cnt_t m_framesDoneInCurBuf; f_cnt_t m_framesToDoInCurBuf; diff --git a/src/core/audio/AudioJack.cpp b/src/core/audio/AudioJack.cpp index b6da7c845..429c4ddcb 100644 --- a/src/core/audio/AudioJack.cpp +++ b/src/core/audio/AudioJack.cpp @@ -34,7 +34,6 @@ #include "ConfigManager.h" #include "Engine.h" #include "GuiApplication.h" -#include "LcdSpinBox.h" #include "MainWindow.h" #include "MidiJack.h" @@ -44,19 +43,14 @@ namespace lmms AudioJack::AudioJack(bool& successful, AudioEngine* audioEngineParam) : AudioDevice( - // clang-format off - std::clamp( - ConfigManager::inst()->value("audiojack", "channels").toInt(), - DEFAULT_CHANNELS, - DEFAULT_CHANNELS - ), - // clang-format on + DEFAULT_CHANNELS, audioEngineParam) , m_client(nullptr) , m_active(false) , m_midiClient(nullptr) , m_tempOutBufs(new jack_default_audio_sample_t*[channels()]) , m_outBuf(new SampleFrame[audioEngine()->framesPerPeriod()]) + , m_inBuf(new SampleFrame[audioEngine()->framesPerPeriod()]) , m_framesDoneInCurBuf(0) , m_framesToDoInCurBuf(0) { @@ -66,6 +60,8 @@ AudioJack::AudioJack(bool& successful, AudioEngine* audioEngineParam) if (successful) { connect(this, SIGNAL(zombified()), this, SLOT(restartAfterZombified()), Qt::QueuedConnection); } + + m_supportsCapture = true; } @@ -90,6 +86,7 @@ AudioJack::~AudioJack() delete[] m_tempOutBufs; delete[] m_outBuf; + delete[] m_inBuf; } @@ -154,6 +151,16 @@ bool AudioJack::initJackClient() clientName.toLatin1().constData(), jack_get_client_name(m_client)); } + resizeInputBuffer(jack_get_buffer_size(m_client)); + + // set buffer-size callback + jack_set_buffer_size_callback(m_client, + [](jack_nframes_t nframes, void* udata) -> int { + static_cast(udata)->resizeInputBuffer(nframes); + return 0; + }, + this); + // set process-callback jack_set_process_callback(m_client, staticProcessCallback, this); @@ -167,6 +174,10 @@ bool AudioJack::initJackClient() QString name = QString("master out ") + ((ch % 2) ? "R" : "L") + QString::number(ch / 2 + 1); m_outputPorts.push_back( jack_port_register(m_client, name.toLatin1().constData(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0)); + + QString input_name = QString("master in ") + ((ch % 2) ? "R" : "L") + QString::number(ch / 2 + 1); + m_inputPorts.push_back(jack_port_register(m_client, input_name.toLatin1().constData(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0)); + if (m_outputPorts.back() == nullptr) { printf("no more JACK-ports available!\n"); @@ -180,6 +191,14 @@ bool AudioJack::initJackClient() +void AudioJack::resizeInputBuffer(jack_nframes_t nframes) +{ + m_inputFrameBuffer.resize(nframes); +} + + + + void AudioJack::startProcessing() { if (m_active || m_client == nullptr) @@ -290,7 +309,6 @@ void AudioJack::renamePort(AudioBusHandle* port) int AudioJack::processCallback(jack_nframes_t nframes) { - // do midi processing first so that midi input can // add to the following sound processing if (m_midiClient && nframes > 0) @@ -356,6 +374,16 @@ int AudioJack::processCallback(jack_nframes_t nframes) } } + for (int c = 0; c < channels(); ++c) + { + jack_default_audio_sample_t* jack_input_buffer = (jack_default_audio_sample_t*) jack_port_get_buffer(m_inputPorts[c], nframes); + + for (jack_nframes_t frame = 0; frame < nframes; frame++) + { + m_inputFrameBuffer[frame][c] = static_cast(jack_input_buffer[frame]); + } + } + audioEngine()->pushInputFrames (m_inputFrameBuffer.data(), nframes); return 0; } @@ -390,24 +418,6 @@ AudioJack::setupWidget::setupWidget(QWidget* parent) m_clientName = new QLineEdit(cn, this); form->addRow(tr("Client name"), m_clientName); - - auto m = new gui::LcdSpinBoxModel(/* this */); - m->setRange(DEFAULT_CHANNELS, DEFAULT_CHANNELS); - m->setStep(2); - m->setValue(ConfigManager::inst()->value("audiojack", "channels").toInt()); - - m_channels = new gui::LcdSpinBox(1, this); - m_channels->setModel(m); - - form->addRow(tr("Channels"), m_channels); -} - - - - -AudioJack::setupWidget::~setupWidget() -{ - delete m_channels->model(); } @@ -416,7 +426,6 @@ AudioJack::setupWidget::~setupWidget() void AudioJack::setupWidget::saveSettings() { ConfigManager::inst()->setValue("audiojack", "clientname", m_clientName->text()); - ConfigManager::inst()->setValue("audiojack", "channels", QString::number(m_channels->value())); } From 3f8b86a559a56e58b161f95096b5f0aa3ba36e0e Mon Sep 17 00:00:00 2001 From: Andrew Wiltshire <62200778+AW1534@users.noreply.github.com> Date: Sat, 31 May 2025 18:58:08 +0100 Subject: [PATCH 47/62] Ignore "Keep plugin windows on top" setting when on wayland (#7901) Fix a crash that occurs when running under Wayland and when loading a VST plugin with the option "Keep plugin windows on top when not embedded" enabled. --- include/GuiApplication.h | 1 + plugins/VstBase/VstPlugin.cpp | 7 +++---- src/gui/GuiApplication.cpp | 5 +++++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/include/GuiApplication.h b/include/GuiApplication.h index 16680934f..02fb6ed67 100644 --- a/include/GuiApplication.h +++ b/include/GuiApplication.h @@ -55,6 +55,7 @@ public: static GuiApplication* instance(); static void sigintHandler(int); + static bool isWayland(); #ifdef LMMS_BUILD_WIN32 static QFont getWin32SystemFont(); #endif diff --git a/plugins/VstBase/VstPlugin.cpp b/plugins/VstBase/VstPlugin.cpp index 5dbe7a698..f26245728 100644 --- a/plugins/VstBase/VstPlugin.cpp +++ b/plugins/VstBase/VstPlugin.cpp @@ -49,12 +49,12 @@ #include "AudioEngine.h" #include "ConfigManager.h" +#include "FileDialog.h" #include "GuiApplication.h" #include "LocaleHelper.h" #include "MainWindow.h" #include "PathUtil.h" #include "Song.h" -#include "FileDialog.h" #ifdef LMMS_BUILD_LINUX # include @@ -404,9 +404,8 @@ bool VstPlugin::processMessage( const message & _m ) { case IdVstPluginWindowID: m_pluginWindowID = _m.getInt(); - if( m_embedMethod == "none" - && ConfigManager::inst()->value( - "ui", "vstalwaysontop" ).toInt() ) + if (m_embedMethod == "none" && !gui::GuiApplication::isWayland() + && ConfigManager::inst()->value("ui", "vstalwaysontop").toInt()) { #ifdef LMMS_BUILD_WIN32 // We're changing the owner, not the parent, diff --git a/src/gui/GuiApplication.cpp b/src/gui/GuiApplication.cpp index 7e62a3141..292284b3c 100644 --- a/src/gui/GuiApplication.cpp +++ b/src/gui/GuiApplication.cpp @@ -79,6 +79,11 @@ GuiApplication* GuiApplication::instance() return s_instance; } +bool GuiApplication::isWayland() +{ + return QGuiApplication::platformName().contains("wayland"); +} + GuiApplication::GuiApplication() From 2d2c26d5010fe226840d230538a0a2df3876549f Mon Sep 17 00:00:00 2001 From: Sotonye Atemie Date: Sun, 1 Jun 2025 04:45:43 -0400 Subject: [PATCH 48/62] Remove `m_inBuf` from `AudioJack` (#7922) Extra variable in `AudioJack` that can be removed, as `m_inputFrameBuffer` is currently being used instead. --- include/AudioJack.h | 1 - src/core/audio/AudioJack.cpp | 2 -- 2 files changed, 3 deletions(-) diff --git a/include/AudioJack.h b/include/AudioJack.h index ab87e97f6..84d85a649 100644 --- a/include/AudioJack.h +++ b/include/AudioJack.h @@ -113,7 +113,6 @@ private: jack_default_audio_sample_t** m_tempOutBufs; std::vector m_inputFrameBuffer; SampleFrame* m_outBuf; - SampleFrame* m_inBuf; f_cnt_t m_framesDoneInCurBuf; f_cnt_t m_framesToDoInCurBuf; diff --git a/src/core/audio/AudioJack.cpp b/src/core/audio/AudioJack.cpp index 429c4ddcb..9abbcc347 100644 --- a/src/core/audio/AudioJack.cpp +++ b/src/core/audio/AudioJack.cpp @@ -50,7 +50,6 @@ AudioJack::AudioJack(bool& successful, AudioEngine* audioEngineParam) , m_midiClient(nullptr) , m_tempOutBufs(new jack_default_audio_sample_t*[channels()]) , m_outBuf(new SampleFrame[audioEngine()->framesPerPeriod()]) - , m_inBuf(new SampleFrame[audioEngine()->framesPerPeriod()]) , m_framesDoneInCurBuf(0) , m_framesToDoInCurBuf(0) { @@ -86,7 +85,6 @@ AudioJack::~AudioJack() delete[] m_tempOutBufs; delete[] m_outBuf; - delete[] m_inBuf; } From fd1a4c1b6ec873b609e81f302bc55f233e968488 Mon Sep 17 00:00:00 2001 From: Andrew Wiltshire <62200778+AW1534@users.noreply.github.com> Date: Sun, 1 Jun 2025 14:46:00 +0100 Subject: [PATCH 49/62] Move `FileRevealer` into the `lmms::gui` namespace (#7923) --- include/FileRevealer.h | 4 ++-- src/gui/FileRevealer.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/FileRevealer.h b/include/FileRevealer.h index feb6d1223..99b04916c 100644 --- a/include/FileRevealer.h +++ b/include/FileRevealer.h @@ -27,7 +27,7 @@ #include -namespace lmms { +namespace lmms::gui { /** * @class FileRevealer @@ -73,5 +73,5 @@ protected: static bool supportsArg(const QString& command, const QString& arg); }; -} // namespace lmms +} // namespace lmms::gui #endif // LMMS_FILE_REVEALER_H diff --git a/src/gui/FileRevealer.cpp b/src/gui/FileRevealer.cpp index e93cc7aed..6e2ad56b4 100644 --- a/src/gui/FileRevealer.cpp +++ b/src/gui/FileRevealer.cpp @@ -35,7 +35,7 @@ #include "lmmsconfig.h" -namespace lmms { +namespace lmms::gui { bool FileRevealer::s_canSelect = false; const QString& FileRevealer::getDefaultFileManager() @@ -180,4 +180,4 @@ bool FileRevealer::supportsArg(const QString& command, const QString& arg) return output.contains(arg); } -} // namespace lmms +} // namespace lmms::gui From 2747b402a05276992aa9c2e1a686205b6968ebfd Mon Sep 17 00:00:00 2001 From: Andrew Wiltshire <62200778+AW1534@users.noreply.github.com> Date: Sun, 1 Jun 2025 17:19:34 +0100 Subject: [PATCH 50/62] Rename some include guards (#7924) --------- Co-authored-by: Sotonye Atemie --- include/FileRevealer.h | 7 ++++--- include/FontHelper.h | 6 +++--- include/SimpleTextFloat.h | 6 +++--- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/include/FileRevealer.h b/include/FileRevealer.h index 99b04916c..46c52cd8c 100644 --- a/include/FileRevealer.h +++ b/include/FileRevealer.h @@ -22,8 +22,8 @@ * */ -#ifndef LMMS_FILE_REVEALER_H -#define LMMS_FILE_REVEALER_H +#ifndef LMMS_GUI_FILE_REVEALER_H +#define LMMS_GUI_FILE_REVEALER_H #include @@ -74,4 +74,5 @@ protected: }; } // namespace lmms::gui -#endif // LMMS_FILE_REVEALER_H + +#endif // LMMS_GUI_FILE_REVEALER_H diff --git a/include/FontHelper.h b/include/FontHelper.h index ccef24775..c1c0ee665 100644 --- a/include/FontHelper.h +++ b/include/FontHelper.h @@ -22,8 +22,8 @@ * */ -#ifndef LMMS_FONT_HELPER_H -#define LMMS_FONT_HELPER_H +#ifndef LMMS_GUI_FONT_HELPER_H +#define LMMS_GUI_FONT_HELPER_H #include #include @@ -44,4 +44,4 @@ inline QFont adjustedToPixelSize(QFont font, int size) } // namespace lmms::gui -#endif // LMMS_FONT_HELPER_H +#endif // LMMS_GUI_FONT_HELPER_H diff --git a/include/SimpleTextFloat.h b/include/SimpleTextFloat.h index bde6c84fa..e930d2430 100644 --- a/include/SimpleTextFloat.h +++ b/include/SimpleTextFloat.h @@ -23,8 +23,8 @@ */ -#ifndef SIMPLE_TEXT_FLOAT_H -#define SIMPLE_TEXT_FLOAT_H +#ifndef LMMS_GUI_SIMPLE_TEXT_FLOAT_H +#define LMMS_GUI_SIMPLE_TEXT_FLOAT_H #include @@ -64,4 +64,4 @@ private: } // namespace lmms::gui -#endif +#endif // LMMS_GUI_SIMPLE_TEXT_FLOAT_H From 25e8b640d84a0d6cbe7dcd6153cbf0fcdda42d40 Mon Sep 17 00:00:00 2001 From: regulus79 <117475203+regulus79@users.noreply.github.com> Date: Mon, 2 Jun 2025 00:42:02 -0400 Subject: [PATCH 51/62] Update Sample Length when Loading New Sample via Double-Click (#7898) This PR makes it so that when you double-click on a sample clip and select a new sample, it will automatically update the length of the clip, as long as the clip has auto-resize enabled. --- include/SampleClip.h | 1 - src/core/SampleClip.cpp | 14 ++++++++------ src/gui/clips/SampleClipView.cpp | 7 ++----- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/include/SampleClip.h b/include/SampleClip.h index cbd3ac5d5..ea7853386 100644 --- a/include/SampleClip.h +++ b/include/SampleClip.h @@ -54,7 +54,6 @@ public: SampleClip& operator=( const SampleClip& that ) = delete; void changeLength( const TimePos & _length ) override; - void changeLengthToSampleLength(); const QString& sampleFile() const; bool hasSampleFileLoaded(const QString & filename) const; diff --git a/src/core/SampleClip.cpp b/src/core/SampleClip.cpp index 5c04287c4..11aabc2ff 100644 --- a/src/core/SampleClip.cpp +++ b/src/core/SampleClip.cpp @@ -152,12 +152,6 @@ void SampleClip::changeLength( const TimePos & _length ) Clip::changeLength(std::max(static_cast(_length), 1)); } -void SampleClip::changeLengthToSampleLength() -{ - int length = m_sample.sampleSize() / Engine::framesPerTick(); - changeLength(length); -} - const QString& SampleClip::sampleFile() const @@ -261,6 +255,14 @@ void SampleClip::setIsPlaying(bool isPlaying) void SampleClip::updateLength() { + // If the clip has already been manually resized, don't automatically resize it. + // Unless we are in a pattern, where you can't resize stuff manually + if (getAutoResize() || !getResizable()) + { + changeLength(sampleLength()); + setStartTimeOffset(0); + } + emit sampleChanged(); Engine::getSong()->setModified(); diff --git a/src/gui/clips/SampleClipView.cpp b/src/gui/clips/SampleClipView.cpp index b281e5304..df3726cbf 100644 --- a/src/gui/clips/SampleClipView.cpp +++ b/src/gui/clips/SampleClipView.cpp @@ -192,11 +192,7 @@ void SampleClipView::mouseDoubleClickEvent( QMouseEvent * ) if (selectedAudioFile.isEmpty()) { return; } - if (m_clip->hasSampleFileLoaded(selectedAudioFile)) - { - m_clip->changeLengthToSampleLength(); - } - else + if (!m_clip->hasSampleFileLoaded(selectedAudioFile)) { auto sampleBuffer = SampleLoader::createBufferFromFile(selectedAudioFile); if (sampleBuffer != SampleBuffer::emptyBuffer()) @@ -204,6 +200,7 @@ void SampleClipView::mouseDoubleClickEvent( QMouseEvent * ) m_clip->setSampleBuffer(sampleBuffer); } } + m_clip->updateLength(); } From 2c1d402255cf1d5d08a2ca40a3929a5ba27c3ce5 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Tue, 3 Jun 2025 22:51:30 +0200 Subject: [PATCH 52/62] Flanger: connect "Invert" to model (#7929) Connect the Flanger's "Invert" check box to the model. Before this commit it was a placebo effect. ;) --- plugins/Flanger/FlangerControlsDialog.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/Flanger/FlangerControlsDialog.cpp b/plugins/Flanger/FlangerControlsDialog.cpp index d30c1fe60..e39b122ce 100644 --- a/plugins/Flanger/FlangerControlsDialog.cpp +++ b/plugins/Flanger/FlangerControlsDialog.cpp @@ -85,6 +85,7 @@ FlangerControlsDialog::FlangerControlsDialog( FlangerControls *controls ) : knobLayout->addWidget(whiteNoiseKnob); auto invertCb = new LedCheckBox(tr("Invert"), this); + invertCb->setModel(&controls->m_invertFeedbackModel); mainLayout->addWidget(invertCb, 0, Qt::AlignLeft); } From 8acc4ed3ae36fa1d5b507ab76d0c90eb1176c157 Mon Sep 17 00:00:00 2001 From: regulus79 <117475203+regulus79@users.noreply.github.com> Date: Tue, 3 Jun 2025 23:10:13 -0400 Subject: [PATCH 53/62] Fix Position Line gradient appearing in all editors no matter which one is playing (#7882) --- include/PositionLine.h | 6 +++++- src/gui/editors/PianoRoll.cpp | 2 +- src/gui/editors/PositionLine.cpp | 8 +++----- src/gui/editors/SongEditor.cpp | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/include/PositionLine.h b/include/PositionLine.h index 3e798948f..7a619c450 100644 --- a/include/PositionLine.h +++ b/include/PositionLine.h @@ -28,6 +28,8 @@ #include +#include "Song.h" + namespace lmms::gui { @@ -37,7 +39,7 @@ class PositionLine : public QWidget Q_PROPERTY(bool tailGradient MEMBER m_hasTailGradient) Q_PROPERTY(QColor lineColor MEMBER m_lineColor) public: - PositionLine(QWidget* parent); + PositionLine(QWidget* parent, Song::PlayMode playMode); public slots: void zoomChange(float zoom); @@ -45,6 +47,8 @@ public slots: private: void paintEvent(QPaintEvent* pe) override; + Song::PlayMode m_playMode; + bool m_hasTailGradient; QColor m_lineColor; }; diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 05892da3f..54fcaa449 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -283,7 +283,7 @@ PianoRoll::PianoRoll() : this, SLOT( updatePosition( const lmms::TimePos& ) ) ); // white position line follows timeline marker - m_positionLine = new PositionLine(this); + m_positionLine = new PositionLine(this, Song::PlayMode::MidiClip); //update timeline when in step-recording mode connect( &m_stepRecorderWidget, SIGNAL( positionChanged( const lmms::TimePos& ) ), diff --git a/src/gui/editors/PositionLine.cpp b/src/gui/editors/PositionLine.cpp index dda44e8f7..1b43d6383 100644 --- a/src/gui/editors/PositionLine.cpp +++ b/src/gui/editors/PositionLine.cpp @@ -26,14 +26,13 @@ #include -#include "Song.h" - namespace lmms::gui { -PositionLine::PositionLine(QWidget* parent) : +PositionLine::PositionLine(QWidget* parent, Song::PlayMode playMode) : QWidget(parent), + m_playMode(playMode), m_hasTailGradient(false), m_lineColor(0, 0, 0, 0) { @@ -64,8 +63,7 @@ void PositionLine::paintEvent(QPaintEvent* pe) // If gradient is enabled, we're in focus and we're playing, enable gradient if (m_hasTailGradient && Engine::getSong()->isPlaying() && - (Engine::getSong()->playMode() == Song::PlayMode::Song || - Engine::getSong()->playMode() == Song::PlayMode::MidiClip)) + (Engine::getSong()->playMode() == m_playMode)) { c.setAlpha(60); gradient.setColorAt(w, c); diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index 49f620b68..678256a17 100644 --- a/src/gui/editors/SongEditor.cpp +++ b/src/gui/editors/SongEditor.cpp @@ -113,7 +113,7 @@ SongEditor::SongEditor( Song * song ) : // when tracks realign, adjust height of position line connect(this, &TrackContainerView::tracksRealigned, this, &SongEditor::updatePositionLine); - m_positionLine = new PositionLine(this); + m_positionLine = new PositionLine(this, Song::PlayMode::Song); static_cast( layout() )->insertWidget( 1, m_timeLine ); connect( m_song, SIGNAL(playbackStateChanged()), From ce3439b079047db7a39a73a870fde42f6521c318 Mon Sep 17 00:00:00 2001 From: regulus79 <117475203+regulus79@users.noreply.github.com> Date: Wed, 4 Jun 2025 21:26:41 -0400 Subject: [PATCH 54/62] Fix Piano Roll key events from leaking to other instrument windows (#7915) --- src/gui/editors/PianoRoll.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 54fcaa449..ea73e1993 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -1300,6 +1300,7 @@ void PianoRoll::keyPressEvent(QKeyEvent* ke) // if a chord is set, play all chord notes (simulate click on all): playChordNotes(key_num); ke->accept(); + return; } } @@ -1536,6 +1537,7 @@ void PianoRoll::keyReleaseEvent(QKeyEvent* ke ) // if a chord is set, simulate click release on all chord notes pauseChordNotes(key_num); ke->accept(); + return; } } @@ -1560,6 +1562,9 @@ void PianoRoll::keyReleaseEvent(QKeyEvent* ke ) update(); } break; + default: + ke->ignore(); + break; } update(); From 889a2f8aacee75019b6bfe3d885b2ac5c8d8b48a Mon Sep 17 00:00:00 2001 From: Bimal Poudel Date: Thu, 5 Jun 2025 06:32:38 -0600 Subject: [PATCH 55/62] Fix quick access letter conflict with Export and Export Tracks (#7927) Update shortcut `T` for exporting Tracks, so that `x` is default export. --------- Co-authored-by: Sotonye Atemie Co-authored-by: Andrew Wiltshire <62200778+AW1534@users.noreply.github.com> --- src/gui/MainWindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 90d4196a3..e18bb3cbb 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -312,7 +312,7 @@ void MainWindow::finalize() SLOT(onExportProject()), combine(Qt::CTRL, Qt::Key_E)); project_menu->addAction( embed::getIconPixmap( "project_export" ), - tr( "E&xport Tracks..." ), + tr("Export &Tracks..."), this, SLOT(onExportProjectTracks()), combine(Qt::CTRL, Qt::SHIFT, Qt::Key_E)); From 3a18276136d676b6591d154f4783c9fcda3dc496 Mon Sep 17 00:00:00 2001 From: Sotonye Atemie Date: Fri, 6 Jun 2025 16:55:26 -0400 Subject: [PATCH 56/62] Use complete basename in `PathUtil::cleanName` instead of basename (#7934) Use `completeBaseName()` in `PathUtill::cleanName` instead of the `baseName()`. Needed for handling filenames with periods correctly. --- src/core/PathUtil.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/PathUtil.cpp b/src/core/PathUtil.cpp index 144c8e986..f374c60af 100644 --- a/src/core/PathUtil.cpp +++ b/src/core/PathUtil.cpp @@ -115,9 +115,9 @@ namespace lmms::PathUtil return path.mid( basePrefix(baseLookup(path)).length() ); } - QString cleanName(const QString & path) + QString cleanName(const QString& path) { - return stripPrefix(QFileInfo(path).baseName()); + return stripPrefix(QFileInfo(path).completeBaseName()); } From 0dfd2d5597872542f654c9f2efa1ed1317dbf64b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petar=20Kati=C4=87?= Date: Sun, 8 Jun 2025 04:09:54 +0200 Subject: [PATCH 57/62] Init commit (#7940) Fixes a bug where the "Init" button in LOMM visually behaves as a toggle button instead of a push button. --- plugins/LOMM/LOMMControlDialog.cpp | 33 +++++++++++++++--------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/plugins/LOMM/LOMMControlDialog.cpp b/plugins/LOMM/LOMMControlDialog.cpp index e53987a05..b3cf34506 100644 --- a/plugins/LOMM/LOMMControlDialog.cpp +++ b/plugins/LOMM/LOMMControlDialog.cpp @@ -47,24 +47,24 @@ LOMMControlDialog::LOMMControlDialog(LOMMControls* controls) : createKnob(KnobType::Bright26, this, 363, 220, &controls->m_outVolModel, tr("Output Volume:"), " dB", tr("Output volume")); createKnob(KnobType::Bright26, this, 10, 179, &controls->m_upwardModel, tr("Upward Depth:"), "", tr("Upward compression amount for all bands")); createKnob(KnobType::Bright26, this, 363, 179, &controls->m_downwardModel, tr("Downward Depth:"), "", tr("Downward compression amount for all bands")); - + createLcdFloatSpinBox(5, 2, "11green", tr("High/Mid Crossover"), this, 352, 76, &controls->m_split1Model, tr("High/Mid Crossover")); createLcdFloatSpinBox(5, 2, "11green", tr("Mid/Low Crossover"), this, 352, 156, &controls->m_split2Model, tr("Mid/Low Crossover")); - + createPixmapButton(tr("High/mid band split"), this, 369, 104, &controls->m_split1EnabledModel, "crossover_led_green", "crossover_led_off", tr("High/mid band split")); createPixmapButton(tr("Mid/low band split"), this, 369, 126, &controls->m_split2EnabledModel, "crossover_led_green", "crossover_led_off", tr("Mid/low band split")); - + createPixmapButton(tr("Enable High Band"), this, 143, 66, &controls->m_band1EnabledModel, "high_band_active", "high_band_inactive", tr("Enable High Band")); createPixmapButton(tr("Enable Mid Band"), this, 143, 146, &controls->m_band2EnabledModel, "mid_band_active", "mid_band_inactive", tr("Enable Mid Band")); createPixmapButton(tr("Enable Low Band"), this, 143, 226, &controls->m_band3EnabledModel, "low_band_active", "low_band_inactive", tr("Enable Low Band")); - + createKnob(KnobType::Bright26, this, 53, 43, &controls->m_inHighModel, tr("High Input Volume:"), " dB", tr("Input volume for high band")); createKnob(KnobType::Bright26, this, 53, 123, &controls->m_inMidModel, tr("Mid Input Volume:"), " dB", tr("Input volume for mid band")); createKnob(KnobType::Bright26, this, 53, 203, &controls->m_inLowModel, tr("Low Input Volume:"), " dB", tr("Input volume for low band")); createKnob(KnobType::Bright26, this, 320, 43, &controls->m_outHighModel, tr("High Output Volume:"), " dB", tr("Output volume for high band")); createKnob(KnobType::Bright26, this, 320, 123, &controls->m_outMidModel, tr("Mid Output Volume:"), " dB", tr("Output volume for mid band")); createKnob(KnobType::Bright26, this, 320, 203, &controls->m_outLowModel, tr("Low Output Volume:"), " dB", tr("Output volume for low band")); - + createLcdFloatSpinBox(3, 3, "11green", tr("Above Threshold High"), this, 300, 13, &controls->m_aThreshHModel, tr("Downward compression threshold for high band")); createLcdFloatSpinBox(3, 3, "11green", tr("Above Threshold Mid"), this, 300, 93, &controls->m_aThreshMModel, tr("Downward compression threshold for mid band")); createLcdFloatSpinBox(3, 3, "11green", tr("Above Threshold Low"), this, 300, 173, &controls->m_aThreshLModel, tr("Downward compression threshold for low band")); @@ -78,27 +78,27 @@ LOMMControlDialog::LOMMControlDialog(LOMMControls* controls) : createLcdFloatSpinBox(2, 2, "11green", tr("Below Ratio High"), this, 87, 44, &controls->m_bRatioHModel, tr("Upward compression ratio for high band")); createLcdFloatSpinBox(2, 2, "11green", tr("Below Ratio Mid"), this, 87, 124, &controls->m_bRatioMModel, tr("Upward compression ratio for mid band")); createLcdFloatSpinBox(2, 2, "11green", tr("Below Ratio Low"), this, 87, 204, &controls->m_bRatioLModel, tr("Upward compression ratio for low band")); - + createKnob(KnobType::Small17, this, 120, 61, &controls->m_atkHModel, tr("Attack High:"), " ms", tr("Attack time for high band")); createKnob(KnobType::Small17, this, 120, 141, &controls->m_atkMModel, tr("Attack Mid:"), " ms", tr("Attack time for mid band")); createKnob(KnobType::Small17, this, 120, 221, &controls->m_atkLModel, tr("Attack Low:"), " ms", tr("Attack time for low band")); createKnob(KnobType::Small17, this, 261, 61, &controls->m_relHModel, tr("Release High:"), " ms", tr("Release time for high band")); createKnob(KnobType::Small17, this, 261, 141, &controls->m_relMModel, tr("Release Mid:"), " ms", tr("Release time for mid band")); createKnob(KnobType::Small17, this, 261, 221, &controls->m_relLModel, tr("Release Low:"), " ms", tr("Release time for low band")); - + createKnob(KnobType::Small17, this, 380, 42, &controls->m_rmsTimeModel, tr("RMS Time:"), " ms", tr("RMS size for sidechain signal (set to 0 for Peak mode)")); createKnob(KnobType::Small17, this, 356, 42, &controls->m_kneeModel, tr("Knee:"), " dB", tr("Knee size for all compressors")); createKnob(KnobType::Small17, this, 24, 146, &controls->m_rangeModel, tr("Range:"), " dB", tr("Maximum gain increase for all bands")); createKnob(KnobType::Small17, this, 13, 114, &controls->m_balanceModel, tr("Balance:"), " dB", tr("Bias input volume towards one channel")); - + createPixmapButton(tr("Scale output volume with Depth"), this, 51, 0, &controls->m_depthScalingModel, "depthScaling_active", "depthScaling_inactive", tr("Scale output volume with Depth parameter")); createPixmapButton(tr("Stereo Link"), this, 52, 237, &controls->m_stereoLinkModel, "stereoLink_active", "stereoLink_inactive", tr("Apply same gain change to both channels")); - + createKnob(KnobType::Small17, this, 24, 80, &controls->m_autoTimeModel, tr("Auto Time:"), "", tr("Speed up attack and release times when transients occur")); createKnob(KnobType::Bright26, this, 363, 4, &controls->m_mixModel, tr("Mix:"), "", tr("Wet/Dry of all bands")); - + m_feedbackButton = createPixmapButton(tr("Feedback"), this, 317, 238, &controls->m_feedbackModel, "feedback_active", "feedback_inactive", tr("Use output as sidechain signal instead of input")); createPixmapButton(tr("Mid/Side"), this, 285, 238, &controls->m_midsideModel, "midside_active", "midside_inactive", tr("Compress mid/side channels instead of left/right")); @@ -107,14 +107,15 @@ LOMMControlDialog::LOMMControlDialog(LOMMControls* controls) : createPixmapButton(tr("Lookahead"), this, 147, 0, &controls->m_lookaheadEnableModel, "lookahead_active", "lookahead_inactive", tr(("Enable lookahead with fixed " + std::to_string(int(LOMM_MAX_LOOKAHEAD)) + " ms latency").c_str())); createLcdFloatSpinBox(2, 2, "11green", tr("Lookahead"), this, 214, 2, &controls->m_lookaheadModel, tr("Lookahead length")); - + PixmapButton* initButton = createPixmapButton(tr("Clear all parameters"), this, 84, 237, nullptr, "init_active", "init_inactive", tr("Clear all parameters")); - + initButton->setCheckable(false); + connect(initButton, SIGNAL(clicked()), m_controls, SLOT(resetAllParameters())); connect(&controls->m_lookaheadEnableModel, SIGNAL(dataChanged()), this, SLOT(updateFeedbackVisibility())); connect(&controls->m_midsideModel, SIGNAL(dataChanged()), this, SLOT(updateLowSideUpwardSuppressVisibility())); connect(getGUI()->mainWindow(), SIGNAL(periodicUpdate()), this, SLOT(updateDisplay())); - + emit updateFeedbackVisibility(); emit updateLowSideUpwardSuppressVisibility(); } @@ -161,7 +162,7 @@ void LOMMControlDialog::paintEvent(QPaintEvent *event) p.fillRect(LOMM_DISPLAY_X, LOMM_DISPLAY_Y[2 * i], thresholdsX[i + 3] - LOMM_DISPLAY_X, LOMM_DISPLAY_Y[2 * i + 1] + LOMM_DISPLAY_HEIGHT - LOMM_DISPLAY_Y[2 * i], bColor); p.drawLine(thresholdsX[i + 3], LOMM_DISPLAY_Y[2 * i], thresholdsX[i + 3], LOMM_DISPLAY_Y[2 * i + 1] + LOMM_DISPLAY_HEIGHT); } - + QPen inputPen(QColor(200, 200, 200, 80), 1); QPen outputPen(QColor(255, 255, 255, 255), 1); for (int i = 0; i < 3; ++i) { @@ -239,10 +240,10 @@ void LOMMControlDialog::mouseMoveEvent(QMouseEvent * event) const float distance = event->pos().x() - m_lastMousePos.x(); float dbDistance = distance * LOMM_DISPLAY_DB_PER_PIXEL; m_lastMousePos = event->pos(); - + FloatModel* aModel[] = {&m_controls->m_aThreshHModel, &m_controls->m_aThreshMModel, &m_controls->m_aThreshLModel}; FloatModel* bModel[] = {&m_controls->m_bThreshHModel, &m_controls->m_bThreshMModel, &m_controls->m_bThreshLModel}; - + float bVal = bModel[m_bandDrag]->value(); float aVal = aModel[m_bandDrag]->value(); if (m_dragType == 0) From 53d30d8b91930797dfe1bfdaaefb1731a1a95dab Mon Sep 17 00:00:00 2001 From: Dalton Messmer Date: Sun, 8 Jun 2025 20:15:16 -0400 Subject: [PATCH 58/62] Audio buffer views (#7945) Adds two non-owning views for audio buffers: `InterleavedBufferView` and `PlanarBufferView`. The channel count can be specified at either compile-time or runtime. Specifying at compile-time provides both performance and space optimizations. The way this works is the same as `std::span` except this uses the new `DynamicChannelCount` constant rather than `std::dynamic_extent`. --- include/AudioBufferView.h | 276 ++++++++++++++++++++++++++++++++++++++ include/LmmsTypes.h | 31 ++--- 2 files changed, 292 insertions(+), 15 deletions(-) create mode 100644 include/AudioBufferView.h diff --git a/include/AudioBufferView.h b/include/AudioBufferView.h new file mode 100644 index 000000000..56fff10fa --- /dev/null +++ b/include/AudioBufferView.h @@ -0,0 +1,276 @@ +/* + * AudioBufferView.h - Non-owning views for interleaved and + * non-interleaved (planar) buffers + * + * Copyright (c) 2025 Dalton Messmer + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LMMS_AUDIO_BUFFER_VIEW_H +#define LMMS_AUDIO_BUFFER_VIEW_H + +#include +#include +#include + +#include "LmmsTypes.h" + +namespace lmms +{ + +//! Use when the number of channels is not known at compile time +inline constexpr auto DynamicChannelCount = static_cast(-1); + + +namespace detail { + +// For static channel count +template +class BufferViewData +{ +public: + constexpr BufferViewData() = default; + constexpr BufferViewData(const BufferViewData&) = default; + + constexpr BufferViewData(SampleT* data, proc_ch_t channels, f_cnt_t frames) noexcept + : m_data{data} + , m_frames{frames} + { + assert(channels == channelCount); + } + + constexpr BufferViewData(SampleT* data, f_cnt_t frames) noexcept + : m_data{data} + , m_frames{frames} + { + } + + constexpr auto data() const noexcept -> SampleT* { return m_data; } + constexpr auto channels() const noexcept -> proc_ch_t { return channelCount; } + constexpr auto frames() const noexcept -> f_cnt_t { return m_frames; } + +protected: + SampleT* m_data = nullptr; + f_cnt_t m_frames = 0; +}; + +// For dynamic channel count +template +class BufferViewData +{ +public: + constexpr BufferViewData() = default; + constexpr BufferViewData(const BufferViewData&) = default; + + constexpr BufferViewData(SampleT* data, proc_ch_t channels, f_cnt_t frames) noexcept + : m_data{data} + , m_channels{channels} + , m_frames{frames} + { + assert(channels != DynamicChannelCount); + } + + constexpr auto data() const noexcept -> SampleT* { return m_data; } + constexpr auto channels() const noexcept -> proc_ch_t { return m_channels; } + constexpr auto frames() const noexcept -> f_cnt_t { return m_frames; } + +protected: + SampleT* m_data = nullptr; + proc_ch_t m_channels = 0; + f_cnt_t m_frames = 0; +}; + +} // namespace detail + + +/** + * Non-owning view for multi-channel interleaved audio data + * + * TODO C++23: Use std::mdspan? + */ +template +class InterleavedBufferView : public detail::BufferViewData +{ + using Base = detail::BufferViewData; + +public: + using Base::Base; + + //! Contruct const from mutable (static channel count) + template requires (std::is_const_v && channelCount != DynamicChannelCount) + constexpr InterleavedBufferView(InterleavedBufferView, channelCount> other) noexcept + : Base{other.data(), other.frames()} + { + } + + //! Contruct const from mutable (dynamic channel count) + template requires (std::is_const_v && channelCount == DynamicChannelCount) + constexpr InterleavedBufferView(InterleavedBufferView, channelCount> other) noexcept + : Base{other.data(), other.channels(), other.frames()} + { + } + + //! Construct dynamic channel count from static + template + requires (channelCount == DynamicChannelCount && otherChannels != DynamicChannelCount) + constexpr InterleavedBufferView(InterleavedBufferView other) noexcept + : Base{other.data(), otherChannels, other.frames()} + { + } + + constexpr auto empty() const noexcept -> bool + { + return !this->m_data || this->channels() == 0 || this->m_frames == 0; + } + + //! @return the frame at the given index + constexpr auto frame(f_cnt_t index) const noexcept + { + if constexpr (channelCount == DynamicChannelCount) + { + return std::span{framePtr(index), this->channels()}; + } + else + { + return std::span{framePtr(index), this->channels()}; + } + } + + /** + * @return pointer to the frame at the given index. + * The size of the frame is `channels()`. + */ + constexpr auto framePtr(f_cnt_t index) const noexcept -> SampleT* + { + assert(index < this->m_frames); + return this->m_data + index * this->channels(); + } + + /** + * @return pointer to the frame at the given index. + * The size of the frame is `channels()`. + */ + constexpr auto operator[](f_cnt_t index) const noexcept -> SampleT* + { + return framePtr(index); + } +}; + +// Check that the std::span-like space optimization works +static_assert(sizeof(InterleavedBufferView) > sizeof(InterleavedBufferView)); +static_assert(sizeof(InterleavedBufferView) == sizeof(void*) + sizeof(f_cnt_t)); + + +/** + * Non-owning view for multi-channel non-interleaved audio data + * + * The data type is `SampleT* const*` which is a 2D array accessed as data[channel][frame index] + * where each channel's buffer contains `frames()` frames. + * + * TODO C++23: Use std::mdspan? + */ +template +class PlanarBufferView : public detail::BufferViewData +{ + using Base = detail::BufferViewData; + +public: + using Base::Base; + + //! Contruct const from mutable (static channel count) + template requires (std::is_const_v && channelCount != DynamicChannelCount) + constexpr PlanarBufferView(PlanarBufferView, channelCount> other) noexcept + : Base{other.data(), other.frames()} + { + } + + //! Contruct const from mutable (dynamic channel count) + template requires (std::is_const_v && channelCount == DynamicChannelCount) + constexpr PlanarBufferView(PlanarBufferView, channelCount> other) noexcept + : Base{other.data(), other.channels(), other.frames()} + { + } + + //! Construct dynamic channel count from static + template + requires (channelCount == DynamicChannelCount && otherChannels != DynamicChannelCount) + constexpr PlanarBufferView(PlanarBufferView other) noexcept + : Base{other.data(), otherChannels, other.frames()} + { + } + + constexpr auto empty() const noexcept -> bool + { + return !this->m_data || this->channels() == 0 || this->m_frames == 0; + } + + //! @return the buffer of the given channel + constexpr auto buffer(proc_ch_t channel) const noexcept -> std::span + { + return {bufferPtr(channel), this->m_frames}; + } + + //! @return the buffer of the given channel + template requires (channelCount != DynamicChannelCount) + constexpr auto buffer() const noexcept -> std::span + { + return {bufferPtr(), this->m_frames}; + } + + /** + * @return pointer to the buffer of the given channel. + * The size of the buffer is `frames()`. + */ + constexpr auto bufferPtr(proc_ch_t channel) const noexcept -> SampleT* + { + assert(channel < this->channels()); + assert(this->m_data != nullptr); + return this->m_data[channel]; + } + + /** + * @return pointer to the buffer of the given channel. + * The size of the buffer is `frames()`. + */ + template requires (channelCount != DynamicChannelCount) + constexpr auto bufferPtr() const noexcept -> SampleT* + { + static_assert(channel < channelCount); + assert(this->m_data != nullptr); + return this->m_data[channel]; + } + + /** + * @return pointer to the buffer of a given channel. + * The size of the buffer is `frames()`. + */ + constexpr auto operator[](proc_ch_t channel) const noexcept -> SampleT* + { + return bufferPtr(channel); + } +}; + +// Check that the std::span-like space optimization works +static_assert(sizeof(PlanarBufferView) > sizeof(PlanarBufferView)); +static_assert(sizeof(PlanarBufferView) == sizeof(void**) + sizeof(f_cnt_t)); + +} // namespace lmms + +#endif // LMMS_AUDIO_BUFFER_VIEW_H diff --git a/include/LmmsTypes.h b/include/LmmsTypes.h index cf759aef9..c348a85bf 100644 --- a/include/LmmsTypes.h +++ b/include/LmmsTypes.h @@ -32,24 +32,25 @@ namespace lmms { -using bar_t = int32_t; -using tick_t = int32_t; -using volume_t = uint8_t; -using panning_t = int8_t; +using bar_t = std::int32_t; +using tick_t = std::int32_t; +using volume_t = std::uint8_t; +using panning_t = std::int8_t; -using sample_t = float; // standard sample-type -using int_sample_t = int16_t; // 16-bit-int-sample +using sample_t = float; // standard sample-type +using int_sample_t = std::int16_t; // 16-bit-int-sample -using sample_rate_t = uint32_t; // sample-rate -using fpp_t = size_t; // frames per period (0-16384) -using f_cnt_t = size_t; // standard frame-count -using ch_cnt_t = uint8_t; // channel-count (0-DEFAULT_CHANNELS) -using bpm_t = uint16_t; // tempo (MIN_BPM to MAX_BPM) -using bitrate_t = uint16_t; // bitrate in kbps -using mix_ch_t = uint16_t; // Mixer-channel (0 to MAX_CHANNEL) - -using jo_id_t = uint32_t; // (unique) ID of a journalling object +using sample_rate_t = std::uint32_t; // sample-rate +using fpp_t = std::size_t; // frames per period (0-16384) +using f_cnt_t = std::size_t; // standard frame-count +using ch_cnt_t = std::uint8_t; // channel-count (0-DEFAULT_CHANNELS) +using bpm_t = std::uint16_t; // tempo (MIN_BPM to MAX_BPM) +using bitrate_t = std::uint16_t; // bitrate in kbps +using mix_ch_t = std::uint16_t; // Mixer-channel (0 to MAX_CHANNEL) +using track_ch_t = std::uint16_t; // track channel index/count (0-256) +using proc_ch_t = std::uint16_t; // audio processor channel index/count +using jo_id_t = std::uint32_t; // (unique) ID of a journalling object } // namespace lmms From a505f570e944cdbf9c9aa532e67b947a578137af Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 11 Jun 2025 01:04:45 +0200 Subject: [PATCH 59/62] Fix TextFloat flickering (#7942) Fixes TextFloat flickering which occurs when scrolling on knobs, faders, and note volume in the piano roll. --------- Co-authored-by: Dalton Messmer --- include/SimpleTextFloat.h | 5 ++++- src/gui/editors/PianoRoll.cpp | 9 +-------- src/gui/widgets/Fader.cpp | 2 +- src/gui/widgets/FloatModelEditorBase.cpp | 2 +- src/gui/widgets/SimpleTextFloat.cpp | 10 ++-------- 5 files changed, 9 insertions(+), 19 deletions(-) diff --git a/include/SimpleTextFloat.h b/include/SimpleTextFloat.h index e930d2430..2821ca411 100644 --- a/include/SimpleTextFloat.h +++ b/include/SimpleTextFloat.h @@ -47,7 +47,10 @@ public: void showWithDelay(int msecBeforeDisplay, int msecDisplayTime); - void setVisibilityTimeOut(int msecs); + void showWithTimeout(int msec) + { + showWithDelay(0, msec); + } void moveGlobal(QWidget * w, const QPoint & offset) { diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index ea73e1993..3ef056c43 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -451,14 +451,7 @@ void PianoRoll::showTextFloat(const QString &text, const QPoint &pos, int timeou s_textFloat->setText( text ); // show the float, offset slightly so as to not obscure anything s_textFloat->moveGlobal( this, pos + QPoint(4, 16) ); - if (timeout == -1) - { - s_textFloat->show(); - } - else - { - s_textFloat->setVisibilityTimeOut( timeout ); - } + s_textFloat->showWithTimeout(timeout); } diff --git a/src/gui/widgets/Fader.cpp b/src/gui/widgets/Fader.cpp index 7ce85963b..46f336cb7 100644 --- a/src/gui/widgets/Fader.cpp +++ b/src/gui/widgets/Fader.cpp @@ -122,7 +122,7 @@ void Fader::adjustByDecibelDelta(float value) adjustModelByDBDelta(value); updateTextFloat(); - s_textFloat->setVisibilityTimeOut(1000); + s_textFloat->showWithTimeout(1000); } void Fader::adjustByDialog() diff --git a/src/gui/widgets/FloatModelEditorBase.cpp b/src/gui/widgets/FloatModelEditorBase.cpp index 3c7fe93c7..ed09fa261 100644 --- a/src/gui/widgets/FloatModelEditorBase.cpp +++ b/src/gui/widgets/FloatModelEditorBase.cpp @@ -338,7 +338,7 @@ void FloatModelEditorBase::wheelEvent(QWheelEvent * we) s_textFloat->setText(displayValue()); s_textFloat->moveGlobal(this, QPoint(width() + 2, 0)); - s_textFloat->setVisibilityTimeOut(1000); + s_textFloat->showWithTimeout(1000); emit sliderMoved(model()->value()); } diff --git a/src/gui/widgets/SimpleTextFloat.cpp b/src/gui/widgets/SimpleTextFloat.cpp index e37753229..1be683837 100644 --- a/src/gui/widgets/SimpleTextFloat.cpp +++ b/src/gui/widgets/SimpleTextFloat.cpp @@ -62,7 +62,7 @@ void SimpleTextFloat::setText(const QString & text) void SimpleTextFloat::showWithDelay(int msecBeforeDisplay, int msecDisplayTime) { - if (msecBeforeDisplay != 0) + if (msecBeforeDisplay > 0) { m_showTimer->start(msecBeforeDisplay); } @@ -71,7 +71,7 @@ void SimpleTextFloat::showWithDelay(int msecBeforeDisplay, int msecDisplayTime) show(); } - if (msecDisplayTime != 0) + if (msecDisplayTime > 0) { m_hideTimer->start(msecBeforeDisplay + msecDisplayTime); } @@ -84,10 +84,4 @@ void SimpleTextFloat::hide() QWidget::hide(); } -void SimpleTextFloat::setVisibilityTimeOut(int msecs) -{ - QTimer::singleShot(msecs, this, SLOT(hide())); - show(); -} - } // namespace lmms::gui From 45ab56dd71f38a203ea899af6b664451254cfc59 Mon Sep 17 00:00:00 2001 From: regulus79 <117475203+regulus79@users.noreply.github.com> Date: Fri, 13 Jun 2025 21:30:11 -0400 Subject: [PATCH 60/62] Fix Auto Resize when Dragging Sample Files onto Sample Clips (#7952) Changes things so that only sample clips with auto-resize enabled will automatically resize to the sample length when dragging-dropping a sample from the sidebar onto a sample clip. Non-auto-resize clips will keep their original length, but still update so that their start time offset is 0. --- src/core/SampleClip.cpp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/core/SampleClip.cpp b/src/core/SampleClip.cpp index 11aabc2ff..007265c0c 100644 --- a/src/core/SampleClip.cpp +++ b/src/core/SampleClip.cpp @@ -179,26 +179,21 @@ void SampleClip::setSampleBuffer(std::shared_ptr sb) void SampleClip::setSampleFile(const QString& sf) { - int length = 0; - + // Remove any prior offset in the clip + setStartTimeOffset(0); if (!sf.isEmpty()) { - //Otherwise set it to the sample's length m_sample = Sample(gui::SampleLoader::createBufferFromFile(sf)); - length = sampleLength(); + updateLength(); } - - if (length == 0) + else { - //If there is no sample, make the clip a bar long + // If there is no sample, make the clip a bar long float nom = Engine::getSong()->getTimeSigModel().getNumerator(); float den = Engine::getSong()->getTimeSigModel().getDenominator(); - length = DefaultTicksPerBar * (nom / den); + changeLength(DefaultTicksPerBar * (nom / den)); } - changeLength(length); - setStartTimeOffset(0); - emit sampleChanged(); emit playbackPositionChanged(); } From 76dddd8c66b103723940bf5f9326e6b8934e3d5c Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 15 Jun 2025 20:41:23 +0200 Subject: [PATCH 61/62] Don't undo zoom! (#7943) Makes it so that editor zoom, automation tension, along with quantization, note length, and all other combo boxes are not added to the undo history. --------- Co-authored-by: szeli1 <143485814+szeli1@users.noreply.github.com> --- include/ComboBoxModel.h | 1 + src/gui/editors/AutomationEditor.cpp | 3 +++ src/gui/editors/SongEditor.cpp | 11 +++++++---- src/gui/widgets/ComboBox.cpp | 8 ++++---- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/include/ComboBoxModel.h b/include/ComboBoxModel.h index 7037495ea..904629ee2 100644 --- a/include/ComboBoxModel.h +++ b/include/ComboBoxModel.h @@ -45,6 +45,7 @@ public: bool isDefaultConstructed = false ) : IntModel( 0, 0, 0, parent, displayName, isDefaultConstructed ) { + setJournalling(false); } void addItem( QString item, std::unique_ptr loader = nullptr ); diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index c3aa4a8ec..6872f26a4 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -114,10 +114,13 @@ AutomationEditor::AutomationEditor() : //keeps the direction of the widget, undepended on the locale setLayoutDirection( Qt::LeftToRight ); + // Set up tension model m_tensionModel = new FloatModel(1.f, 0.f, 1.f, 0.01f); + m_tensionModel->setJournalling(false); connect( m_tensionModel, SIGNAL(dataChanged()), this, SLOT(setTension())); + // Set up quantization model for (auto q : Quantizations) { m_quantizeModel.addItem(QString("1/%1").arg(q)); } diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index 678256a17..48a1a309e 100644 --- a/src/gui/editors/SongEditor.cpp +++ b/src/gui/editors/SongEditor.cpp @@ -94,9 +94,7 @@ SongEditor::SongEditor( Song * song ) : : DEFAULT_SETTINGS_WIDGET_WIDTH + TRACK_OP_WIDTH), m_selectRegion(false) { - m_zoomingModel->setParent(this); - m_snappingModel->setParent(this); - + // Set up timeline m_timeLine = new TimeLineWidget(m_trackHeadWidth, 32, pixelsPerBar(), m_song->getPlayPos(Song::PlayMode::Song), m_song->getTimeline(Song::PlayMode::Song), @@ -244,10 +242,15 @@ SongEditor::SongEditor( Song * song ) : connect(contentWidget()->verticalScrollBar(), SIGNAL(valueChanged(int)),this, SLOT(updateRubberband())); connect(m_timeLine, SIGNAL(selectionFinished()), this, SLOT(stopSelectRegion())); - //zoom connects + + // Set up zooming model + m_zoomingModel->setParent(this); + m_zoomingModel->setJournalling(false); connect(m_zoomingModel, SIGNAL(dataChanged()), this, SLOT(zoomingChanged())); + // Set up snapping model + m_snappingModel->setParent(this); for (float bars : SNAP_SIZES) { if (bars > 1.0f) diff --git a/src/gui/widgets/ComboBox.cpp b/src/gui/widgets/ComboBox.cpp index 945d30aa3..3683cd68c 100644 --- a/src/gui/widgets/ComboBox.cpp +++ b/src/gui/widgets/ComboBox.cpp @@ -69,7 +69,7 @@ ComboBox::ComboBox( QWidget * _parent, const QString & _name ) : void ComboBox::selectNext() { - model()->setInitValue( model()->value() + 1 ); + model()->setValue(model()->value() + 1); } @@ -77,7 +77,7 @@ void ComboBox::selectNext() void ComboBox::selectPrevious() { - model()->setInitValue( model()->value() - 1 ); + model()->setValue(model()->value() - 1); } @@ -221,7 +221,7 @@ void ComboBox::wheelEvent( QWheelEvent* event ) if( model() ) { const int direction = (event->angleDelta().y() < 0 ? 1 : -1) * (event->inverted() ? -1 : 1); - model()->setInitValue(model()->value() + direction); + model()->setValue(model()->value() + direction); update(); event->accept(); } @@ -234,7 +234,7 @@ void ComboBox::setItem( QAction* item ) { if( model() ) { - model()->setInitValue( item->data().toInt() ); + model()->setValue(item->data().toInt()); } } From 2806a31e4c5880cca95e9f92a0d5b20b82ca1824 Mon Sep 17 00:00:00 2001 From: Fawn Date: Sun, 15 Jun 2025 20:23:52 -0600 Subject: [PATCH 62/62] Use vector assets for Dispersion plugin (#7773) --- data/themes/default/style.css | 18 +++++++ plugins/Dispersion/CMakeLists.txt | 2 +- .../Dispersion/DispersionControlDialog.cpp | 46 ++++++++---------- plugins/Dispersion/artwork.png | Bin 8757 -> 0 bytes plugins/Dispersion/dc_active.png | Bin 9043 -> 0 bytes plugins/Dispersion/dc_inactive.png | Bin 8948 -> 0 bytes plugins/Dispersion/logo.png | Bin 774 -> 0 bytes plugins/Dispersion/logo.svg | 3 ++ 8 files changed, 43 insertions(+), 26 deletions(-) delete mode 100644 plugins/Dispersion/artwork.png delete mode 100644 plugins/Dispersion/dc_active.png delete mode 100644 plugins/Dispersion/dc_inactive.png delete mode 100644 plugins/Dispersion/logo.png create mode 100644 plugins/Dispersion/logo.svg diff --git a/data/themes/default/style.css b/data/themes/default/style.css index f7a5acc14..f78df0982 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -387,6 +387,24 @@ lmms--gui--TrackView > QWidget { } +QPushButton#btn { + color: #d1d8e4; + padding: 2 4; + border: 1 solid #000; + border-radius: 3; + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 255, 255, 20%), stop:0.1 rgba(255, 255, 255, 4%), stop:0.9 rgba(0, 0, 0, 4%), stop:1 rgba(0, 0, 0, 40%)); +} + +QPushButton#btn:hover { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 255, 255, 25%), stop:0.1 rgba(255, 255, 255, 7%), stop:0.9 rgba(255, 255, 255, 4%), stop:1 rgba(0, 0, 0, 35%)); +} + +QPushButton#btn:pressed, +QPushButton#btn:checked { + color: #02ee89; + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(0, 0, 0, 25%), stop:0.1 transparent, stop:0.9 rgba(0, 0, 0, 15%), stop:1 rgba(0, 0, 0, 35%)); +} + /* autoscroll, loop, stop behaviour toggle buttons */ /* track background config */ diff --git a/plugins/Dispersion/CMakeLists.txt b/plugins/Dispersion/CMakeLists.txt index a40e04b80..190a4ae86 100644 --- a/plugins/Dispersion/CMakeLists.txt +++ b/plugins/Dispersion/CMakeLists.txt @@ -1,3 +1,3 @@ INCLUDE(BuildPlugin) -BUILD_PLUGIN(dispersion Dispersion.cpp DispersionControls.cpp DispersionControlDialog.cpp MOCFILES DispersionControls.h DispersionControlDialog.h EMBEDDED_RESOURCES *.png) +BUILD_PLUGIN(dispersion Dispersion.cpp DispersionControls.cpp DispersionControlDialog.cpp MOCFILES DispersionControls.h DispersionControlDialog.h EMBEDDED_RESOURCES logo.svg) diff --git a/plugins/Dispersion/DispersionControlDialog.cpp b/plugins/Dispersion/DispersionControlDialog.cpp index 3a717d7aa..aacb48290 100644 --- a/plugins/Dispersion/DispersionControlDialog.cpp +++ b/plugins/Dispersion/DispersionControlDialog.cpp @@ -24,14 +24,14 @@ #include "DispersionControlDialog.h" -#include "DispersionControls.h" +#include + +#include "AutomatableButton.h" +#include "DispersionControls.h" #include "embed.h" #include "Knob.h" #include "LcdSpinBox.h" -#include "PixmapButton.h" - -#include namespace lmms::gui @@ -42,40 +42,36 @@ DispersionControlDialog::DispersionControlDialog(DispersionControls* controls) : EffectControlDialog(controls) { setAutoFillBackground(true); - QPalette pal; - pal.setBrush(backgroundRole(), PLUGIN_NAME::getIconPixmap("artwork")); - setPalette(pal); - auto layout = new QHBoxLayout(this); + layout->setSpacing(5); - LcdSpinBox * m_amountBox = new LcdSpinBox(3, this, "Amount"); - m_amountBox->setModel(&controls->m_amountModel); - m_amountBox->setLabel(tr("AMOUNT")); - m_amountBox->setToolTip(tr("Number of all-pass filters")); + auto amountBox = new LcdSpinBox(3, this, "Amount"); + amountBox->setModel(&controls->m_amountModel); + amountBox->setLabel(tr("AMOUNT")); + amountBox->setToolTip(tr("Number of all-pass filters")); + layout->addWidget(amountBox); - Knob * freqKnob = new Knob(KnobType::Bright26, tr("FREQ"), this); + auto freqKnob = new Knob(KnobType::Bright26, tr("FREQ"), this); freqKnob->setModel(&controls->m_freqModel); - freqKnob->setHintText(tr("Frequency:") , " Hz"); + freqKnob->setHintText(tr("Frequency:") , tr("Hz")); + layout->addWidget(freqKnob); - Knob * resoKnob = new Knob(KnobType::Bright26, tr("RESO"), this); + auto resoKnob = new Knob(KnobType::Bright26, tr("RESO"), this); resoKnob->setModel(&controls->m_resoModel); - resoKnob->setHintText(tr("Resonance:") , " octaves"); + resoKnob->setHintText(tr("Resonance:") , tr("octaves")); + layout->addWidget(resoKnob); - Knob * feedbackKnob = new Knob(KnobType::Bright26, tr("FEED"), this); + auto feedbackKnob = new Knob(KnobType::Bright26, tr("FEED"), this); feedbackKnob->setModel(&controls->m_feedbackModel); feedbackKnob->setHintText(tr("Feedback:") , ""); + layout->addWidget(feedbackKnob); - PixmapButton * dcButton = new PixmapButton(this, tr("DC Offset Removal")); - dcButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("dc_active")); - dcButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("dc_inactive")); + auto dcButton = new AutomatableButton(this, tr("DC Offset Removal")); dcButton->setCheckable(true); + dcButton->setText(tr("DC")); dcButton->setModel(&controls->m_dcModel); dcButton->setToolTip(tr("Remove DC Offset")); - - layout->addWidget(m_amountBox); - layout->addWidget(freqKnob); - layout->addWidget(resoKnob); - layout->addWidget(feedbackKnob); + dcButton->setObjectName("btn"); layout->addWidget(dcButton); } diff --git a/plugins/Dispersion/artwork.png b/plugins/Dispersion/artwork.png deleted file mode 100644 index 17e3b9a118973f04f70c8a813f0c4667a7eb6f5d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8757 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 F{s*w2i^Ko` diff --git a/plugins/Dispersion/dc_active.png b/plugins/Dispersion/dc_active.png deleted file mode 100644 index d9c8c93784521b5310d745be52a6a876d1782627..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9043 zcmeHrcTm$?(01q$sUjdvx}XSz9zyR4y-QOGAwYmY2%!ifA_|cry-Kf2mm-P+Qbds6 zJA!~x1f&RFK==aJ+va;`zPU5s``=9_$?xpheRlWRJ!f*xBO?QC8Xy}G007Xypc*EG zUsl5BJS92dyIj;e8vtNf^f$A_nZSLyF<1{rlpB&8cN>G`M*5)~0RX?rvNWqW{*pM2 z6RUSN#MRB%i<zMcuTXJVVt zwX|`oaQtwnx1@UO%`DybSD%%#WP6oa6pD!7W0aGQrcWT7v*a(s+F$R&vn4Xm{oHxT zmbKND9Gv;+zIDD{*ra^Gh_e{9-kjU$Xb z<>F6?n|=#Fxo!QcNVi7lct7Jgn%o1Zq`Qsq%V2nPbJN79-kK3wFGt~S1J>DHva zyEq%movuRRuE*g)14-=zyu?3aY=5ajicdS&iFA4w;7O~^Ogs#l30szDz+Cz5QLIvE z%p?6Rh;%ewzV2XA4h^PDVR&=_OK)$g&Sxopd0X3_C(Ignr7!^-XJ(==62{^6K~C4g z$n88o8!S#UADU>P_DCSnHb22cx1^C=MRLTpAj#U*wR_^?t1a9@=xF_B&ihQgz8n54 zwRNfok>J4&buCu!niZ#%x%b_sj;Y=+mYAQ_e9{pXvqFxH$UJ)yGT~X2QTMfK&E3N| zpygnFpG%`h(RfGD#6RziW>htA1y6blY%qlZd~#Su%An1isn`^q zeekkTXGd}Ad3&1h_ls5?d+MI;!2vJkQF6FqoMPsWX7!a#Us{v=}%#OS?vq(otX~h>VZ45+kI|Q zAES;W*?Ts@+rDm6O$!~#QSt7zRU}nmiz6sQ?x60R96{;}O?$Qe2Ic*7P~Z9)XTe+I zgYrZ>ij@_xNt4w4D^dEog#q(>&tLZEp-hLZS6hwQJ#%@V0_!zJDxap9Egtm_9Bo7? zb8v=sW^UQP*qyO;747uT2+Rooo_ww|Yiz!DR7mf-xA?@=n9Ut|o)Ng*E_Yv>^^Qx) zlEJO;N`xlqin_xm3|{(6pF2XXViSMEB6T(F=XA&2dhEm7SZu^RF&l zy`5qklHQidGdcq|&Mbzgz5zE`CfH>0qf&;PWCQvf9BtL2y5${^Pl-8RUnURHc^J6g z&c>YoNVD+5yrE)?dKEQILS&==`v68A{%Uio=P_RbyWPeNlvw-Fd5g@<&2&+a?MLpW zQ&%~)$mV&B@>*j24F#U^caOQx$;D`jidC0$IE9r+A3ONc$>GgA>Vva zNU>HPnqDysRsDRgjMc`K4jI$Q-ag@LtqyI{b1n?+o$N!U4%@%X0M8yLBI(mF)#Sd zuYeghaaT1O)OV)h$r?8tO5SsHVglJvBJ)F(N9&H~~eD-XEnzpZvQx`$~u-Y@6poE2^444)lcdOlzruWkxe zp5JWapnN47>gcv&dq17TLZnOa2C>6pcotjc4V7m7(e$v8Q3IZ!z`Dk+Rn3tc>8Qa( zWb$a&v{KE8$A(*w!W=CLI`d|uanze{j-P2A27%Wz7TJH?=t-L{vT~E#d!C+3d5Pp*VP_#Qsn?v$A$W?VIf$Xi81#~KjuYldD;}}` znVaH$Li?>{Hs#NfH#3&{nC_VU5I67Dx(l9{Q zTE?@tAwFw3qsBL~H>$znoIL89gU$mU*ly7Rq()UNNSM6j<3Y^Dw3E&!^jHFi2DfLZ z0-m^BL(^P~#mrPxw$$fb-qqPKOLO!gYq=8lE}tzfJjt0N@Qot?2lodwJ>|m`TrQ=% zM^C2Gvd znr*k}VDzPCN+&cWb;j&EQAmhSdfdLfd z63nAJSXOr}&WqOkM8PP3iVh=Ftavcz7&zSK%)LlOS$%Y8R}aWJ){Z|?D>gLOjY_SL zD`rc_8g38TS_()TIu5?Y!ou0;D+6mN#?=_*VQkY+GOciQ^3vH<%=aX_NBOfxRkNKN zfIKgl-*GnRVba>0QPN5W^TiY+w4^0C_ktbydP>&$T{T0a*~X`@Kw85T$%fw&vVUW! z5zA6HDYUF6!_9Zg-{9gtYIz{O8$28=lWyy6r(#7xH)}Fd5MMk$0n@QxnJ${jmaX0> zx){JP?lQ5!*FjS+O*Z*qS0G>)?-qT>1YJ1A7BI6x^yD(a6QEeBYULbL>=(McAvo^g z)M%1&^v;-)bg))V`$Q$3dq)TevF2TA8UCE|VIKkCF7>SS95O(p zXk&li8|Nk2KQt?W4|SY&y6)`B?VZSGeH@x_R^lQ)IAo1t3+*3qxgIjnpFelf(|;7h zcJOds#Y$ujw$vB@$s4LuN_vO9IKf@*#xuaKuCiMjGg8Kv3rigBS67gHG;Fc_T1hvO zUMsgT=+V$*F&!YjL`pV#ZV0XN{?6y!Pi#vo7THg2k`I4W*8%Su-Riz|Ut>}mW3WOU z971INP_6y4vi^z$$p*d#y&ThjozFgNk1W?hfHu+o93${^tWF{+fkhoh{A3W{0ezLFPw#DG>;(OWX(j*CuH$USIw#Z6O zY9(IT0=y=aHStyWdJSTv??E{=o1OZS2}^2W;Zdg(@@{NQSXW!@VmH*}e&Wq#UT%{r z9+qTb6*ZFFt??Dk`D)fwTll9;g|P}xnBkzJL@IKt%1;mql&B?K65lPMBot}1t5nM&8=_3ad*vVrRy$wKr( zW{8Ife@R;)Det*0>28_w-5zjcTzY}4w@?tOxZR-1OC-y=k%%G{~re)=Nlk8bbDb`_xO`Gi{K zF~NyMvow95T{OOOWJDH$%7mAPBsg$Hzv)b~Wl#tOi%ADOLC7WIe$fKx{{adbBaZuc6TH}|dMNFbt79?jRVY<~0leV?~rCDv#% zuY8_DFxsoGpC^rIj?Oo`m}E{Z+JF5*8p~enYE53wl&aENJ4blh2J;+oMMEPeF2GuQ zAFoh6Gf?$?jeRaJl`5Ag+ePCM=O-xBM7no~-id3$j|Fy8c@-{aw&a<;)x@5FN+5OEY8SZI*p}5_{}wz?mc< zICFli!q4R)o}~0cAr*(#@UsazQQ~U7!Y^+%!*BJs55-PEd$T__uQn}xEq`4!95x(> zJ9NBeSgp;khKGudlHbtG;_x^WI9P;Hh!^|FW@<1!rc75ePKXVRu=m#-_$Wkd1Kwyi z)*bkf^O_}%czviDQ_cCa!~GdbO@B=ZjZ<5}&86PhRDj{BrJ*T8Zb?j~9OM z27lLyCd1mETh-lvg9#L+7gpB>__VyI_?jGWca3?+H&k`_CGJxX8 z8nQ60SzKl08eby*TOa>jZsUUz0%-;ce%*te~;|7F<%dzs`8 z@z%8=nh3uTKbN6FxlKlGOJfs!-y?0BdhRX0F=X;bFbl`tPB%W*podW#|4@y+3*8+a zs-pnoFgJ_Z{>hrm)N7MIf=!g(?7Vfc7bVADY!{)*LTlQ02tt!qe&ckkphvP5U1y$x) zUk+Ly>u{zV^EZqR=4z|-$zVKZ1{W3QuoTMyg97TM3*E^ThOZLWWm>^Z&*C^f@2>O|;P5H9BYP=pIOOMN{E!UHV| zckr-Biu$23gbO+VKtb6L14pX#Ij9j1+mDaX1V_Ow8BUSJYQh)C21zCN3u@ zCkB!blaLT0Xoz^-cE`c}MBKgjP9c8B&_H@2uqX@;<>AhKiV3&(@Wv_f@)G8`|A-Hb z(bxYI-regL3j{vI{NNZdaZ!*M8ZGvB3oo3e4*}#?K>wqKml@$6Q_KYE<>8G*AT@oE z?l```LpUJ*w8wa3-Okc+K!_pTkZ6Lc7s0FeKU`|V^o{i%3a{gGAs6FhT?&0hhIhJ4%8I5NH|;s zECrV&KuLi`a(qKoKGbjfHM9Tw67)A~3nbQy z(21v*;vi8;i8J%lvOoyQ5Qv4J_9+42OisuJqK-wvaUNJR4-Yp*-qVn{Pc8qP)+a0{ z2RII{0mmT;pdblJ2uKnlA!jBb4w00CfC$x)h5QQO6i(m60p)o6|D}Dpc(@e^`h=20 zQCbgx%v; zyZ#|Z{SU3+ATH-9Cr((`U<4c?B88BYB|OQAi^xjbgQX;8~zogBVz@z|BDla zGljoxG6cKdWrWR(uosH`xf%ZAj1cet=Id82{x@B4bN}b$U-A1NUH{SbuNe4O%KvrO ze{}sT2L6@uf8F)}jV|C{$0?*c;a`w1;jrZHBAr1vXp!6NYHI*aPT$!LMezg)6$WbM z1ptWhoqmY?3Y2{aLJAyAUz6es$t6-=3fgPguL#HKDVT<;8MvzLU?Git_Sddi*iIar$55F;da%f3(G^0-TNQoq=tQcLcYab5YcMhEB(ZJ2p$JNr)zcHEh!AknS+uy4LF~Z3W2zB`det zC%Lx+IX0p9Ki4A0`zq#U!AxwrfWeD1ucKx|Z+CIQuNZSAr&u{IH_Inn*E}JbAgz`E z@{DUHBpJ`gF~+s?ii6h!o!nZ_l$oFWwA2PIPOGvN*eibVGWDmS0~(ngdY~pgyq!>a O08G7I{{I0n?sE$O diff --git a/plugins/Dispersion/dc_inactive.png b/plugins/Dispersion/dc_inactive.png deleted file mode 100644 index 9a0ee069399c47b10bdfc291a79e573eccf98591..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8948 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 HfAfC;^8!HH diff --git a/plugins/Dispersion/logo.png b/plugins/Dispersion/logo.png deleted file mode 100644 index 9340da708dd79ed97111eb535f51b81a91d6a15b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 774 zcmV+h1Nr=kP)7WEc)VQ)zLm`B#lSD% z0Wg;#IH?7|3b0p&fj_`2;A{cm@zx+(Ge?s$@EN$6LtMjg>;+(boCbaXw;ja=b6mQ?gRp67Yf18(18niCN0v&eY^{Cr&#;#IcF{ks?!* z&o!_q>9xbSw`QytRI!M?zP_o#K-8ExJaV?vi znPt?~0KhTu23U+Gy8^Tw;^SzWSet9n + +