Implemented midi tab widget
This commit is contained in:
@ -42,6 +42,7 @@ SOURCES += \
|
|||||||
widgets/loopstationwidget.cpp \
|
widgets/loopstationwidget.cpp \
|
||||||
widgets/mainwindow.cpp \
|
widgets/mainwindow.cpp \
|
||||||
widgets/midibutton.cpp \
|
widgets/midibutton.cpp \
|
||||||
|
widgets/miditabwidget.cpp \
|
||||||
widgets/previewwidget.cpp \
|
widgets/previewwidget.cpp \
|
||||||
widgets/scratchwidget.cpp \
|
widgets/scratchwidget.cpp \
|
||||||
widgets/sequencerwidget.cpp \
|
widgets/sequencerwidget.cpp \
|
||||||
@ -79,6 +80,7 @@ HEADERS += \
|
|||||||
widgets/loopstationwidget.h \
|
widgets/loopstationwidget.h \
|
||||||
widgets/mainwindow.h \
|
widgets/mainwindow.h \
|
||||||
widgets/midibutton.h \
|
widgets/midibutton.h \
|
||||||
|
widgets/miditabwidget.h \
|
||||||
widgets/previewwidget.h \
|
widgets/previewwidget.h \
|
||||||
widgets/scratchwidget.h \
|
widgets/scratchwidget.h \
|
||||||
widgets/sequencerwidget.h \
|
widgets/sequencerwidget.h \
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
#include "audioformat.h"
|
#include "audioformat.h"
|
||||||
#include "midicontainers.h"
|
#include "midicontainers.h"
|
||||||
@ -50,7 +51,9 @@ void DrumPadSamplesWidget::midiReceived(const midi::MidiMessage &message)
|
|||||||
return;
|
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;
|
return;
|
||||||
|
|
||||||
for (DrumPadSampleWidget &widget : getWidgets())
|
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)
|
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();
|
widget.released();
|
||||||
else if (message.cmd == midi::Command::NoteOn)
|
break;
|
||||||
widget.pressed(message.velocity);
|
default:
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,6 +89,7 @@ void DrumPadSampleWidget::setChannel(quint8 channel)
|
|||||||
{
|
{
|
||||||
m_ui->channelSpinBox->setValue(channel);
|
m_ui->channelSpinBox->setValue(channel);
|
||||||
|
|
||||||
|
Q_ASSERT(m_settings);
|
||||||
if (m_settings)
|
if (m_settings)
|
||||||
m_settings->setDrumpadChannel(m_padNr, channel);
|
m_settings->setDrumpadChannel(m_padNr, channel);
|
||||||
else
|
else
|
||||||
@ -104,6 +105,7 @@ void DrumPadSampleWidget::setNote(quint8 note)
|
|||||||
{
|
{
|
||||||
m_ui->noteSpinBox->setValue(note);
|
m_ui->noteSpinBox->setValue(note);
|
||||||
|
|
||||||
|
Q_ASSERT(m_settings);
|
||||||
if (m_settings)
|
if (m_settings)
|
||||||
m_settings->setDrumpadNote(m_padNr, note);
|
m_settings->setDrumpadNote(m_padNr, note);
|
||||||
else
|
else
|
||||||
|
@ -133,6 +133,7 @@ void DrumPadWidget::currentRowChanged(const QModelIndex ¤t)
|
|||||||
{
|
{
|
||||||
if (!current.isValid())
|
if (!current.isValid())
|
||||||
{
|
{
|
||||||
|
Q_ASSERT(m_settings);
|
||||||
if (m_settings)
|
if (m_settings)
|
||||||
m_settings->setDrumpadLastPresetId(QString{});
|
m_settings->setDrumpadLastPresetId(QString{});
|
||||||
else
|
else
|
||||||
@ -142,6 +143,7 @@ void DrumPadWidget::currentRowChanged(const QModelIndex ¤t)
|
|||||||
|
|
||||||
const auto &preset = m_presetsModel.getPreset(m_presetsProxyModel.mapToSource(current));
|
const auto &preset = m_presetsModel.getPreset(m_presetsProxyModel.mapToSource(current));
|
||||||
|
|
||||||
|
Q_ASSERT(m_settings);
|
||||||
if (m_settings)
|
if (m_settings)
|
||||||
m_settings->setDrumpadLastPresetId(preset.id ? *preset.id : QString{});
|
m_settings->setDrumpadLastPresetId(preset.id ? *preset.id : QString{});
|
||||||
else
|
else
|
||||||
|
@ -116,6 +116,7 @@ void LoopStationWidget::currentRowChanged(const QModelIndex ¤t)
|
|||||||
{
|
{
|
||||||
if (!current.isValid())
|
if (!current.isValid())
|
||||||
{
|
{
|
||||||
|
Q_ASSERT(m_settings);
|
||||||
if (m_settings)
|
if (m_settings)
|
||||||
m_settings->setLoopstationLastPresetId(QString{});
|
m_settings->setLoopstationLastPresetId(QString{});
|
||||||
else
|
else
|
||||||
@ -125,6 +126,7 @@ void LoopStationWidget::currentRowChanged(const QModelIndex ¤t)
|
|||||||
|
|
||||||
const auto &preset = m_presetsModel.getPreset(m_presetsProxyModel.mapToSource(current));
|
const auto &preset = m_presetsModel.getPreset(m_presetsProxyModel.mapToSource(current));
|
||||||
|
|
||||||
|
Q_ASSERT(m_settings);
|
||||||
if (m_settings)
|
if (m_settings)
|
||||||
m_settings->setLoopstationLastPresetId(preset.id ? *preset.id : QString{});
|
m_settings->setLoopstationLastPresetId(preset.id ? *preset.id : QString{});
|
||||||
else
|
else
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
#include <QEventLoop>
|
#include <QEventLoop>
|
||||||
#include <QMetaEnum>
|
#include <QMetaEnum>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QTimer>
|
|
||||||
#include <QAbstractEventDispatcher>
|
#include <QAbstractEventDispatcher>
|
||||||
#include <QAudioDeviceInfo>
|
#include <QAudioDeviceInfo>
|
||||||
#include <QDebug>
|
#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.flag?"true":"false", QMetaEnum::fromType<midi::Command>().valueToKey(int(message.cmd)))
|
||||||
.arg(message.channel).arg(message.note).arg(message.velocity), 1000);
|
.arg(message.channel).arg(message.note).arg(message.velocity), 1000);
|
||||||
|
|
||||||
|
m_ui->tabWidget->midiReceived(message);
|
||||||
|
|
||||||
if (m_ui->tabWidget->currentIndex() == 0)
|
if (m_ui->tabWidget->currentIndex() == 0)
|
||||||
m_ui->drumPadWidget->midiReceived(message);
|
m_ui->drumPadWidget->midiReceived(message);
|
||||||
else if (m_ui->tabWidget->currentIndex() == 1)
|
else if (m_ui->tabWidget->currentIndex() == 1)
|
||||||
|
@ -217,7 +217,7 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTabWidget" name="tabWidget">
|
<widget class="MidiTabWidget" name="tabWidget">
|
||||||
<widget class="DrumPadWidget" name="drumPadWidget">
|
<widget class="DrumPadWidget" name="drumPadWidget">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>DrumPad</string>
|
<string>DrumPad</string>
|
||||||
@ -279,6 +279,12 @@
|
|||||||
<header>widgets/loopstationwidget.h</header>
|
<header>widgets/loopstationwidget.h</header>
|
||||||
<container>1</container>
|
<container>1</container>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>MidiTabWidget</class>
|
||||||
|
<extends>QTabWidget</extends>
|
||||||
|
<header>widgets/miditabwidget.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "midibutton.h"
|
#include "midibutton.h"
|
||||||
|
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
#include "midicontainers.h"
|
#include "midicontainers.h"
|
||||||
|
|
||||||
@ -47,12 +48,14 @@ void MidiButton::learn()
|
|||||||
|
|
||||||
void MidiButton::midiReceived(const midi::MidiMessage &message)
|
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;
|
return;
|
||||||
|
|
||||||
if (m_learning)
|
if (m_learning)
|
||||||
{
|
{
|
||||||
if (message.cmd != midi::Command::NoteOn)
|
if ((message.cmd != midi::Command::NoteOn && message.cmd != midi::Command::ControlChange) || message.velocity == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
setChannel(message.channel);
|
setChannel(message.channel);
|
||||||
@ -67,15 +70,16 @@ void MidiButton::midiReceived(const midi::MidiMessage &message)
|
|||||||
switch (message.cmd)
|
switch (message.cmd)
|
||||||
{
|
{
|
||||||
case midi::Command::NoteOn:
|
case midi::Command::NoteOn:
|
||||||
if (message.velocity == 0)
|
case midi::Command::ControlChange:
|
||||||
emit released();
|
if (message.velocity != 0)
|
||||||
else
|
|
||||||
emit pressed();
|
emit pressed();
|
||||||
return;
|
else
|
||||||
|
Q_FALLTHROUGH();
|
||||||
case midi::Command::NoteOff:
|
case midi::Command::NoteOff:
|
||||||
emit released();
|
emit released();
|
||||||
return;
|
break;
|
||||||
default: __builtin_unreachable();
|
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