From ec57884d95d4fbae71934f179b10ab08fe2e8119 Mon Sep 17 00:00:00 2001 From: Sotonye Atemie Date: Sat, 24 Jan 2026 19:50:59 -0500 Subject: [PATCH 01/12] Decouple mixer channel LCD updates from Mixer::moveChannelLeft --- include/InstrumentTrack.h | 1 + include/Mixer.h | 3 +++ include/SampleTrack.h | 1 + src/core/Mixer.cpp | 47 ++++------------------------------ src/tracks/InstrumentTrack.cpp | 7 ++++- src/tracks/SampleTrack.cpp | 7 +++++ 6 files changed, 23 insertions(+), 43 deletions(-) diff --git a/include/InstrumentTrack.h b/include/InstrumentTrack.h index b8777f88a44..cafa0d4f505 100644 --- a/include/InstrumentTrack.h +++ b/include/InstrumentTrack.h @@ -261,6 +261,7 @@ protected slots: void updatePitch(); void updatePitchRange(); void updateMixerChannel(); + void mixerChannelsSwapped(int fromIndex, int toIndex); private: diff --git a/include/Mixer.h b/include/Mixer.h index 6e3c86565ce..cf488d9b4ee 100644 --- a/include/Mixer.h +++ b/include/Mixer.h @@ -215,6 +215,9 @@ class LMMS_EXPORT Mixer : public Model, public JournallingObject MixerRouteVector m_mixerRoutes; +signals: + void channelsSwapped(int fromIndex, int toIndex); + private: // the mixer channels in the mixer. index 0 is always master. std::vector m_mixerChannels; diff --git a/include/SampleTrack.h b/include/SampleTrack.h index d333cd59394..4f119bad4e7 100644 --- a/include/SampleTrack.h +++ b/include/SampleTrack.h @@ -90,6 +90,7 @@ public slots: void updateClips(); void setPlayingClips( bool isPlaying ); void updateMixerChannel(); + void mixerChannelsSwapped(int fromIndex, int toIndex); private: FloatModel m_volumeModel; diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index 6007be466db..f2974ef43ae 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -427,10 +427,8 @@ void Mixer::deleteChannel( int index ) void Mixer::moveChannelLeft( int index ) { // can't move master or first channel - if (index <= 1 || static_cast(index) >= m_mixerChannels.size()) - { - return; - } + if (index <= 1 || static_cast(index) >= m_mixerChannels.size()) { return; } + // channels to swap int a = index - 1, b = index; @@ -438,49 +436,14 @@ void Mixer::moveChannelLeft( int index ) if (m_lastSoloed == a) { m_lastSoloed = b; } else if (m_lastSoloed == b) { m_lastSoloed = a; } - // go through every instrument and adjust for the channel index change - const TrackContainer::TrackList& songTrackList = Engine::getSong()->tracks(); - const TrackContainer::TrackList& patternTrackList = Engine::patternStore()->tracks(); - - for (const auto& trackList : {songTrackList, patternTrackList}) - { - for (const auto& track : trackList) - { - if (track->type() == Track::Type::Instrument) - { - auto inst = (InstrumentTrack*)track; - int val = inst->mixerChannelModel()->value(0); - if( val == a ) - { - inst->mixerChannelModel()->setValue(b); - } - else if( val == b ) - { - inst->mixerChannelModel()->setValue(a); - } - } - else if (track->type() == Track::Type::Sample) - { - auto strk = (SampleTrack*)track; - int val = strk->mixerChannelModel()->value(0); - if( val == a ) - { - strk->mixerChannelModel()->setValue(b); - } - else if( val == b ) - { - strk->mixerChannelModel()->setValue(a); - } - } - } - } - // Swap positions in array - qSwap(m_mixerChannels[index], m_mixerChannels[index - 1]); + std::swap(m_mixerChannels[index], m_mixerChannels[index - 1]); // Update m_channelIndex of both channels m_mixerChannels[index]->setIndex(index); m_mixerChannels[index - 1]->setIndex(index - 1); + + emit channelsSwapped(index, index - 1); } diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 18ad5c95844..982e028b9b6 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -109,6 +109,7 @@ 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); + connect(Engine::mixer(), &Mixer::channelsSwapped, this, &InstrumentTrack::mixerChannelsSwapped); autoAssignMidiDevice(true); } @@ -676,7 +677,11 @@ void InstrumentTrack::updateMixerChannel() m_audioBusHandle.setNextMixerChannel(m_mixerChannelModel.value()); } - +void InstrumentTrack::mixerChannelsSwapped(int fromIndex, int toIndex) +{ + if (m_mixerChannelModel.value() == fromIndex) { m_mixerChannelModel.setValue(toIndex); } + else if (m_mixerChannelModel.value() == toIndex) { m_mixerChannelModel.setValue(fromIndex); } +} int InstrumentTrack::masterKey( int _midi_key ) const diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index fe83b99e24e..20fbaf64cda 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -57,6 +57,7 @@ SampleTrack::SampleTrack(TrackContainer* tc) : m_mixerChannelModel.setRange(0, Engine::mixer()->numChannels()-1, 1); connect(&m_mixerChannelModel, SIGNAL(dataChanged()), this, SLOT(updateMixerChannel())); + connect(Engine::mixer(), &Mixer::channelsSwapped, this, &SampleTrack::mixerChannelsSwapped); } @@ -250,5 +251,11 @@ void SampleTrack::updateMixerChannel() m_audioBusHandle.setNextMixerChannel(m_mixerChannelModel.value()); } +void SampleTrack::mixerChannelsSwapped(int fromIndex, int toIndex) +{ + if (m_mixerChannelModel.value() == fromIndex) { m_mixerChannelModel.setValue(toIndex); } + else if (m_mixerChannelModel.value() == toIndex) { m_mixerChannelModel.setValue(fromIndex); } +} + } // namespace lmms From 443b5e1e323a573bae4a5f440888a8da5fcac066 Mon Sep 17 00:00:00 2001 From: Sotonye Atemie Date: Sat, 24 Jan 2026 19:55:41 -0500 Subject: [PATCH 02/12] Decouple mixer channel LCD updates from Mixer::deleteChannel --- include/InstrumentTrack.h | 1 + include/Mixer.h | 1 + include/SampleTrack.h | 1 + src/core/Mixer.cpp | 46 ++-------------------------------- src/tracks/InstrumentTrack.cpp | 7 ++++++ src/tracks/SampleTrack.cpp | 8 ++++++ 6 files changed, 20 insertions(+), 44 deletions(-) diff --git a/include/InstrumentTrack.h b/include/InstrumentTrack.h index cafa0d4f505..667382166b7 100644 --- a/include/InstrumentTrack.h +++ b/include/InstrumentTrack.h @@ -262,6 +262,7 @@ protected slots: void updatePitchRange(); void updateMixerChannel(); void mixerChannelsSwapped(int fromIndex, int toIndex); + void mixerChannelDeleted(int index); private: diff --git a/include/Mixer.h b/include/Mixer.h index cf488d9b4ee..63d74025500 100644 --- a/include/Mixer.h +++ b/include/Mixer.h @@ -216,6 +216,7 @@ class LMMS_EXPORT Mixer : public Model, public JournallingObject MixerRouteVector m_mixerRoutes; signals: + void channelDeleted(int index); void channelsSwapped(int fromIndex, int toIndex); private: diff --git a/include/SampleTrack.h b/include/SampleTrack.h index 4f119bad4e7..bdd2b1ec7be 100644 --- a/include/SampleTrack.h +++ b/include/SampleTrack.h @@ -91,6 +91,7 @@ public slots: void setPlayingClips( bool isPlaying ); void updateMixerChannel(); void mixerChannelsSwapped(int fromIndex, int toIndex); + void mixerChannelDeleted(int index); private: FloatModel m_volumeModel; diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index f2974ef43ae..0c20f599823 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -336,50 +336,6 @@ void Mixer::deleteChannel( int index ) // channel deletion is performed between mixer rounds Engine::audioEngine()->requestChangeInModel(); - // go through every instrument and adjust for the channel index change - TrackContainer::TrackList tracks; - - auto& songTracks = Engine::getSong()->tracks(); - auto& patternStoreTracks = Engine::patternStore()->tracks(); - tracks.insert(tracks.end(), songTracks.begin(), songTracks.end()); - tracks.insert(tracks.end(), patternStoreTracks.begin(), patternStoreTracks.end()); - - for( Track* t : tracks ) - { - if( t->type() == Track::Type::Instrument ) - { - auto inst = dynamic_cast(t); - int val = inst->mixerChannelModel()->value(0); - if( val == index ) - { - // we are deleting this track's channel send - // send to master - inst->mixerChannelModel()->setValue(0); - } - else if( val > index ) - { - // subtract 1 to make up for the missing channel - inst->mixerChannelModel()->setValue(val-1); - } - } - else if( t->type() == Track::Type::Sample ) - { - auto strk = dynamic_cast(t); - int val = strk->mixerChannelModel()->value(0); - if( val == index ) - { - // we are deleting this track's channel send - // send to master - strk->mixerChannelModel()->setValue(0); - } - else if( val > index ) - { - // subtract 1 to make up for the missing channel - strk->mixerChannelModel()->setValue(val-1); - } - } - } - MixerChannel * ch = m_mixerChannels[index]; // delete all of this channel's sends and receives @@ -419,6 +375,8 @@ void Mixer::deleteChannel( int index ) } } + emit channelDeleted(index); + Engine::audioEngine()->doneChangeInModel(); } diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 982e028b9b6..aa484ac3d24 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -110,6 +110,7 @@ InstrumentTrack::InstrumentTrack(TrackContainer* tc) : connect(&m_pitchRangeModel, SIGNAL(dataChanged()), this, SLOT(updatePitchRange()), Qt::DirectConnection); connect(&m_mixerChannelModel, SIGNAL(dataChanged()), this, SLOT(updateMixerChannel()), Qt::DirectConnection); connect(Engine::mixer(), &Mixer::channelsSwapped, this, &InstrumentTrack::mixerChannelsSwapped); + connect(Engine::mixer(), &Mixer::channelDeleted, this, &InstrumentTrack::mixerChannelDeleted); autoAssignMidiDevice(true); } @@ -683,6 +684,12 @@ void InstrumentTrack::mixerChannelsSwapped(int fromIndex, int toIndex) else if (m_mixerChannelModel.value() == toIndex) { m_mixerChannelModel.setValue(fromIndex); } } +void InstrumentTrack::mixerChannelDeleted(int index) +{ + if (m_mixerChannelModel.value() == index) { m_mixerChannelModel.setValue(0); } + else if (m_mixerChannelModel.value() > index) { m_mixerChannelModel.setValue(m_mixerChannelModel.value() - 1); } +} + int InstrumentTrack::masterKey( int _midi_key ) const { diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 20fbaf64cda..ccb28db7d20 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -58,6 +58,8 @@ SampleTrack::SampleTrack(TrackContainer* tc) : connect(&m_mixerChannelModel, SIGNAL(dataChanged()), this, SLOT(updateMixerChannel())); connect(Engine::mixer(), &Mixer::channelsSwapped, this, &SampleTrack::mixerChannelsSwapped); + connect(Engine::mixer(), &Mixer::channelDeleted, this, &SampleTrack::mixerChannelDeleted); + } @@ -257,5 +259,11 @@ void SampleTrack::mixerChannelsSwapped(int fromIndex, int toIndex) else if (m_mixerChannelModel.value() == toIndex) { m_mixerChannelModel.setValue(fromIndex); } } +void SampleTrack::mixerChannelDeleted(int index) +{ + if (m_mixerChannelModel.value() == index) { m_mixerChannelModel.setValue(0); } + else if (m_mixerChannelModel.value() > index) { m_mixerChannelModel.setValue(m_mixerChannelModel.value() - 1); } +} + } // namespace lmms From fdcc34063a5d0a2266d12530bbed955498b569ac Mon Sep 17 00:00:00 2001 From: Sotonye Atemie Date: Mon, 26 Jan 2026 09:43:36 -0500 Subject: [PATCH 03/12] Decouple mixer channel LCD range updates from MixerView --- include/InstrumentTrack.h | 2 +- include/Mixer.h | 1 + include/MixerView.h | 2 -- include/SampleTrack.h | 1 + src/core/Mixer.cpp | 1 + src/gui/MixerView.cpp | 34 ---------------------------------- src/tracks/InstrumentTrack.cpp | 6 ++++++ src/tracks/SampleTrack.cpp | 6 ++++++ 8 files changed, 16 insertions(+), 37 deletions(-) diff --git a/include/InstrumentTrack.h b/include/InstrumentTrack.h index 667382166b7..4cb9601c2d1 100644 --- a/include/InstrumentTrack.h +++ b/include/InstrumentTrack.h @@ -263,7 +263,7 @@ protected slots: void updateMixerChannel(); void mixerChannelsSwapped(int fromIndex, int toIndex); void mixerChannelDeleted(int index); - + void mixerChannelCreated(int index); private: void processCCEvent(int controller); diff --git a/include/Mixer.h b/include/Mixer.h index 63d74025500..3f32d2e77f3 100644 --- a/include/Mixer.h +++ b/include/Mixer.h @@ -216,6 +216,7 @@ class LMMS_EXPORT Mixer : public Model, public JournallingObject MixerRouteVector m_mixerRoutes; signals: + void channelCreated(int index); void channelDeleted(int index); void channelsSwapped(int fromIndex, int toIndex); diff --git a/include/MixerView.h b/include/MixerView.h index f5f98b62a2e..fd6feeaaa2c 100644 --- a/include/MixerView.h +++ b/include/MixerView.h @@ -124,8 +124,6 @@ private slots: QWidget* m_racksWidget; Mixer* m_mixer; - void updateMaxChannelSelector(); - friend class MixerChannelView; } ; diff --git a/include/SampleTrack.h b/include/SampleTrack.h index bdd2b1ec7be..58eb1a1282f 100644 --- a/include/SampleTrack.h +++ b/include/SampleTrack.h @@ -92,6 +92,7 @@ public slots: void updateMixerChannel(); void mixerChannelsSwapped(int fromIndex, int toIndex); void mixerChannelDeleted(int index); + void mixerChannelCreated(int index); private: FloatModel m_volumeModel; diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index 0c20f599823..2b966cad1a0 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -276,6 +276,7 @@ int Mixer::createChannel() m_mixerChannels[index]->m_muteModel.setValue(true); } + emit channelCreated(index); return index; } diff --git a/src/gui/MixerView.cpp b/src/gui/MixerView.cpp index 299f1bff569..9d2dc1e5fed 100644 --- a/src/gui/MixerView.cpp +++ b/src/gui/MixerView.cpp @@ -190,8 +190,6 @@ int MixerView::addNewChannel() updateMixerChannel(newChannelIndex); - updateMaxChannelSelector(); - return newChannelIndex; } @@ -231,38 +229,8 @@ void MixerView::refreshDisplay() { updateMixerChannel(i); } - - updateMaxChannelSelector(); } - -// update the and max. channel number for every instrument -void MixerView::updateMaxChannelSelector() -{ - const TrackContainer::TrackList& songTracks = Engine::getSong()->tracks(); - const TrackContainer::TrackList& patternStoreTracks = Engine::patternStore()->tracks(); - - for (const auto& trackList : {songTracks, patternStoreTracks}) - { - for (const auto& track : trackList) - { - if (track->type() == Track::Type::Instrument) - { - auto inst = (InstrumentTrack*)track; - inst->mixerChannelModel()->setRange(0, - m_mixerChannelViews.size()-1,1); - } - else if (track->type() == Track::Type::Sample) - { - auto strk = (SampleTrack*)track; - strk->mixerChannelModel()->setRange(0, - m_mixerChannelViews.size()-1,1); - } - } - } -} - - void MixerView::saveSettings(QDomDocument& doc, QDomElement& domElement) { MainWindow::saveWidgetState(this, domElement); @@ -415,8 +383,6 @@ void MixerView::deleteChannel(int index) selLine = m_mixerChannelViews.size() - 1; } setCurrentMixerChannel(selLine); - - updateMaxChannelSelector(); } void MixerView::deleteUnusedChannels() diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index aa484ac3d24..dc03264c0ca 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -111,6 +111,7 @@ InstrumentTrack::InstrumentTrack(TrackContainer* tc) : connect(&m_mixerChannelModel, SIGNAL(dataChanged()), this, SLOT(updateMixerChannel()), Qt::DirectConnection); connect(Engine::mixer(), &Mixer::channelsSwapped, this, &InstrumentTrack::mixerChannelsSwapped); connect(Engine::mixer(), &Mixer::channelDeleted, this, &InstrumentTrack::mixerChannelDeleted); + connect(Engine::mixer(), &Mixer::channelCreated, this, &InstrumentTrack::mixerChannelCreated); autoAssignMidiDevice(true); } @@ -688,8 +689,13 @@ void InstrumentTrack::mixerChannelDeleted(int index) { if (m_mixerChannelModel.value() == index) { m_mixerChannelModel.setValue(0); } else if (m_mixerChannelModel.value() > index) { m_mixerChannelModel.setValue(m_mixerChannelModel.value() - 1); } + m_mixerChannelModel.setRange(0, m_mixerChannelModel.maxValue() - 1); } +void InstrumentTrack::mixerChannelCreated(int index) +{ + m_mixerChannelModel.setRange(0, m_mixerChannelModel.maxValue() + 1); +} int InstrumentTrack::masterKey( int _midi_key ) const { diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index ccb28db7d20..7b393bb08e3 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -59,6 +59,7 @@ SampleTrack::SampleTrack(TrackContainer* tc) : connect(&m_mixerChannelModel, SIGNAL(dataChanged()), this, SLOT(updateMixerChannel())); connect(Engine::mixer(), &Mixer::channelsSwapped, this, &SampleTrack::mixerChannelsSwapped); connect(Engine::mixer(), &Mixer::channelDeleted, this, &SampleTrack::mixerChannelDeleted); + connect(Engine::mixer(), &Mixer::channelCreated, this, &SampleTrack::mixerChannelCreated); } @@ -263,7 +264,12 @@ void SampleTrack::mixerChannelDeleted(int index) { if (m_mixerChannelModel.value() == index) { m_mixerChannelModel.setValue(0); } else if (m_mixerChannelModel.value() > index) { m_mixerChannelModel.setValue(m_mixerChannelModel.value() - 1); } + m_mixerChannelModel.setRange(0, m_mixerChannelModel.maxValue() - 1); } +void SampleTrack::mixerChannelCreated(int index) +{ + m_mixerChannelModel.setRange(0, m_mixerChannelModel.maxValue() + 1); +} } // namespace lmms From 07ee6beebf778a3e72799c44c35f500077d147cc Mon Sep 17 00:00:00 2001 From: Sotonye Atemie Date: Thu, 19 Mar 2026 15:26:36 -0400 Subject: [PATCH 04/12] Add MixerChannelModel --- include/InstrumentTrack.h | 8 ++---- include/MixerChannelModel.h | 50 ++++++++++++++++++++++++++++++++++ include/SampleTrack.h | 6 ++-- src/core/CMakeLists.txt | 1 + src/core/MixerChannelModel.cpp | 34 +++++++++++++++++++++++ src/tracks/InstrumentTrack.cpp | 24 ---------------- src/tracks/SampleTrack.cpp | 24 ---------------- 7 files changed, 89 insertions(+), 58 deletions(-) create mode 100644 include/MixerChannelModel.h create mode 100644 src/core/MixerChannelModel.cpp diff --git a/include/InstrumentTrack.h b/include/InstrumentTrack.h index 4cb9601c2d1..345c5ddf91b 100644 --- a/include/InstrumentTrack.h +++ b/include/InstrumentTrack.h @@ -26,7 +26,6 @@ #ifndef LMMS_INSTRUMENT_TRACK_H #define LMMS_INSTRUMENT_TRACK_H - #include "AudioBusHandle.h" #include "InstrumentFunctions.h" #include "InstrumentSoundShaping.h" @@ -34,12 +33,12 @@ #include "Midi.h" #include "MidiEventProcessor.h" #include "MidiPort.h" +#include "MixerChannelModel.h" #include "NotePlayHandle.h" #include "Piano.h" #include "Plugin.h" #include "Track.h" - namespace lmms { @@ -261,9 +260,6 @@ protected slots: void updatePitch(); void updatePitchRange(); void updateMixerChannel(); - void mixerChannelsSwapped(int fromIndex, int toIndex); - void mixerChannelDeleted(int index); - void mixerChannelCreated(int index); private: void processCCEvent(int controller); @@ -298,7 +294,7 @@ protected slots: FloatModel m_pitchModel; IntModel m_pitchRangeModel; - IntModel m_mixerChannelModel; + MixerChannelModel m_mixerChannelModel; BoolModel m_useMasterPitchModel; Instrument * m_instrument; diff --git a/include/MixerChannelModel.h b/include/MixerChannelModel.h new file mode 100644 index 00000000000..5a7f6903e71 --- /dev/null +++ b/include/MixerChannelModel.h @@ -0,0 +1,50 @@ +/* + * MixerChannelLcdSpinBox.h + * + * Copyright (c) 2026 saker + * + * 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_MIXER_CHANNEL_MODEL_H +#define LMMS_MIXER_CHANNEL_MODEL_H + +#include "AutomatableModel.h" + +namespace lmms { +/** + * @brief An @ref IntModel that keeps track of its assigned mixer channel. + * + * This model tracks its assigned mixer channel. It is a subclass of IntModel that adds functionality to handle channel + * creation, deletion, and swapping. Both the value and valid range are automatically updated to reflect these changes. + */ +class MixerChannelModel : public IntModel +{ +public: + MixerChannelModel(Model* parent = nullptr); + +private: + void channelsSwapped(int fromIndex, int toIndex); + void channelDeleted(int index); + void channelCreated(int index); +}; + +} // namespace lmms + +#endif // LMMS_MIXER_CHANNEL_MODEL_H \ No newline at end of file diff --git a/include/SampleTrack.h b/include/SampleTrack.h index 58eb1a1282f..aa2983226ab 100644 --- a/include/SampleTrack.h +++ b/include/SampleTrack.h @@ -26,6 +26,7 @@ #define LMMS_SAMPLE_TRACK_H #include "AudioBusHandle.h" +#include "MixerChannelModel.h" #include "Track.h" @@ -90,14 +91,11 @@ public slots: void updateClips(); void setPlayingClips( bool isPlaying ); void updateMixerChannel(); - void mixerChannelsSwapped(int fromIndex, int toIndex); - void mixerChannelDeleted(int index); - void mixerChannelCreated(int index); private: FloatModel m_volumeModel; FloatModel m_panningModel; - IntModel m_mixerChannelModel; + MixerChannelModel m_mixerChannelModel; AudioBusHandle m_audioBusHandle; bool m_isPlaying; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 85344487b68..ffcd03ee08f 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -44,6 +44,7 @@ set(LMMS_SRCS core/MicroTimer.cpp core/Microtuner.cpp core/MixHelpers.cpp + core/MixerChannelModel.cpp core/Model.cpp core/ModelVisitor.cpp core/Note.cpp diff --git a/src/core/MixerChannelModel.cpp b/src/core/MixerChannelModel.cpp new file mode 100644 index 00000000000..a8eb300dd54 --- /dev/null +++ b/src/core/MixerChannelModel.cpp @@ -0,0 +1,34 @@ +#include "MixerChannelModel.h" + +#include "Engine.h" +#include "Mixer.h" + +namespace lmms { +MixerChannelModel::MixerChannelModel(Model* parent) + : IntModel(0, 0, 0, parent, tr("Mixer channel")) +{ + setRange(0, Engine::mixer()->numChannels() - 1, 1); + connect(Engine::mixer(), &Mixer::channelsSwapped, this, &MixerChannelModel::channelsSwapped); + connect(Engine::mixer(), &Mixer::channelDeleted, this, &MixerChannelModel::channelDeleted); + connect(Engine::mixer(), &Mixer::channelCreated, this, &MixerChannelModel::channelCreated); +} + +void MixerChannelModel::channelsSwapped(int fromIndex, int toIndex) +{ + if (value() == fromIndex) { setValue(toIndex); } + else if (value() == toIndex) { setValue(fromIndex); } +} + +void MixerChannelModel::channelDeleted(int index) +{ + if (value() == index) { setValue(0); } + else if (value() > index) { setValue(value() - 1); } + setRange(0, maxValue() - 1); +} + +void MixerChannelModel::channelCreated(int index) +{ + setRange(0, maxValue() + 1); +} + +} // namespace lmms \ No newline at end of file diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index dc03264c0ca..62d99ceb694 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -63,7 +63,6 @@ InstrumentTrack::InstrumentTrack(TrackContainer* tc) : m_audioBusHandle(tr("unnamed_track"), true, &m_volumeModel, &m_panningModel, &m_mutedModel), m_pitchModel(0, MinPitchDefault, MaxPitchDefault, 1, this, tr("Pitch")), m_pitchRangeModel(1, 1, 60, this, tr("Pitch range")), - m_mixerChannelModel(0, 0, 0, this, tr("Mixer channel")), m_useMasterPitchModel(true, this, tr("Master pitch")), m_instrument(nullptr), m_soundShaping(this), @@ -79,8 +78,6 @@ InstrumentTrack::InstrumentTrack(TrackContainer* tc) : m_firstKeyModel.setInitValue(0); m_lastKeyModel.setInitValue(NumKeys - 1); - m_mixerChannelModel.setRange( 0, Engine::mixer()->numChannels()-1, 1); - for( int i = 0; i < NumKeys; ++i ) { m_notes[i] = nullptr; @@ -109,9 +106,6 @@ 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); - connect(Engine::mixer(), &Mixer::channelsSwapped, this, &InstrumentTrack::mixerChannelsSwapped); - connect(Engine::mixer(), &Mixer::channelDeleted, this, &InstrumentTrack::mixerChannelDeleted); - connect(Engine::mixer(), &Mixer::channelCreated, this, &InstrumentTrack::mixerChannelCreated); autoAssignMidiDevice(true); } @@ -679,24 +673,6 @@ void InstrumentTrack::updateMixerChannel() m_audioBusHandle.setNextMixerChannel(m_mixerChannelModel.value()); } -void InstrumentTrack::mixerChannelsSwapped(int fromIndex, int toIndex) -{ - if (m_mixerChannelModel.value() == fromIndex) { m_mixerChannelModel.setValue(toIndex); } - else if (m_mixerChannelModel.value() == toIndex) { m_mixerChannelModel.setValue(fromIndex); } -} - -void InstrumentTrack::mixerChannelDeleted(int index) -{ - if (m_mixerChannelModel.value() == index) { m_mixerChannelModel.setValue(0); } - else if (m_mixerChannelModel.value() > index) { m_mixerChannelModel.setValue(m_mixerChannelModel.value() - 1); } - m_mixerChannelModel.setRange(0, m_mixerChannelModel.maxValue() - 1); -} - -void InstrumentTrack::mixerChannelCreated(int index) -{ - m_mixerChannelModel.setRange(0, m_mixerChannelModel.maxValue() + 1); -} - int InstrumentTrack::masterKey( int _midi_key ) const { diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 7b393bb08e3..8fffce1bbaf 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -48,19 +48,13 @@ SampleTrack::SampleTrack(TrackContainer* tc) : Track(Track::Type::Sample, tc), m_volumeModel(DefaultVolume, MinVolume, MaxVolume, 0.1f, this, tr("Volume")), m_panningModel(DefaultPanning, PanningLeft, PanningRight, 0.1f, this, tr("Panning")), - m_mixerChannelModel(0, 0, 0, this, tr("Mixer channel")), m_audioBusHandle(tr("Sample track"), true, &m_volumeModel, &m_panningModel, &m_mutedModel), m_isPlaying(false) { setName(tr("Sample track")); m_panningModel.setCenterValue(DefaultPanning); - m_mixerChannelModel.setRange(0, Engine::mixer()->numChannels()-1, 1); connect(&m_mixerChannelModel, SIGNAL(dataChanged()), this, SLOT(updateMixerChannel())); - connect(Engine::mixer(), &Mixer::channelsSwapped, this, &SampleTrack::mixerChannelsSwapped); - connect(Engine::mixer(), &Mixer::channelDeleted, this, &SampleTrack::mixerChannelDeleted); - connect(Engine::mixer(), &Mixer::channelCreated, this, &SampleTrack::mixerChannelCreated); - } @@ -254,22 +248,4 @@ void SampleTrack::updateMixerChannel() m_audioBusHandle.setNextMixerChannel(m_mixerChannelModel.value()); } -void SampleTrack::mixerChannelsSwapped(int fromIndex, int toIndex) -{ - if (m_mixerChannelModel.value() == fromIndex) { m_mixerChannelModel.setValue(toIndex); } - else if (m_mixerChannelModel.value() == toIndex) { m_mixerChannelModel.setValue(fromIndex); } -} - -void SampleTrack::mixerChannelDeleted(int index) -{ - if (m_mixerChannelModel.value() == index) { m_mixerChannelModel.setValue(0); } - else if (m_mixerChannelModel.value() > index) { m_mixerChannelModel.setValue(m_mixerChannelModel.value() - 1); } - m_mixerChannelModel.setRange(0, m_mixerChannelModel.maxValue() - 1); -} - -void SampleTrack::mixerChannelCreated(int index) -{ - m_mixerChannelModel.setRange(0, m_mixerChannelModel.maxValue() + 1); -} - } // namespace lmms From 357901ad4245ef367a5540e7519590aaf9733327 Mon Sep 17 00:00:00 2001 From: Sotonye Atemie Date: Thu, 19 Mar 2026 17:10:43 -0400 Subject: [PATCH 05/12] Decouple tracks from mixer when checking if channel is in use --- include/AutomatableModel.h | 1 + include/Mixer.h | 1 + src/core/Mixer.cpp | 38 +++++----------------------------- src/core/MixerChannelModel.cpp | 8 +++++++ 4 files changed, 15 insertions(+), 33 deletions(-) diff --git a/include/AutomatableModel.h b/include/AutomatableModel.h index f7d9b470e80..d53de7f5fd2 100644 --- a/include/AutomatableModel.h +++ b/include/AutomatableModel.h @@ -144,6 +144,7 @@ class LMMS_EXPORT AutomatableModel : public Model, public JournallingObject return (std::round(v) != 0); } + float oldValue() const { return m_oldValue; } template inline T value( int frameOffset = 0 ) const diff --git a/include/Mixer.h b/include/Mixer.h index 3f32d2e77f3..ed80be1b316 100644 --- a/include/Mixer.h +++ b/include/Mixer.h @@ -65,6 +65,7 @@ class MixerChannel : public ThreadableJob QMutex m_lock; bool m_queued; // are we queued up for rendering yet? bool m_muted; // are we muted? updated per period so we don't have to call m_muteModel.value() twice + int m_useCount; // pointers to other channels that this one sends to MixerRouteVector m_sends; diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index 2b966cad1a0..ca74bf2f9ab 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -70,6 +70,7 @@ MixerChannel::MixerChannel( int idx, Model * _parent ) : m_name(), m_lock(), m_queued( false ), + m_useCount(0), m_dependenciesMet(0), m_channelIndex(idx) { @@ -813,41 +814,12 @@ void Mixer::validateChannelName( int index, int oldIndex ) bool Mixer::isChannelInUse(int index) { - // check if the index mixer channel receives audio from any other channel - if (!m_mixerChannels[index]->m_receives.empty()) - { - return true; - } - - // check if the destination mixer channel on any instrument or sample track is the index mixer channel - TrackContainer::TrackList tracks; + assert(index >= 0 && index < m_mixerChannels.size()); - auto& songTracks = Engine::getSong()->tracks(); - auto& patternStoreTracks = Engine::patternStore()->tracks(); - tracks.insert(tracks.end(), songTracks.begin(), songTracks.end()); - tracks.insert(tracks.end(), patternStoreTracks.begin(), patternStoreTracks.end()); - - for (const auto t : tracks) - { - if (t->type() == Track::Type::Instrument) - { - auto inst = dynamic_cast(t); - if (inst->mixerChannelModel()->value() == index) - { - return true; - } - } - else if (t->type() == Track::Type::Sample) - { - auto strack = dynamic_cast(t); - if (strack->mixerChannelModel()->value() == index) - { - return true; - } - } - } + // check if the index mixer channel receives audio from any other channel + if (!m_mixerChannels[index]->m_receives.empty()) { return true; } - return false; + return m_mixerChannels[index]->m_useCount > 0; } diff --git a/src/core/MixerChannelModel.cpp b/src/core/MixerChannelModel.cpp index a8eb300dd54..1aef88af9b2 100644 --- a/src/core/MixerChannelModel.cpp +++ b/src/core/MixerChannelModel.cpp @@ -8,9 +8,17 @@ MixerChannelModel::MixerChannelModel(Model* parent) : IntModel(0, 0, 0, parent, tr("Mixer channel")) { setRange(0, Engine::mixer()->numChannels() - 1, 1); + connect(Engine::mixer(), &Mixer::channelsSwapped, this, &MixerChannelModel::channelsSwapped); connect(Engine::mixer(), &Mixer::channelDeleted, this, &MixerChannelModel::channelDeleted); connect(Engine::mixer(), &Mixer::channelCreated, this, &MixerChannelModel::channelCreated); + + connect(this, &MixerChannelModel::dataChanged, Engine::mixer(), [this] { + --Engine::mixer()->mixerChannel(oldValue())->m_useCount; + ++Engine::mixer()->mixerChannel(value())->m_useCount; + }); + + ++Engine::mixer()->mixerChannel(0)->m_useCount; } void MixerChannelModel::channelsSwapped(int fromIndex, int toIndex) From c0bbb077a1d0a2239c2637e7b9934e0312e8f08b Mon Sep 17 00:00:00 2001 From: Sotonye Atemie Date: Thu, 19 Mar 2026 17:18:43 -0400 Subject: [PATCH 06/12] Update copyright --- include/MixerChannelModel.h | 2 +- src/core/MixerChannelModel.cpp | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/include/MixerChannelModel.h b/include/MixerChannelModel.h index 5a7f6903e71..7180b45df56 100644 --- a/include/MixerChannelModel.h +++ b/include/MixerChannelModel.h @@ -1,5 +1,5 @@ /* - * MixerChannelLcdSpinBox.h + * MixerChannelModel.h * * Copyright (c) 2026 saker * diff --git a/src/core/MixerChannelModel.cpp b/src/core/MixerChannelModel.cpp index 1aef88af9b2..10242063b8e 100644 --- a/src/core/MixerChannelModel.cpp +++ b/src/core/MixerChannelModel.cpp @@ -1,3 +1,27 @@ +/* + * MixerChannelModel.cpp + * + * Copyright (c) 2026 saker + * + * 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. + * + */ + #include "MixerChannelModel.h" #include "Engine.h" From 942ba8097a2f1eebff8f1d3269cefb37c5f4f341 Mon Sep 17 00:00:00 2001 From: Sotonye Atemie Date: Thu, 19 Mar 2026 17:19:13 -0400 Subject: [PATCH 07/12] Add newlines at the end of file --- include/MixerChannelModel.h | 2 +- src/core/MixerChannelModel.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/MixerChannelModel.h b/include/MixerChannelModel.h index 7180b45df56..0a302e31e4e 100644 --- a/include/MixerChannelModel.h +++ b/include/MixerChannelModel.h @@ -47,4 +47,4 @@ class MixerChannelModel : public IntModel } // namespace lmms -#endif // LMMS_MIXER_CHANNEL_MODEL_H \ No newline at end of file +#endif // LMMS_MIXER_CHANNEL_MODEL_H diff --git a/src/core/MixerChannelModel.cpp b/src/core/MixerChannelModel.cpp index 10242063b8e..7512198f02a 100644 --- a/src/core/MixerChannelModel.cpp +++ b/src/core/MixerChannelModel.cpp @@ -63,4 +63,4 @@ void MixerChannelModel::channelCreated(int index) setRange(0, maxValue() + 1); } -} // namespace lmms \ No newline at end of file +} // namespace lmms From 694d82c1a10eeb69eaf8bd67fa69e939f2e582d8 Mon Sep 17 00:00:00 2001 From: Sotonye Atemie Date: Fri, 20 Mar 2026 11:55:20 -0400 Subject: [PATCH 08/12] Do not update use count when the attached changed only changed in index Also update the use count when any mixer channel model is deleted --- include/MixerChannelModel.h | 6 ++++++ src/core/MixerChannelModel.cpp | 28 ++++++++++++++++++++-------- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/include/MixerChannelModel.h b/include/MixerChannelModel.h index 0a302e31e4e..e4d6316ad1e 100644 --- a/include/MixerChannelModel.h +++ b/include/MixerChannelModel.h @@ -38,6 +38,12 @@ class MixerChannelModel : public IntModel { public: MixerChannelModel(Model* parent = nullptr); + ~MixerChannelModel(); + + MixerChannelModel(const MixerChannelModel&) = delete; + MixerChannelModel(MixerChannelModel&&) = delete; + MixerChannelModel& operator=(const MixerChannelModel&) = delete; + MixerChannelModel& operator=(MixerChannelModel&&) = delete; private: void channelsSwapped(int fromIndex, int toIndex); diff --git a/src/core/MixerChannelModel.cpp b/src/core/MixerChannelModel.cpp index 7512198f02a..cf0202e3531 100644 --- a/src/core/MixerChannelModel.cpp +++ b/src/core/MixerChannelModel.cpp @@ -31,18 +31,30 @@ namespace lmms { MixerChannelModel::MixerChannelModel(Model* parent) : IntModel(0, 0, 0, parent, tr("Mixer channel")) { - setRange(0, Engine::mixer()->numChannels() - 1, 1); + const auto mixer = Engine::mixer(); + setRange(0, mixer->numChannels() - 1, 1); - connect(Engine::mixer(), &Mixer::channelsSwapped, this, &MixerChannelModel::channelsSwapped); - connect(Engine::mixer(), &Mixer::channelDeleted, this, &MixerChannelModel::channelDeleted); - connect(Engine::mixer(), &Mixer::channelCreated, this, &MixerChannelModel::channelCreated); + connect(mixer, &Mixer::channelsSwapped, this, &MixerChannelModel::channelsSwapped); + connect(mixer, &Mixer::channelDeleted, this, &MixerChannelModel::channelDeleted); + connect(mixer, &Mixer::channelCreated, this, &MixerChannelModel::channelCreated); - connect(this, &MixerChannelModel::dataChanged, Engine::mixer(), [this] { - --Engine::mixer()->mixerChannel(oldValue())->m_useCount; - ++Engine::mixer()->mixerChannel(value())->m_useCount; + connect(this, &MixerChannelModel::dataChanged, mixer, [this, mixer] { + // both channels must exist so we know the channel was actually changed to another + if (oldValue() < 0 || oldValue() >= mixer->numChannels()) { return; } + if (value() < 0 || value() >= mixer->numChannels()) { return; } + + --mixer->mixerChannel(oldValue())->m_useCount; + ++mixer->mixerChannel(value())->m_useCount; }); - ++Engine::mixer()->mixerChannel(0)->m_useCount; + ++mixer->mixerChannel(0)->m_useCount; +} + +MixerChannelModel::~MixerChannelModel() +{ + const auto mixer = Engine::mixer(); + if (value() < 0 || value() >= mixer->numChannels()) { return; } + --mixer->mixerChannel(value())->m_useCount; } void MixerChannelModel::channelsSwapped(int fromIndex, int toIndex) From 1675e2f0e69e1ec471c4522992bfba8704165556 Mon Sep 17 00:00:00 2001 From: Sotonye Atemie Date: Fri, 20 Mar 2026 12:43:13 -0400 Subject: [PATCH 09/12] Make use count private --- include/Mixer.h | 7 ++++++- src/core/Mixer.cpp | 2 +- src/core/MixerChannelModel.cpp | 8 ++++---- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/include/Mixer.h b/include/Mixer.h index dc7631090e7..2f766c24735 100644 --- a/include/Mixer.h +++ b/include/Mixer.h @@ -64,7 +64,6 @@ class MixerChannel : public ThreadableJob QMutex m_lock; bool m_queued; // are we queued up for rendering yet? bool m_muted; // are we muted? updated per period so we don't have to call m_muteModel.value() twice - int m_useCount; // pointers to other channels that this one sends to MixerRouteVector m_sends; @@ -89,9 +88,15 @@ class MixerChannel : public ThreadableJob void incrementDeps(); void processed(); + int useCount() const { return m_useCount; } + + void incrementUseCount() { ++m_useCount; } + void decrementUseCount() { --m_useCount; } + private: void doProcessing() override; int m_channelIndex; + int m_useCount; std::optional m_color; }; diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index 2cfd13f47b9..35ee3cbfa3b 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -819,7 +819,7 @@ bool Mixer::isChannelInUse(int index) // check if the index mixer channel receives audio from any other channel if (!m_mixerChannels[index]->m_receives.empty()) { return true; } - return m_mixerChannels[index]->m_useCount > 0; + return m_mixerChannels[index]->useCount() > 0; } diff --git a/src/core/MixerChannelModel.cpp b/src/core/MixerChannelModel.cpp index cf0202e3531..4edda25fe47 100644 --- a/src/core/MixerChannelModel.cpp +++ b/src/core/MixerChannelModel.cpp @@ -43,18 +43,18 @@ MixerChannelModel::MixerChannelModel(Model* parent) if (oldValue() < 0 || oldValue() >= mixer->numChannels()) { return; } if (value() < 0 || value() >= mixer->numChannels()) { return; } - --mixer->mixerChannel(oldValue())->m_useCount; - ++mixer->mixerChannel(value())->m_useCount; + mixer->mixerChannel(oldValue())->decrementUseCount(); + mixer->mixerChannel(value())->incrementUseCount(); }); - ++mixer->mixerChannel(0)->m_useCount; + mixer->mixerChannel(0)->incrementUseCount(); } MixerChannelModel::~MixerChannelModel() { const auto mixer = Engine::mixer(); if (value() < 0 || value() >= mixer->numChannels()) { return; } - --mixer->mixerChannel(value())->m_useCount; + mixer->mixerChannel(value())->decrementUseCount(); } void MixerChannelModel::channelsSwapped(int fromIndex, int toIndex) From bee8620bf9f170208b163d272a664e3bf75f5f43 Mon Sep 17 00:00:00 2001 From: Sotonye Atemie Date: Fri, 20 Mar 2026 12:48:28 -0400 Subject: [PATCH 10/12] Rename MixerChannelModel to MixerChannelLcdModel --- include/InstrumentTrack.h | 4 ++-- ...rChannelModel.h => MixerChannelLcdModel.h} | 20 +++++++++---------- include/SampleTrack.h | 4 ++-- src/core/CMakeLists.txt | 2 +- ...nnelModel.cpp => MixerChannelLcdModel.cpp} | 20 +++++++++---------- 5 files changed, 25 insertions(+), 25 deletions(-) rename include/{MixerChannelModel.h => MixerChannelLcdModel.h} (74%) rename src/core/{MixerChannelModel.cpp => MixerChannelLcdModel.cpp} (74%) diff --git a/include/InstrumentTrack.h b/include/InstrumentTrack.h index 01caaa1809d..0658c389cfd 100644 --- a/include/InstrumentTrack.h +++ b/include/InstrumentTrack.h @@ -33,7 +33,7 @@ #include "Midi.h" #include "MidiEventProcessor.h" #include "MidiPort.h" -#include "MixerChannelModel.h" +#include "MixerChannelLcdModel.h" #include "NotePlayHandle.h" #include "Piano.h" #include "Plugin.h" @@ -294,7 +294,7 @@ protected slots: FloatModel m_pitchModel; IntModel m_pitchRangeModel; - MixerChannelModel m_mixerChannelModel; + MixerChannelLcdModel m_mixerChannelModel; BoolModel m_useMasterPitchModel; Instrument * m_instrument; diff --git a/include/MixerChannelModel.h b/include/MixerChannelLcdModel.h similarity index 74% rename from include/MixerChannelModel.h rename to include/MixerChannelLcdModel.h index e4d6316ad1e..68d9bbb5aa9 100644 --- a/include/MixerChannelModel.h +++ b/include/MixerChannelLcdModel.h @@ -22,8 +22,8 @@ * */ -#ifndef LMMS_MIXER_CHANNEL_MODEL_H -#define LMMS_MIXER_CHANNEL_MODEL_H +#ifndef LMMS_MIXER_CHANNEL_LCD_MODEL_H +#define LMMS_MIXER_CHANNEL_LCD_MODEL_H #include "AutomatableModel.h" @@ -34,16 +34,16 @@ namespace lmms { * This model tracks its assigned mixer channel. It is a subclass of IntModel that adds functionality to handle channel * creation, deletion, and swapping. Both the value and valid range are automatically updated to reflect these changes. */ -class MixerChannelModel : public IntModel +class MixerChannelLcdModel : public IntModel { public: - MixerChannelModel(Model* parent = nullptr); - ~MixerChannelModel(); + MixerChannelLcdModel(Model* parent = nullptr); + ~MixerChannelLcdModel(); - MixerChannelModel(const MixerChannelModel&) = delete; - MixerChannelModel(MixerChannelModel&&) = delete; - MixerChannelModel& operator=(const MixerChannelModel&) = delete; - MixerChannelModel& operator=(MixerChannelModel&&) = delete; + MixerChannelLcdModel(const MixerChannelLcdModel&) = delete; + MixerChannelLcdModel(MixerChannelLcdModel&&) = delete; + MixerChannelLcdModel& operator=(const MixerChannelLcdModel&) = delete; + MixerChannelLcdModel& operator=(MixerChannelLcdModel&&) = delete; private: void channelsSwapped(int fromIndex, int toIndex); @@ -53,4 +53,4 @@ class MixerChannelModel : public IntModel } // namespace lmms -#endif // LMMS_MIXER_CHANNEL_MODEL_H +#endif // LMMS_MIXER_CHANNEL_LCD_MODEL_H diff --git a/include/SampleTrack.h b/include/SampleTrack.h index 9ef7a9aca72..84610f149be 100644 --- a/include/SampleTrack.h +++ b/include/SampleTrack.h @@ -26,7 +26,7 @@ #define LMMS_SAMPLE_TRACK_H #include "AudioBusHandle.h" -#include "MixerChannelModel.h" +#include "MixerChannelLcdModel.h" #include "Track.h" @@ -95,7 +95,7 @@ public slots: private: FloatModel m_volumeModel; FloatModel m_panningModel; - MixerChannelModel m_mixerChannelModel; + MixerChannelLcdModel m_mixerChannelModel; AudioBusHandle m_audioBusHandle; bool m_isPlaying; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 7ae4d7005c4..dae27113c7c 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -45,7 +45,7 @@ set(LMMS_SRCS core/MicroTimer.cpp core/Microtuner.cpp core/MixHelpers.cpp - core/MixerChannelModel.cpp + core/MixerChannelLcdModel.cpp core/Model.cpp core/ModelVisitor.cpp core/Note.cpp diff --git a/src/core/MixerChannelModel.cpp b/src/core/MixerChannelLcdModel.cpp similarity index 74% rename from src/core/MixerChannelModel.cpp rename to src/core/MixerChannelLcdModel.cpp index 4edda25fe47..092dd87382d 100644 --- a/src/core/MixerChannelModel.cpp +++ b/src/core/MixerChannelLcdModel.cpp @@ -22,23 +22,23 @@ * */ -#include "MixerChannelModel.h" +#include "MixerChannelLcdModel.h" #include "Engine.h" #include "Mixer.h" namespace lmms { -MixerChannelModel::MixerChannelModel(Model* parent) +MixerChannelLcdModel::MixerChannelLcdModel(Model* parent) : IntModel(0, 0, 0, parent, tr("Mixer channel")) { const auto mixer = Engine::mixer(); setRange(0, mixer->numChannels() - 1, 1); - connect(mixer, &Mixer::channelsSwapped, this, &MixerChannelModel::channelsSwapped); - connect(mixer, &Mixer::channelDeleted, this, &MixerChannelModel::channelDeleted); - connect(mixer, &Mixer::channelCreated, this, &MixerChannelModel::channelCreated); + connect(mixer, &Mixer::channelsSwapped, this, &MixerChannelLcdModel::channelsSwapped); + connect(mixer, &Mixer::channelDeleted, this, &MixerChannelLcdModel::channelDeleted); + connect(mixer, &Mixer::channelCreated, this, &MixerChannelLcdModel::channelCreated); - connect(this, &MixerChannelModel::dataChanged, mixer, [this, mixer] { + connect(this, &MixerChannelLcdModel::dataChanged, mixer, [this, mixer] { // both channels must exist so we know the channel was actually changed to another if (oldValue() < 0 || oldValue() >= mixer->numChannels()) { return; } if (value() < 0 || value() >= mixer->numChannels()) { return; } @@ -50,27 +50,27 @@ MixerChannelModel::MixerChannelModel(Model* parent) mixer->mixerChannel(0)->incrementUseCount(); } -MixerChannelModel::~MixerChannelModel() +MixerChannelLcdModel::~MixerChannelLcdModel() { const auto mixer = Engine::mixer(); if (value() < 0 || value() >= mixer->numChannels()) { return; } mixer->mixerChannel(value())->decrementUseCount(); } -void MixerChannelModel::channelsSwapped(int fromIndex, int toIndex) +void MixerChannelLcdModel::channelsSwapped(int fromIndex, int toIndex) { if (value() == fromIndex) { setValue(toIndex); } else if (value() == toIndex) { setValue(fromIndex); } } -void MixerChannelModel::channelDeleted(int index) +void MixerChannelLcdModel::channelDeleted(int index) { if (value() == index) { setValue(0); } else if (value() > index) { setValue(value() - 1); } setRange(0, maxValue() - 1); } -void MixerChannelModel::channelCreated(int index) +void MixerChannelLcdModel::channelCreated(int index) { setRange(0, maxValue() + 1); } From 2fbe3225360e6ec903d8032a80bce89fe577c9e6 Mon Sep 17 00:00:00 2001 From: Sotonye Atemie Date: Fri, 20 Mar 2026 12:49:49 -0400 Subject: [PATCH 11/12] Rename MixerChannelModel to MixerChannelLcdModel in copyright --- include/MixerChannelLcdModel.h | 2 +- src/core/MixerChannelLcdModel.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/MixerChannelLcdModel.h b/include/MixerChannelLcdModel.h index 68d9bbb5aa9..4a44d14e409 100644 --- a/include/MixerChannelLcdModel.h +++ b/include/MixerChannelLcdModel.h @@ -1,5 +1,5 @@ /* - * MixerChannelModel.h + * MixerChannelLcdModel.h * * Copyright (c) 2026 saker * diff --git a/src/core/MixerChannelLcdModel.cpp b/src/core/MixerChannelLcdModel.cpp index 092dd87382d..ab5fa021d72 100644 --- a/src/core/MixerChannelLcdModel.cpp +++ b/src/core/MixerChannelLcdModel.cpp @@ -1,5 +1,5 @@ /* - * MixerChannelModel.cpp + * MixerChannelLcdModel.cpp * * Copyright (c) 2026 saker * From ec2c8d840ec971c8d296d521ea166a592242f9f2 Mon Sep 17 00:00:00 2001 From: Sotonye Atemie Date: Sat, 21 Mar 2026 09:02:06 -0400 Subject: [PATCH 12/12] Fix Mixer m_useCount init order --- src/core/Mixer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index 35ee3cbfa3b..020f6810a5d 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -69,9 +69,9 @@ MixerChannel::MixerChannel( int idx, Model * _parent ) : m_name(), m_lock(), m_queued( false ), - m_useCount(0), m_dependenciesMet(0), - m_channelIndex(idx) + m_channelIndex(idx), + m_useCount(0) { m_buffer.allocateInterleavedBuffer(); }