Implemented midi tab widget
This commit is contained in:
@ -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 \
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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/>
|
||||
|
@ -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
115
widgets/miditabwidget.cpp
Normal 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
48
widgets/miditabwidget.h
Normal 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;
|
||||
};
|
Reference in New Issue
Block a user