From 9d6c73782ce54af64587565ec4b62dabfb757398 Mon Sep 17 00:00:00 2001 From: 0xFEEDC0DE64 Date: Wed, 28 Dec 2022 03:02:23 +0100 Subject: [PATCH] Implemented midi tab widget --- DrumMachine.pro | 2 + widgets/drumpadsampleswidget.cpp | 21 ++++-- widgets/drumpadsamplewidget.cpp | 2 + widgets/drumpadwidget.cpp | 2 + widgets/loopstationwidget.cpp | 2 + widgets/mainwindow.cpp | 3 +- widgets/mainwindow.ui | 8 ++- widgets/midibutton.cpp | 20 +++--- widgets/miditabwidget.cpp | 115 +++++++++++++++++++++++++++++++ widgets/miditabwidget.h | 48 +++++++++++++ 10 files changed, 209 insertions(+), 14 deletions(-) create mode 100644 widgets/miditabwidget.cpp create mode 100644 widgets/miditabwidget.h diff --git a/DrumMachine.pro b/DrumMachine.pro index 09cf081..a5804a9 100755 --- a/DrumMachine.pro +++ b/DrumMachine.pro @@ -42,6 +42,7 @@ SOURCES += \ widgets/loopstationwidget.cpp \ widgets/mainwindow.cpp \ widgets/midibutton.cpp \ + widgets/miditabwidget.cpp \ widgets/previewwidget.cpp \ widgets/scratchwidget.cpp \ widgets/sequencerwidget.cpp \ @@ -79,6 +80,7 @@ HEADERS += \ widgets/loopstationwidget.h \ widgets/mainwindow.h \ widgets/midibutton.h \ + widgets/miditabwidget.h \ widgets/previewwidget.h \ widgets/scratchwidget.h \ widgets/sequencerwidget.h \ diff --git a/widgets/drumpadsampleswidget.cpp b/widgets/drumpadsampleswidget.cpp index 58d8d67..2c579a1 100755 --- a/widgets/drumpadsampleswidget.cpp +++ b/widgets/drumpadsampleswidget.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "audioformat.h" #include "midicontainers.h" @@ -50,7 +51,9 @@ void DrumPadSamplesWidget::midiReceived(const midi::MidiMessage &message) return; } - if (message.cmd != midi::Command::NoteOn && message.cmd != midi::Command::NoteOff) + if (message.cmd != midi::Command::NoteOn && + message.cmd != midi::Command::NoteOff && + message.cmd != midi::Command::ControlChange) return; for (DrumPadSampleWidget &widget : getWidgets()) @@ -61,10 +64,20 @@ void DrumPadSamplesWidget::midiReceived(const midi::MidiMessage &message) } else if (widget.channel() == message.channel && widget.note() == message.note) { - if (message.cmd == midi::Command::NoteOff || (message.cmd == midi::Command::NoteOn && message.velocity == 0)) + switch (message.cmd) + { + case midi::Command::NoteOn: + case midi::Command::ControlChange: + if (message.velocity != 0) + widget.pressed(message.velocity); + else + Q_FALLTHROUGH(); + case midi::Command::NoteOff: widget.released(); - else if (message.cmd == midi::Command::NoteOn) - widget.pressed(message.velocity); + break; + default: + __builtin_unreachable(); + } } } } diff --git a/widgets/drumpadsamplewidget.cpp b/widgets/drumpadsamplewidget.cpp index 45662c0..a340a53 100755 --- a/widgets/drumpadsamplewidget.cpp +++ b/widgets/drumpadsamplewidget.cpp @@ -89,6 +89,7 @@ void DrumPadSampleWidget::setChannel(quint8 channel) { m_ui->channelSpinBox->setValue(channel); + Q_ASSERT(m_settings); if (m_settings) m_settings->setDrumpadChannel(m_padNr, channel); else @@ -104,6 +105,7 @@ void DrumPadSampleWidget::setNote(quint8 note) { m_ui->noteSpinBox->setValue(note); + Q_ASSERT(m_settings); if (m_settings) m_settings->setDrumpadNote(m_padNr, note); else diff --git a/widgets/drumpadwidget.cpp b/widgets/drumpadwidget.cpp index 3e27360..ab36095 100644 --- a/widgets/drumpadwidget.cpp +++ b/widgets/drumpadwidget.cpp @@ -133,6 +133,7 @@ void DrumPadWidget::currentRowChanged(const QModelIndex ¤t) { if (!current.isValid()) { + Q_ASSERT(m_settings); if (m_settings) m_settings->setDrumpadLastPresetId(QString{}); else @@ -142,6 +143,7 @@ void DrumPadWidget::currentRowChanged(const QModelIndex ¤t) const auto &preset = m_presetsModel.getPreset(m_presetsProxyModel.mapToSource(current)); + Q_ASSERT(m_settings); if (m_settings) m_settings->setDrumpadLastPresetId(preset.id ? *preset.id : QString{}); else diff --git a/widgets/loopstationwidget.cpp b/widgets/loopstationwidget.cpp index 1815e29..556d782 100644 --- a/widgets/loopstationwidget.cpp +++ b/widgets/loopstationwidget.cpp @@ -116,6 +116,7 @@ void LoopStationWidget::currentRowChanged(const QModelIndex ¤t) { if (!current.isValid()) { + Q_ASSERT(m_settings); if (m_settings) m_settings->setLoopstationLastPresetId(QString{}); else @@ -125,6 +126,7 @@ void LoopStationWidget::currentRowChanged(const QModelIndex ¤t) const auto &preset = m_presetsModel.getPreset(m_presetsProxyModel.mapToSource(current)); + Q_ASSERT(m_settings); if (m_settings) m_settings->setLoopstationLastPresetId(preset.id ? *preset.id : QString{}); else diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 72b647d..e4ecde8 100755 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include @@ -244,6 +243,8 @@ void MainWindow::midiReceived(const midi::MidiMessage &message) .arg(message.flag?"true":"false", QMetaEnum::fromType().valueToKey(int(message.cmd))) .arg(message.channel).arg(message.note).arg(message.velocity), 1000); + m_ui->tabWidget->midiReceived(message); + if (m_ui->tabWidget->currentIndex() == 0) m_ui->drumPadWidget->midiReceived(message); else if (m_ui->tabWidget->currentIndex() == 1) diff --git a/widgets/mainwindow.ui b/widgets/mainwindow.ui index f6f62bd..df0b5b3 100755 --- a/widgets/mainwindow.ui +++ b/widgets/mainwindow.ui @@ -217,7 +217,7 @@ - + DrumPad @@ -279,6 +279,12 @@
widgets/loopstationwidget.h
1 + + MidiTabWidget + QTabWidget +
widgets/miditabwidget.h
+ 1 +
diff --git a/widgets/midibutton.cpp b/widgets/midibutton.cpp index d0653f4..1b012fa 100644 --- a/widgets/midibutton.cpp +++ b/widgets/midibutton.cpp @@ -1,6 +1,7 @@ #include "midibutton.h" #include +#include #include "midicontainers.h" @@ -47,12 +48,14 @@ void MidiButton::learn() void MidiButton::midiReceived(const midi::MidiMessage &message) { - if (message.cmd != midi::Command::NoteOn && message.cmd != midi::Command::NoteOff) + if (message.cmd != midi::Command::NoteOn && + message.cmd != midi::Command::NoteOff && + message.cmd != midi::Command::ControlChange) return; if (m_learning) { - if (message.cmd != midi::Command::NoteOn) + if ((message.cmd != midi::Command::NoteOn && message.cmd != midi::Command::ControlChange) || message.velocity == 0) return; setChannel(message.channel); @@ -67,15 +70,16 @@ void MidiButton::midiReceived(const midi::MidiMessage &message) switch (message.cmd) { case midi::Command::NoteOn: - if (message.velocity == 0) - emit released(); - else + case midi::Command::ControlChange: + if (message.velocity != 0) emit pressed(); - return; + else + Q_FALLTHROUGH(); case midi::Command::NoteOff: emit released(); - return; - default: __builtin_unreachable(); + break; + default: + __builtin_unreachable(); } } } diff --git a/widgets/miditabwidget.cpp b/widgets/miditabwidget.cpp new file mode 100644 index 0000000..ba90c27 --- /dev/null +++ b/widgets/miditabwidget.cpp @@ -0,0 +1,115 @@ +#include "miditabwidget.h" + +#include +#include +#include +#include + +#include "midicontainers.h" + +MidiTabWidget::MidiTabWidget(QWidget *parent) : + QTabWidget{parent} +{ + tabBar()->setContextMenuPolicy(Qt::CustomContextMenu); + connect(tabBar(), &QWidget::customContextMenuRequested, this, &MidiTabWidget::showContextMenu); +} + +quint8 MidiTabWidget::channel(int index) const +{ + return m_channelNotes[index].channel; +} + +void MidiTabWidget::setChannel(int index, quint8 channel) +{ + auto &channelNote = m_channelNotes[index]; + if (channelNote.channel == channel) + return; + emit channelChanged(index, channelNote.channel = channel); +} + +quint8 MidiTabWidget::note(int index) const +{ + return m_channelNotes[index].note; +} + +void MidiTabWidget::setNote(int index, quint8 note) +{ + auto &channelNote = m_channelNotes[index]; + if (channelNote.note == note) + return; + emit noteChanged(index, channelNote.note = note); +} + +void MidiTabWidget::learn(int index) +{ + if (m_learning) + { + tabBar()->setTabTextColor(*m_learning, m_oldColor); + + if (*m_learning == index) + m_learning = std::nullopt; + else + goto startLearn; + } + else + { +startLearn: + m_oldColor = tabBar()->tabTextColor(index); + tabBar()->setTabTextColor(index, Qt::red); + m_learning = index; + } +} + +void MidiTabWidget::midiReceived(const midi::MidiMessage &message) +{ + if ((message.cmd != midi::Command::NoteOn && message.cmd != midi::Command::ControlChange) || message.velocity == 0) + return; + + if (m_learning) + { + qDebug() << "learning" << message.cmd << message.velocity << message.channel << message.note; + setChannel(*m_learning, message.channel); + setNote(*m_learning, message.note); + learn(*m_learning); + } + else + { + qDebug() << "normal" << message.cmd << message.velocity << message.channel << message.note; + for (int i = 0; i < count(); i++) + { + if (message.channel != m_channelNotes[i].channel || + message.note != m_channelNotes[i].note) + continue; + + setCurrentIndex(i); + return; + } + } +} + +void MidiTabWidget::tabInserted(int index) +{ + QTabWidget::tabInserted(index); + m_channelNotes.insert(std::begin(m_channelNotes) + index, ChannelNote{.channel=quint8(index)}); +} + +void MidiTabWidget::tabRemoved(int index) +{ + QTabWidget::tabInserted(index); + m_channelNotes.erase(std::begin(m_channelNotes) + index); +} + +void MidiTabWidget::showContextMenu(const QPoint &pos) +{ + const auto index = tabBar()->tabAt(pos); + if (index < 0) + return; + + QMenu menu{tabBar()}; + const auto learnAction = menu.addAction(tr("Learn...")); + if (const auto selectedAction = menu.exec(tabBar()->mapToGlobal(pos)); + selectedAction == learnAction) + { + learn(index); + } +} diff --git a/widgets/miditabwidget.h b/widgets/miditabwidget.h new file mode 100644 index 0000000..6e6b5e2 --- /dev/null +++ b/widgets/miditabwidget.h @@ -0,0 +1,48 @@ +#pragma once + +#include + +#include +#include + +namespace midi { struct MidiMessage; } + +class MidiTabWidget : public QTabWidget +{ + Q_OBJECT + +public: + explicit MidiTabWidget(QWidget *parent = nullptr); + + quint8 channel(int index) const; + void setChannel(int index, quint8 channel); + + quint8 note(int index) const; + void setNote(int index, quint8 note); + +signals: + void channelChanged(int index, quint8 channel); + void noteChanged(int index, quint8 note); + +public slots: + void learn(int index); + void midiReceived(const midi::MidiMessage &message); + +protected: + void tabInserted(int index) override; + void tabRemoved(int index) override; + +private slots: + void showContextMenu(const QPoint &pos); + +private: + std::optional m_learning; + QColor m_oldColor; + + struct ChannelNote + { + quint8 channel{99}; + quint8 note{99}; + }; + std::vector m_channelNotes; +};