Implemented midi tab widget

This commit is contained in:
2022-12-28 03:02:23 +01:00
parent f80240847b
commit 9d6c73782c
10 changed files with 209 additions and 14 deletions

View File

@ -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 \

View File

@ -4,6 +4,7 @@
#include <iterator>
#include <QDebug>
#include <QtGlobal>
#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();
}
}
}
}

View File

@ -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

View File

@ -133,6 +133,7 @@ void DrumPadWidget::currentRowChanged(const QModelIndex &current)
{
if (!current.isValid())
{
Q_ASSERT(m_settings);
if (m_settings)
m_settings->setDrumpadLastPresetId(QString{});
else
@ -142,6 +143,7 @@ void DrumPadWidget::currentRowChanged(const QModelIndex &current)
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

View File

@ -116,6 +116,7 @@ void LoopStationWidget::currentRowChanged(const QModelIndex &current)
{
if (!current.isValid())
{
Q_ASSERT(m_settings);
if (m_settings)
m_settings->setLoopstationLastPresetId(QString{});
else
@ -125,6 +126,7 @@ void LoopStationWidget::currentRowChanged(const QModelIndex &current)
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

View File

@ -4,7 +4,6 @@
#include <QEventLoop>
#include <QMetaEnum>
#include <QMessageBox>
#include <QTimer>
#include <QAbstractEventDispatcher>
#include <QAudioDeviceInfo>
#include <QDebug>
@ -244,6 +243,8 @@ void MainWindow::midiReceived(const midi::MidiMessage &message)
.arg(message.flag?"true":"false", QMetaEnum::fromType<midi::Command>().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)

View File

@ -217,7 +217,7 @@
</layout>
</item>
<item>
<widget class="QTabWidget" name="tabWidget">
<widget class="MidiTabWidget" name="tabWidget">
<widget class="DrumPadWidget" name="drumPadWidget">
<attribute name="title">
<string>DrumPad</string>
@ -279,6 +279,12 @@
<header>widgets/loopstationwidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>MidiTabWidget</class>
<extends>QTabWidget</extends>
<header>widgets/miditabwidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>

View File

@ -1,6 +1,7 @@
#include "midibutton.h"
#include <QAction>
#include <QtGlobal>
#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();
}
}
}

115
widgets/miditabwidget.cpp Normal file
View File

@ -0,0 +1,115 @@
#include "miditabwidget.h"
#include <QTabBar>
#include <QMenu>
#include <QAction>
#include <QDebug>
#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);
}
}

48
widgets/miditabwidget.h Normal file
View File

@ -0,0 +1,48 @@
#pragma once
#include <QTabWidget>
#include <optional>
#include <vector>
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<int> m_learning;
QColor m_oldColor;
struct ChannelNote
{
quint8 channel{99};
quint8 note{99};
};
std::vector<ChannelNote> m_channelNotes;
};