From 626d27653a017f81aea3774b14b1e71ab98740ba Mon Sep 17 00:00:00 2001 From: 0xFEEDC0DE64 Date: Tue, 27 Dec 2022 20:54:38 +0100 Subject: [PATCH] Implemented midi control of most buttons in drumpad tab --- DrumMachine.pro | 2 + drummachinesettings.cpp | 146 ++++++++++++++++++++++++++++++-- drummachinesettings.h | 40 ++++++++- midiinwrapper.cpp | 4 +- midiinwrapper.h | 4 +- midioutwrapper.cpp | 2 + midioutwrapper.h | 4 +- presetsmodel.cpp | 13 +++ presetsmodel.h | 2 + synthisizer.h | 5 -- widgets/djwidget.cpp | 3 +- widgets/djwidget.h | 2 +- widgets/drumpadwidget.cpp | 152 ++++++++++++++++++++++++++++++---- widgets/drumpadwidget.h | 12 ++- widgets/drumpadwidget.ui | 50 +++++++++-- widgets/mainwindow.cpp | 15 ++-- widgets/mainwindow.h | 2 +- widgets/midibutton.cpp | 81 ++++++++++++++++++ widgets/midibutton.h | 39 +++++++++ widgets/sampleswidget.cpp | 2 +- widgets/sampleswidget.h | 4 +- widgets/samplewidget.cpp | 14 ++-- widgets/samplewidget.h | 2 +- widgets/samplewidget.ui | 9 +- widgets/sequencerwidget.cpp | 120 ++++++++++++++++++++++++++- widgets/sequencerwidget.h | 17 +++- widgets/sequencerwidget.ui | 143 ++++++++++++++++++++------------ widgets/synthisizerwidget.cpp | 3 +- widgets/synthisizerwidget.h | 3 +- widgets/trackdeck.cpp | 1 - 30 files changed, 763 insertions(+), 133 deletions(-) create mode 100644 widgets/midibutton.cpp create mode 100644 widgets/midibutton.h diff --git a/DrumMachine.pro b/DrumMachine.pro index 24671ed..6ca849f 100755 --- a/DrumMachine.pro +++ b/DrumMachine.pro @@ -29,6 +29,7 @@ SOURCES += \ widgets/djwidget.cpp \ widgets/drumpadwidget.cpp \ widgets/mainwindow.cpp \ + widgets/midibutton.cpp \ widgets/presetdetailwidget.cpp \ widgets/previewwidget.cpp \ widgets/sampleswidget.cpp \ @@ -56,6 +57,7 @@ HEADERS += \ widgets/djwidget.h \ widgets/drumpadwidget.h \ widgets/mainwindow.h \ + widgets/midibutton.h \ widgets/presetdetailwidget.h \ widgets/previewwidget.h \ widgets/sampleswidget.h \ diff --git a/drummachinesettings.cpp b/drummachinesettings.cpp index 1e8dd83..4ed2cf8 100644 --- a/drummachinesettings.cpp +++ b/drummachinesettings.cpp @@ -40,22 +40,152 @@ void DrumMachineSettings::setLastMidiOutDevice(const QString &lastMidiOutDevice) setValue("lastMidiOutDevice", lastMidiOutDevice); } -quint8 DrumMachineSettings::padChannel(quint8 pad) const +QString DrumMachineSettings::drumpadLastPresetId() const { - return value(QString{"pad%0/channel"}.arg(pad)).toUInt(); + return value("drumpad/lastPresetId").toString(); } -void DrumMachineSettings::setPadChannel(quint8 pad, quint8 channel) +void DrumMachineSettings::setDrumpadLastPresetId(const QString &lastPresetId) { - setValue(QString{"pad%0/channel"}.arg(pad), channel); + setValue("drumpad/lastPresetId", lastPresetId); } -quint8 DrumMachineSettings::padNote(quint8 pad) const +quint8 DrumMachineSettings::drumpadChannelPrevPreset() const { - return value(QString{"pad%0/note"}.arg(pad)).toUInt(); + return value("drumpad/prevPreset_channel", 99).toUInt(); } -void DrumMachineSettings::setPadNote(quint8 pad, quint8 note) +void DrumMachineSettings::setDrumpadChannelPrevPreset(quint8 channel) { - setValue(QString{"pad%0/note"}.arg(pad), note); + setValue("drumpad/prevPreset_channel", channel); +} + +quint8 DrumMachineSettings::drumpadNotePrevPreset() const +{ + return value("drumpad/prevPreset_note", 99).toUInt(); +} + +void DrumMachineSettings::setDrumpadNotePrevPreset(quint8 note) +{ + setValue("drumpad/prevPreset_note", note); +} + +quint8 DrumMachineSettings::drumpadChannelNextPreset() const +{ + return value("drumpad/nextPreset_channel", 99).toUInt(); +} + +void DrumMachineSettings::setDrumpadChannelNextPreset(quint8 channel) +{ + setValue("drumpad/nextPreset_channel", channel); +} + +quint8 DrumMachineSettings::drumpadNoteNextPreset() const +{ + return value("drumpad/nextPreset_note", 99).toUInt(); +} + +void DrumMachineSettings::setDrumpadNoteNextPreset(quint8 note) +{ + setValue("drumpad/nextPreset_note", note); +} + +quint8 DrumMachineSettings::drumpadChannelPrevSequence() const +{ + return value("drumpad/prevSequence_channel", 99).toUInt(); +} + +void DrumMachineSettings::setDrumpadChannelPrevSequence(quint8 channel) +{ + setValue("drumpad/prevSequence_channel", channel); +} + +quint8 DrumMachineSettings::drumpadNotePrevSequence() const +{ + return value("drumpad/prevSequence_note", 99).toUInt(); +} + +void DrumMachineSettings::setDrumpadNotePrevSequence(quint8 note) +{ + setValue("drumpad/prevSequence_note", note); +} + +quint8 DrumMachineSettings::drumpadChannelNextSequence() const +{ + return value("drumpad/nextSequence_channel", 99).toUInt(); +} + +void DrumMachineSettings::setDrumpadChannelNextSequence(quint8 channel) +{ + setValue("drumpad/nextSequence_channel", channel); +} + +quint8 DrumMachineSettings::drumpadNoteNextSequence() const +{ + return value("drumpad/nextSequence_note", 99).toUInt(); +} + +void DrumMachineSettings::setDrumpadNoteNextSequence(quint8 note) +{ + setValue("drumpad/nextSequence_note", note); +} + +quint8 DrumMachineSettings::drumpadChannelPlayPause() const +{ + return value("drumpad/playPause_channel", 99).toUInt(); +} + +void DrumMachineSettings::setDrumpadChannelPlayPause(quint8 channel) +{ + setValue("drumpad/playPause_channel", channel); +} + +quint8 DrumMachineSettings::drumpadNotePlayPause() const +{ + return value("drumpad/playPause_note", 99).toUInt(); +} + +void DrumMachineSettings::setDrumpadNotePlayPause(quint8 note) +{ + setValue("drumpad/playPause_note", note); +} + +quint8 DrumMachineSettings::drumpadChannelStop() const +{ + return value("drumpad/stop_channel", 99).toUInt(); +} + +void DrumMachineSettings::setDrumpadChannelStop(quint8 channel) +{ + setValue("drumpad/stop_channel", channel); +} + +quint8 DrumMachineSettings::drumpadNoteStop() const +{ + return value("drumpad/stop_note", 99).toUInt(); +} + +void DrumMachineSettings::setDrumpadNoteStop(quint8 note) +{ + setValue("drumpad/stop_note", note); +} + +quint8 DrumMachineSettings::drumpadChannel(quint8 pad) const +{ + return value(QString{"drumpad/pad%0_channel"}.arg(pad), 99).toUInt(); +} + +void DrumMachineSettings::setDrumpadChannel(quint8 pad, quint8 channel) +{ + setValue(QString{"drumpad/pad%0_channel"}.arg(pad), channel); +} + +quint8 DrumMachineSettings::drumpadNote(quint8 pad) const +{ + return value(QString{"drumpad/pad%0_note"}.arg(pad), 99).toUInt(); +} + +void DrumMachineSettings::setDrumpadNote(quint8 pad, quint8 note) +{ + setValue(QString{"drumpad/pad%0_note"}.arg(pad), note); } diff --git a/drummachinesettings.h b/drummachinesettings.h index e4f82ef..c915832 100644 --- a/drummachinesettings.h +++ b/drummachinesettings.h @@ -19,9 +19,41 @@ public: QString lastMidiOutDevice() const; void setLastMidiOutDevice(const QString &lastMidiOutDevice); - quint8 padChannel(quint8 pad) const; - void setPadChannel(quint8 pad, quint8 channel); + QString drumpadLastPresetId() const; + void setDrumpadLastPresetId(const QString &lastPresetId); - quint8 padNote(quint8 pad) const; - void setPadNote(quint8 pad, quint8 note); + quint8 drumpadChannelPrevPreset() const; + void setDrumpadChannelPrevPreset(quint8 channel); + quint8 drumpadNotePrevPreset() const; + void setDrumpadNotePrevPreset(quint8 note); + + quint8 drumpadChannelNextPreset() const; + void setDrumpadChannelNextPreset(quint8 channel); + quint8 drumpadNoteNextPreset() const; + void setDrumpadNoteNextPreset(quint8 note); + + quint8 drumpadChannelPrevSequence() const; + void setDrumpadChannelPrevSequence(quint8 channel); + quint8 drumpadNotePrevSequence() const; + void setDrumpadNotePrevSequence(quint8 note); + + quint8 drumpadChannelNextSequence() const; + void setDrumpadChannelNextSequence(quint8 channel); + quint8 drumpadNoteNextSequence() const; + void setDrumpadNoteNextSequence(quint8 note); + + quint8 drumpadChannelPlayPause() const; + void setDrumpadChannelPlayPause(quint8 channel); + quint8 drumpadNotePlayPause() const; + void setDrumpadNotePlayPause(quint8 note); + + quint8 drumpadChannelStop() const; + void setDrumpadChannelStop(quint8 channel); + quint8 drumpadNoteStop() const; + void setDrumpadNoteStop(quint8 note); + + quint8 drumpadChannel(quint8 pad) const; + void setDrumpadChannel(quint8 pad, quint8 channel); + quint8 drumpadNote(quint8 pad) const; + void setDrumpadNote(quint8 pad, quint8 note); }; diff --git a/midiinwrapper.cpp b/midiinwrapper.cpp index 204c6e9..5b646dd 100755 --- a/midiinwrapper.cpp +++ b/midiinwrapper.cpp @@ -4,6 +4,8 @@ #include #include +#include "midicontainers.h" + MidiInWrapper::MidiInWrapper(RtMidi::Api api, const QString& clientName, unsigned int queueSizeLimit, QObject *parent) : QObject{parent}, midiIn{api, clientName.toStdString(), queueSizeLimit} @@ -72,5 +74,5 @@ void MidiInWrapper::mycallback(double deltatime, std::vector *mes } const midi::MidiMessage &midiMessage = reinterpret_cast(message->at(0)); - wrapper->messageReceived(midiMessage); + wrapper->midiReceived(midiMessage); } diff --git a/midiinwrapper.h b/midiinwrapper.h index 6f19f34..b4f7a71 100755 --- a/midiinwrapper.h +++ b/midiinwrapper.h @@ -5,7 +5,7 @@ #include "rtmidi/RtMidi.h" -#include "midicontainers.h" +namespace midi { struct MidiMessage; } class MidiInWrapper : public QObject { @@ -25,7 +25,7 @@ public: QStringList portNames(); signals: - void messageReceived(const midi::MidiMessage &message); + void midiReceived(const midi::MidiMessage &message); private: static void mycallback(double deltatime, std::vector *message, void *userData); diff --git a/midioutwrapper.cpp b/midioutwrapper.cpp index b3166d1..53d5904 100644 --- a/midioutwrapper.cpp +++ b/midioutwrapper.cpp @@ -2,6 +2,8 @@ #include +#include "midicontainers.h" + MidiOutWrapper::MidiOutWrapper(RtMidi::Api api, const QString &clientName) : midiOut{api, clientName.toStdString()} { diff --git a/midioutwrapper.h b/midioutwrapper.h index c43cafb..99ce7ee 100644 --- a/midioutwrapper.h +++ b/midioutwrapper.h @@ -1,8 +1,10 @@ #pragma once +#include + #include "rtmidi/RtMidi.h" -#include "midicontainers.h" +namespace midi { struct MidiMessage; } class MidiOutWrapper { diff --git a/presetsmodel.cpp b/presetsmodel.cpp index d710351..0917f66 100755 --- a/presetsmodel.cpp +++ b/presetsmodel.cpp @@ -94,6 +94,19 @@ const presets::Preset &PresetsModel::getPreset(int row) const return m_presets.at(row); } +QModelIndex PresetsModel::findPresetById(const QString &id) const +{ + for (auto iter = std::cbegin(m_presets); iter != std::cend(m_presets); iter++) + { + if (iter->id != id) + continue; + + return createIndex(std::distance(std::cbegin(m_presets), iter), 0); + } + + return {}; +} + int PresetsModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) diff --git a/presetsmodel.h b/presetsmodel.h index 8778dfc..053ebe8 100755 --- a/presetsmodel.h +++ b/presetsmodel.h @@ -24,6 +24,8 @@ public: const presets::Preset &getPreset(const QModelIndex &index) const; const presets::Preset &getPreset(int row) const; + QModelIndex findPresetById(const QString &id) const; + int rowCount(const QModelIndex &parent) const override; int columnCount(const QModelIndex &parent) const override; QVariant data(const QModelIndex &index, int role) const override; diff --git a/synthisizer.h b/synthisizer.h index 1bd4eb0..ee1de09 100644 --- a/synthisizer.h +++ b/synthisizer.h @@ -4,8 +4,6 @@ #include "audioformat.h" -namespace midi { class MidiMessage; } - class DrumMachineSettings; class Synthisizer : public QObject @@ -19,9 +17,6 @@ public: void writeSamples(frame_t *begin, frame_t *end); -signals: - void sendMidi(const midi::MidiMessage &midiMsg); - private: float m_volume{1.f}; int16_t m_frequency{}; diff --git a/widgets/djwidget.cpp b/widgets/djwidget.cpp index f75e52f..820ffcc 100644 --- a/widgets/djwidget.cpp +++ b/widgets/djwidget.cpp @@ -1,7 +1,6 @@ #include "djwidget.h" #include "ui_djwidget.h" -#include #include DjWidget::DjWidget(QWidget *parent) : @@ -87,7 +86,7 @@ void DjWidget::sendColors() { } -void DjWidget::messageReceived(const midi::MidiMessage &message) +void DjWidget::midiReceived(const midi::MidiMessage &message) { } diff --git a/widgets/djwidget.h b/widgets/djwidget.h index 3497458..dd2a443 100644 --- a/widgets/djwidget.h +++ b/widgets/djwidget.h @@ -31,7 +31,7 @@ signals: void sendMidi(const midi::MidiMessage &midiMsg); public slots: - void messageReceived(const midi::MidiMessage &message); + void midiReceived(const midi::MidiMessage &message); private slots: void directorySelected(); diff --git a/widgets/drumpadwidget.cpp b/widgets/drumpadwidget.cpp index e90eefd..8f789b2 100644 --- a/widgets/drumpadwidget.cpp +++ b/widgets/drumpadwidget.cpp @@ -6,7 +6,9 @@ #include #include +#include "midicontainers.h" #include "jsonconverters.h" +#include "drummachinesettings.h" DrumPadWidget::DrumPadWidget(QWidget *parent) : QSplitter{parent}, @@ -14,6 +16,10 @@ DrumPadWidget::DrumPadWidget(QWidget *parent) : { m_ui->setupUi(this); + connect(m_ui->pushButtonUp, &QAbstractButton::pressed, this, &DrumPadWidget::selectPrevPreset); + connect(m_ui->pushButtonDown, &QAbstractButton::pressed, this, &DrumPadWidget::selectNextPreset); + + connect(m_ui->sequencerWidget, &SequencerWidget::sendMidi, this, &DrumPadWidget::sendMidi); connect(m_ui->samplesWidget, &SamplesWidget::sendMidi, this, &DrumPadWidget::sendMidi); connect(m_ui->sequencerWidget, &SequencerWidget::triggerSample, m_ui->samplesWidget, &SamplesWidget::sequencerTriggerSample); @@ -36,19 +42,6 @@ DrumPadWidget::DrumPadWidget(QWidget *parent) : DrumPadWidget::~DrumPadWidget() = default; -void DrumPadWidget::selectFirstPreset() -{ - if (!m_presetsProxyModel.rowCount()) - return; - - const auto index = m_presetsProxyModel.index(0, 0); - if (index.isValid()) - { - m_ui->presetsView->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); - currentRowChanged(index); - } -} - void DrumPadWidget::writeSamples(frame_t *begin, frame_t *end) { m_ui->samplesWidget->writeSamples(begin, end); @@ -68,22 +61,71 @@ void DrumPadWidget::injectDecodingThread(QThread &thread) void DrumPadWidget::loadSettings(DrumMachineSettings &settings) { + m_settings = &settings; + + m_ui->pushButtonUp->setChannel(m_settings->drumpadChannelPrevPreset()); + m_ui->pushButtonUp->setNote(m_settings->drumpadNotePrevPreset()); + m_ui->pushButtonDown->setChannel(m_settings->drumpadChannelNextPreset()); + m_ui->pushButtonDown->setNote(m_settings->drumpadNoteNextPreset()); + + connect(m_ui->pushButtonUp, &MidiButton::channelChanged, m_settings, &DrumMachineSettings::setDrumpadChannelPrevPreset); + connect(m_ui->pushButtonUp, &MidiButton::noteChanged, m_settings, &DrumMachineSettings::setDrumpadNotePrevPreset); + connect(m_ui->pushButtonDown, &MidiButton::channelChanged, m_settings, &DrumMachineSettings::setDrumpadChannelNextPreset); + connect(m_ui->pushButtonDown, &MidiButton::noteChanged, m_settings, &DrumMachineSettings::setDrumpadNoteNextPreset); + + m_ui->sequencerWidget->loadSettings(settings); m_ui->samplesWidget->loadSettings(settings); } void DrumPadWidget::unsendColors() { + emit sendMidi(midi::MidiMessage { + .channel = m_ui->pushButtonUp->channel(), + .cmd = midi::Command::NoteOn, + .flag = true, + .note = m_ui->pushButtonUp->note(), + .velocity = 0 + }); + emit sendMidi(midi::MidiMessage { + .channel = m_ui->pushButtonDown->channel(), + .cmd = midi::Command::NoteOn, + .flag = true, + .note = m_ui->pushButtonDown->note(), + .velocity = 0 + }); + + m_ui->sequencerWidget->unsendColors(); m_ui->samplesWidget->unsendColors(); } void DrumPadWidget::sendColors() { + emit sendMidi(midi::MidiMessage { + .channel = m_ui->pushButtonUp->channel(), + .cmd = midi::Command::NoteOn, + .flag = true, + .note = m_ui->pushButtonUp->note(), + .velocity = 127 + }); + emit sendMidi(midi::MidiMessage { + .channel = m_ui->pushButtonDown->channel(), + .cmd = midi::Command::NoteOn, + .flag = true, + .note = m_ui->pushButtonDown->note(), + .velocity = 127 + }); + + m_ui->sequencerWidget->sendColors(); m_ui->samplesWidget->sendColors(); } -void DrumPadWidget::messageReceived(const midi::MidiMessage &message) +void DrumPadWidget::midiReceived(const midi::MidiMessage &message) { - m_ui->samplesWidget->messageReceived(message); + m_ui->pushButtonUp->midiReceived(message); + m_ui->pushButtonDown->midiReceived(message); + + m_ui->sequencerWidget->midiReceived(message); + m_ui->samplesWidget->midiReceived(message); } void DrumPadWidget::currentRowChanged(const QModelIndex ¤t) @@ -93,6 +135,11 @@ void DrumPadWidget::currentRowChanged(const QModelIndex ¤t) const auto &preset = m_presetsModel.getPreset(m_presetsProxyModel.mapToSource(current)); + if (m_settings) + m_settings->setDrumpadLastPresetId(preset.id ? *preset.id : QString{}); + else + qWarning() << "no settings available"; + m_ui->presetDetailWidget->setPreset(preset); m_filesModel.setPreset(preset); m_ui->sequencerWidget->setPreset(preset); @@ -141,10 +188,83 @@ void DrumPadWidget::requestFinished() m_presetsModel.setPresets(std::move(*std::move(result).presets)); - selectFirstPreset(); + if (m_settings) + { + if (const auto &lastPresetId = m_settings->drumpadLastPresetId(); !lastPresetId.isEmpty()) + { + if (const auto &index = m_presetsModel.findPresetById(lastPresetId); index.isValid()) + selectIndex(m_presetsProxyModel.mapFromSource(index)); + else + { + qWarning() << "invalid last preset id" << lastPresetId; + goto noLastId; + } + } + else + goto noLastId; + } + else + { +noLastId: + selectFirstPreset(); + } } catch (const std::exception &e) { QMessageBox::warning(this, tr("error"), tr("error") + "\n\n" + QString::fromStdString(e.what())); } } + +void DrumPadWidget::selectFirstPreset() +{ + if (!m_presetsProxyModel.rowCount()) + return; + + selectIndex(m_presetsProxyModel.index(0, 0)); +} + +void DrumPadWidget::selectPrevPreset() +{ + if (!m_presetsProxyModel.rowCount()) + return; + + const auto index = m_ui->presetsView->selectionModel()->currentIndex(); + if (!index.isValid()) + { + qWarning() << "invalid index"; + return; + } + + if (index.row() > 0) + selectIndex(m_presetsProxyModel.index(index.row() - 1, 0)); +} + +void DrumPadWidget::selectNextPreset() +{ + if (!m_presetsProxyModel.rowCount()) + return; + + const auto index = m_ui->presetsView->selectionModel()->currentIndex(); + if (!index.isValid()) + { + qWarning() << "invalid index"; + return; + } + + if (index.row() + 1 < m_presetsProxyModel.rowCount()) + selectIndex(m_presetsProxyModel.index(index.row() + 1, 0)); +} + +void DrumPadWidget::selectIndex(const QModelIndex &index) +{ + if (!index.isValid()) + { + qWarning() << "invalid index"; + return; + } + + m_ui->presetsView->scrollTo(index); +// m_ui->presetsView->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + m_ui->presetsView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + currentRowChanged(index); +} diff --git a/widgets/drumpadwidget.h b/widgets/drumpadwidget.h index c46e6ef..4122d8c 100644 --- a/widgets/drumpadwidget.h +++ b/widgets/drumpadwidget.h @@ -6,7 +6,6 @@ #include #include "audioformat.h" -#include "midicontainers.h" #include "presetsmodel.h" #include "filesmodel.h" @@ -18,6 +17,7 @@ class QNetworkAccessManager; class QThread; class DrumMachineSettings; class QNetworkReply; +namespace midi { struct MidiMessage; } class DrumPadWidget : public QSplitter { @@ -27,7 +27,6 @@ public: explicit DrumPadWidget(QWidget *parent = nullptr); ~DrumPadWidget() override; - void selectFirstPreset(); void writeSamples(frame_t *begin, frame_t *end); void injectNetworkAccessManager(QNetworkAccessManager &networkAccessManager); void injectDecodingThread(QThread &thread); @@ -39,16 +38,23 @@ signals: void sendMidi(const midi::MidiMessage &midiMsg); public slots: - void messageReceived(const midi::MidiMessage &message); + void midiReceived(const midi::MidiMessage &message); private slots: void currentRowChanged(const QModelIndex ¤t); void loadPresets(); void requestFinished(); + void selectFirstPreset(); + void selectPrevPreset(); + void selectNextPreset(); private: + void selectIndex(const QModelIndex &index); + const std::unique_ptr m_ui; + DrumMachineSettings *m_settings{}; + PresetsModel m_presetsModel; QSortFilterProxyModel m_presetsProxyModel; diff --git a/widgets/drumpadwidget.ui b/widgets/drumpadwidget.ui index dedb445..df14754 100644 --- a/widgets/drumpadwidget.ui +++ b/widgets/drumpadwidget.ui @@ -1,7 +1,7 @@ DrumPadWidget - + 0 @@ -15,6 +15,9 @@ + + 9 + @@ -26,6 +29,32 @@ + + + + + 32 + 16777215 + + + + + + + + + + + + 32 + 16777215 + + + + + + + @@ -56,7 +85,7 @@ - 2 + 0 @@ -75,12 +104,12 @@ - - - Qt::Vertical - - - + + + Qt::Vertical + + + @@ -102,6 +131,11 @@
widgets/sequencerwidget.h
1 + + MidiButton + QPushButton +
widgets/midibutton.h
+
diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index c26a8f2..4843c8b 100755 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -9,11 +9,8 @@ #include #include -#include "presets.h" #include "midiinwrapper.h" #include "midicontainers.h" -#include "sampleswidget.h" -#include "sequencerwidget.h" namespace { void DummyDeleter(PaStream *stream); @@ -42,7 +39,7 @@ MainWindow::MainWindow(QWidget *parent) : m_ui->drumPadWidget->injectNetworkAccessManager(m_networkAccessManager); - connect(&m_midiIn, &MidiInWrapper::messageReceived, this, &MainWindow::messageReceived); + connect(&m_midiIn, &MidiInWrapper::midiReceived, this, &MainWindow::midiReceived); { QEventLoop eventLoop; @@ -223,18 +220,18 @@ void MainWindow::openMidiOutDevice() m_ui->pushButtonMidiOut->setText(m_midiOut.isPortOpen() ? tr("Close") : tr("Open")); } -void MainWindow::messageReceived(const midi::MidiMessage &message) +void MainWindow::midiReceived(const midi::MidiMessage &message) { m_ui->statusbar->showMessage(tr("Received midi message: flag: %0 cmd: %1 channel: %2 note: %3 velocity: %4") .arg(message.flag?"true":"false", QMetaEnum::fromType().valueToKey(int(message.cmd))) .arg(message.channel).arg(message.note).arg(message.velocity), 1000); if (m_ui->tabWidget->currentIndex() == 0) - m_ui->drumPadWidget->messageReceived(message); + m_ui->drumPadWidget->midiReceived(message); else if (m_ui->tabWidget->currentIndex() == 1) - m_ui->djWidget->messageReceived(message); + m_ui->djWidget->midiReceived(message); else if (m_ui->tabWidget->currentIndex() == 2) - m_ui->synthisizerWidget->messageReceived(message); + m_ui->synthisizerWidget->midiReceived(message); } void MainWindow::sendMidi(const midi::MidiMessage &midiMsg) @@ -316,7 +313,6 @@ void MainWindow::sendColors(int index) int k{0}; for (int j = 0; j < 128; j+= 16) { - qDebug() << k; for (auto i = 0; i < 8; i++) { midi::MidiMessage midiMsg; @@ -349,6 +345,7 @@ void PaStreamStopperAndCloser(PaStream *stream) void paStreamFinished(void* userData) { + Q_UNUSED(userData) printf("Stream Completed\n"); } diff --git a/widgets/mainwindow.h b/widgets/mainwindow.h index daa49b6..9e80b9e 100755 --- a/widgets/mainwindow.h +++ b/widgets/mainwindow.h @@ -32,7 +32,7 @@ private slots: void openAudioDevice(); void openMidiInDevice(); void openMidiOutDevice(); - void messageReceived(const midi::MidiMessage &message); + void midiReceived(const midi::MidiMessage &message); void sendMidi(const midi::MidiMessage &midiMsg); void currentChanged(int index); diff --git a/widgets/midibutton.cpp b/widgets/midibutton.cpp new file mode 100644 index 0000000..d0653f4 --- /dev/null +++ b/widgets/midibutton.cpp @@ -0,0 +1,81 @@ +#include "midibutton.h" + +#include + +#include "midicontainers.h" + +MidiButton::MidiButton(QWidget *parent) : + QPushButton{parent} +{ + setContextMenuPolicy(Qt::ActionsContextMenu); + + auto action = new QAction{tr("Learn...")}; + connect(action, &QAction::triggered, this, &MidiButton::learn); + addAction(action); +} + +MidiButton::MidiButton(const QString &text, QWidget *parent) : + QPushButton{text, parent} +{ +} + +MidiButton::MidiButton(const QIcon &icon, const QString &text, QWidget *parent) : + QPushButton{icon, text, parent} +{ +} + +void MidiButton::learn() +{ + auto palette = this->palette(); + + if (m_learning) + { + palette.setColor(backgroundRole(), m_oldColor); + palette.setBrush(backgroundRole(), m_oldBrush); + } + else + { + m_oldColor = palette.color(backgroundRole()); + m_oldBrush = palette.brush(backgroundRole()); + palette.setColor(backgroundRole(), Qt::red); + palette.setBrush(backgroundRole(), Qt::red); + } + setPalette(palette); + + m_learning = !m_learning; +} + +void MidiButton::midiReceived(const midi::MidiMessage &message) +{ + if (message.cmd != midi::Command::NoteOn && message.cmd != midi::Command::NoteOff) + return; + + if (m_learning) + { + if (message.cmd != midi::Command::NoteOn) + return; + + setChannel(message.channel); + setNote(message.note); + learn(); + } + else + { + if (message.channel != m_channel || message.note != m_note) + return; + + switch (message.cmd) + { + case midi::Command::NoteOn: + if (message.velocity == 0) + emit released(); + else + emit pressed(); + return; + case midi::Command::NoteOff: + emit released(); + return; + default: __builtin_unreachable(); + } + } +} diff --git a/widgets/midibutton.h b/widgets/midibutton.h new file mode 100644 index 0000000..f919071 --- /dev/null +++ b/widgets/midibutton.h @@ -0,0 +1,39 @@ +#pragma once + +#include + +namespace midi { struct MidiMessage; } + +class MidiButton : public QPushButton +{ + Q_OBJECT + Q_PROPERTY(quint8 channel READ channel WRITE setChannel NOTIFY channelChanged) + Q_PROPERTY(quint8 note READ note WRITE setNote NOTIFY noteChanged) + +public: + explicit MidiButton(QWidget *parent = nullptr); + explicit MidiButton(const QString &text, QWidget *parent = nullptr); + MidiButton(const QIcon& icon, const QString &text, QWidget *parent = nullptr); + + quint8 channel() const { return m_channel; } + void setChannel(quint8 channel) { if (channel == m_channel) return; emit channelChanged(m_channel = channel); } + + quint8 note() const { return m_note; } + void setNote(quint8 note) { if (note == m_note) return; emit noteChanged(m_note = note); } + +signals: + void channelChanged(quint8 channel); + void noteChanged(quint8 note); + +public slots: + void learn(); + void midiReceived(const midi::MidiMessage &message); + +private: + quint8 m_channel{99}; + quint8 m_note{99}; + + bool m_learning{}; + QColor m_oldColor; + QBrush m_oldBrush; +}; diff --git a/widgets/sampleswidget.cpp b/widgets/sampleswidget.cpp index 245a5d7..0950f3a 100755 --- a/widgets/sampleswidget.cpp +++ b/widgets/sampleswidget.cpp @@ -41,7 +41,7 @@ void SamplesWidget::setPreset(const presets::Preset &preset) updateWidgets(); } -void SamplesWidget::messageReceived(const midi::MidiMessage &message) +void SamplesWidget::midiReceived(const midi::MidiMessage &message) { if (message == midi::MidiMessage{.channel=0,.cmd=midi::Command::ControlChange,.flag=true,.note=64,.velocity=127}) { diff --git a/widgets/sampleswidget.h b/widgets/sampleswidget.h index 8917cb2..837e9fd 100755 --- a/widgets/sampleswidget.h +++ b/widgets/sampleswidget.h @@ -10,7 +10,7 @@ #include "presets.h" namespace Ui { class SamplesWidget; } -namespace midi { class MidiMessage; } +namespace midi { struct MidiMessage; } class QNetworkAccessManager; class SampleWidget; class DrumMachineSettings; @@ -27,7 +27,7 @@ public: void setPreset(const presets::Preset &preset); - void messageReceived(const midi::MidiMessage &message); + void midiReceived(const midi::MidiMessage &message); void writeSamples(frame_t *begin, frame_t *end); diff --git a/widgets/samplewidget.cpp b/widgets/samplewidget.cpp index 6efe5e7..7793447 100755 --- a/widgets/samplewidget.cpp +++ b/widgets/samplewidget.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include "audiodecoder.h" #include "drummachinesettings.h" @@ -41,8 +40,8 @@ SampleWidget::~SampleWidget() = default; void SampleWidget::loadSettings(DrumMachineSettings &settings) { - m_ui->channelSpinBox->setValue(settings.padChannel(m_padNr)); - m_ui->noteSpinBox->setValue(settings.padNote(m_padNr)); + m_ui->channelSpinBox->setValue(settings.drumpadChannel(m_padNr)); + m_ui->noteSpinBox->setValue(settings.drumpadNote(m_padNr)); m_settings = &settings; } @@ -90,7 +89,9 @@ void SampleWidget::setChannel(quint8 channel) m_ui->channelSpinBox->setValue(channel); if (m_settings) - m_settings->setPadChannel(m_padNr, channel); + m_settings->setDrumpadChannel(m_padNr, channel); + else + qWarning() << "no settings available"; } quint8 SampleWidget::note() const @@ -103,7 +104,9 @@ void SampleWidget::setNote(quint8 note) m_ui->noteSpinBox->setValue(note); if (m_settings) - m_settings->setPadNote(m_padNr, note); + m_settings->setDrumpadNote(m_padNr, note); + else + qWarning() << "no settings available"; } int SampleWidget::speed() const @@ -322,7 +325,6 @@ void SampleWidget::learnPressed() m_ui->toolButtonLearn->setPalette(palette); m_learning = !m_learning; - qDebug() << m_learning; } void SampleWidget::startRequest() diff --git a/widgets/samplewidget.h b/widgets/samplewidget.h index a617eff..1eca2f1 100755 --- a/widgets/samplewidget.h +++ b/widgets/samplewidget.h @@ -7,7 +7,6 @@ #include "audioformat.h" #include "presets.h" #include "audioplayer.h" -#include "midicontainers.h" namespace Ui { class SampleWidget; } class QNetworkAccessManager; @@ -15,6 +14,7 @@ class QNetworkReply; class QAudioBuffer; class AudioDecoder; class DrumMachineSettings; +namespace midi { struct MidiMessage; } class SampleWidget : public QFrame { diff --git a/widgets/samplewidget.ui b/widgets/samplewidget.ui index 0ff0027..3ee777f 100755 --- a/widgets/samplewidget.ui +++ b/widgets/samplewidget.ui @@ -62,7 +62,7 @@ - + 32 @@ -151,6 +151,13 @@
+ + + MidiButton + QPushButton +
widgets/midibutton.h
+
+
diff --git a/widgets/sequencerwidget.cpp b/widgets/sequencerwidget.cpp index 1a18794..2eb0c67 100755 --- a/widgets/sequencerwidget.cpp +++ b/widgets/sequencerwidget.cpp @@ -6,6 +6,8 @@ #include #include "presets.h" +#include "drummachinesettings.h" +#include "midicontainers.h" SequencerWidget::SequencerWidget(QWidget *parent) : QWidget{parent}, @@ -14,12 +16,17 @@ SequencerWidget::SequencerWidget(QWidget *parent) : m_ui->setupUi(this); connect(m_ui->spinBoxTempo, qOverload(&QSpinBox::valueChanged), this, &SequencerWidget::tempoChanged); - connect(m_ui->comboBoxSequence, qOverload(&QComboBox::currentIndexChanged), this, &SequencerWidget::sequenceSelected); - connect(m_ui->horizontalSlider, &QSlider::valueChanged, this, [=](int value){ m_pos = value; updateStatusLabel(); }); + connect(m_ui->comboBoxSequence, qOverload(&QComboBox::currentIndexChanged), this, &SequencerWidget::sequenceSelected); + + connect(m_ui->pushButtonUp, &QAbstractButton::pressed, this, &SequencerWidget::selectPrevSequence); + connect(m_ui->pushButtonDown, &QAbstractButton::pressed, this, &SequencerWidget::selectNextSequence); connect(m_ui->pushButtonPlayPause, &QAbstractButton::pressed, this, &SequencerWidget::playPause); connect(m_ui->pushButtonStop, &QAbstractButton::pressed, this, &SequencerWidget::stop); + connect(m_ui->horizontalSlider, &QSlider::valueChanged, this, [=](int value){ m_pos = value; updateStatusLabel(); }); + + connect(&m_timer, &QTimer::timeout, this, &SequencerWidget::timeout); updateStatusLabel(); @@ -27,6 +34,95 @@ SequencerWidget::SequencerWidget(QWidget *parent) : SequencerWidget::~SequencerWidget() = default; +void SequencerWidget::loadSettings(DrumMachineSettings &settings) +{ + m_ui->pushButtonUp->setChannel(settings.drumpadChannelPrevSequence()); + m_ui->pushButtonUp->setNote(settings.drumpadNotePrevSequence()); + m_ui->pushButtonDown->setChannel(settings.drumpadChannelNextSequence()); + m_ui->pushButtonDown->setNote(settings.drumpadNoteNextSequence()); + m_ui->pushButtonPlayPause->setChannel(settings.drumpadChannelPlayPause()); + m_ui->pushButtonPlayPause->setNote(settings.drumpadNotePlayPause()); + m_ui->pushButtonStop->setChannel(settings.drumpadChannelStop()); + m_ui->pushButtonStop->setNote(settings.drumpadNoteStop()); + + connect(m_ui->pushButtonUp, &MidiButton::channelChanged, &settings, &DrumMachineSettings::setDrumpadChannelPrevSequence); + connect(m_ui->pushButtonUp, &MidiButton::noteChanged, &settings, &DrumMachineSettings::setDrumpadNotePrevSequence); + connect(m_ui->pushButtonDown, &MidiButton::channelChanged, &settings, &DrumMachineSettings::setDrumpadChannelNextSequence); + connect(m_ui->pushButtonDown, &MidiButton::noteChanged, &settings, &DrumMachineSettings::setDrumpadNoteNextSequence); + connect(m_ui->pushButtonPlayPause, &MidiButton::channelChanged, &settings, &DrumMachineSettings::setDrumpadChannelPlayPause); + connect(m_ui->pushButtonPlayPause, &MidiButton::noteChanged, &settings, &DrumMachineSettings::setDrumpadNotePlayPause); + connect(m_ui->pushButtonStop, &MidiButton::channelChanged, &settings, &DrumMachineSettings::setDrumpadChannelStop); + connect(m_ui->pushButtonStop, &MidiButton::noteChanged, &settings, &DrumMachineSettings::setDrumpadNoteStop); +} + +void SequencerWidget::unsendColors() +{ + m_sendColors = false; + + emit sendMidi(midi::MidiMessage { + .channel = m_ui->pushButtonUp->channel(), + .cmd = midi::Command::NoteOn, + .flag = true, + .note = m_ui->pushButtonUp->note(), + .velocity = 0 + }); + emit sendMidi(midi::MidiMessage { + .channel = m_ui->pushButtonDown->channel(), + .cmd = midi::Command::NoteOn, + .flag = true, + .note = m_ui->pushButtonDown->note(), + .velocity = 0 + }); + emit sendMidi(midi::MidiMessage { + .channel = m_ui->pushButtonPlayPause->channel(), + .cmd = midi::Command::NoteOn, + .flag = true, + .note = m_ui->pushButtonPlayPause->note(), + .velocity = 0 + }); + emit sendMidi(midi::MidiMessage { + .channel = m_ui->pushButtonStop->channel(), + .cmd = midi::Command::NoteOn, + .flag = true, + .note = m_ui->pushButtonStop->note(), + .velocity = 0 + }); +} + +void SequencerWidget::sendColors() +{ + m_sendColors = true; + + emit sendMidi(midi::MidiMessage { + .channel = m_ui->pushButtonUp->channel(), + .cmd = midi::Command::NoteOn, + .flag = true, + .note = m_ui->pushButtonUp->note(), + .velocity = 127 + }); + emit sendMidi(midi::MidiMessage { + .channel = m_ui->pushButtonDown->channel(), + .cmd = midi::Command::NoteOn, + .flag = true, + .note = m_ui->pushButtonDown->note(), + .velocity = 127 + }); + emit sendMidi(midi::MidiMessage { + .channel = m_ui->pushButtonPlayPause->channel(), + .cmd = midi::Command::NoteOn, + .flag = true, + .note = m_ui->pushButtonPlayPause->note(), + .velocity = 60 + }); + emit sendMidi(midi::MidiMessage { + .channel = m_ui->pushButtonStop->channel(), + .cmd = midi::Command::NoteOn, + .flag = true, + .note = m_ui->pushButtonStop->note(), + .velocity = 3 + }); +} + void SequencerWidget::setPreset(const presets::Preset &preset) { if (preset.tempo) @@ -66,6 +162,14 @@ void SequencerWidget::setPreset(const presets::Preset &preset) sequenceSelected(); } +void SequencerWidget::midiReceived(const midi::MidiMessage &message) +{ + m_ui->pushButtonUp->midiReceived(message); + m_ui->pushButtonDown->midiReceived(message); + m_ui->pushButtonPlayPause->midiReceived(message); + m_ui->pushButtonStop->midiReceived(message); +} + void SequencerWidget::playPause() { if (m_timer.isActive()) @@ -151,3 +255,15 @@ void SequencerWidget::updateStatusLabel() { m_ui->labelStatus->setText(QString{"%0 / %1"}.arg(m_pos+1).arg(m_selectedSequence && m_selectedSequence->sequencerSize ? *m_selectedSequence->sequencerSize-1 : -1)); } + +void SequencerWidget::selectPrevSequence() +{ + if (const auto index = m_ui->comboBoxSequence->currentIndex(); index > 0) + m_ui->comboBoxSequence->setCurrentIndex(index - 1); +} + +void SequencerWidget::selectNextSequence() +{ + if (const auto index = m_ui->comboBoxSequence->currentIndex(); index + 1 < m_ui->comboBoxSequence->count()) + m_ui->comboBoxSequence->setCurrentIndex(index + 1); +} diff --git a/widgets/sequencerwidget.h b/widgets/sequencerwidget.h index 4614578..6239be1 100755 --- a/widgets/sequencerwidget.h +++ b/widgets/sequencerwidget.h @@ -7,9 +7,11 @@ #include #include +class QLabel; namespace Ui { class SequencerWidget; } namespace presets { class Preset; class Sequence; } -class QLabel; +class DrumMachineSettings; +namespace midi { struct MidiMessage; } class SequencerWidget : public QWidget { @@ -19,11 +21,19 @@ public: explicit SequencerWidget(QWidget *parent = nullptr); ~SequencerWidget() override; + void loadSettings(DrumMachineSettings &settings); + void unsendColors(); + void sendColors(); + void setPreset(const presets::Preset &preset); signals: + void sendMidi(const midi::MidiMessage &midiMsg); void triggerSample(int index); +public slots: + void midiReceived(const midi::MidiMessage &message); + private slots: void playPause(); void stop(); @@ -34,6 +44,9 @@ private slots: void updateStatusLabel(); + void selectPrevSequence(); + void selectNextSequence(); + private: const std::unique_ptr m_ui; @@ -44,4 +57,6 @@ private: int m_pos{}; std::array m_sampleLabels; + + bool m_sendColors{}; }; diff --git a/widgets/sequencerwidget.ui b/widgets/sequencerwidget.ui index 420c010..9a8da7d 100755 --- a/widgets/sequencerwidget.ui +++ b/widgets/sequencerwidget.ui @@ -15,7 +15,7 @@ - + @@ -30,7 +30,33 @@ - + + + + 32 + 16777215 + + + + + + + + + + + + 32 + 16777215 + + + + + + + + + 32 @@ -43,7 +69,7 @@ - + 32 @@ -82,26 +108,26 @@ 0 0 878 - 921 + 828 - + - + TextLabel - + TextLabel - + QFrame::HLine @@ -111,14 +137,14 @@ - + TextLabel - + QFrame::HLine @@ -128,14 +154,14 @@ - + TextLabel - + QFrame::HLine @@ -145,7 +171,7 @@ - + QFrame::HLine @@ -155,7 +181,7 @@ - + QFrame::HLine @@ -165,14 +191,14 @@ - + TextLabel - + QFrame::HLine @@ -182,7 +208,7 @@ - + QFrame::HLine @@ -192,21 +218,21 @@ - + TextLabel - + TextLabel - + QFrame::HLine @@ -216,14 +242,14 @@ - + TextLabel - + QFrame::HLine @@ -233,28 +259,28 @@ - + TextLabel - + TextLabel - + TextLabel - + QFrame::HLine @@ -264,7 +290,7 @@ - + QFrame::HLine @@ -274,35 +300,35 @@ - + TextLabel - + TextLabel - + TextLabel - + TextLabel - + QFrame::HLine @@ -312,14 +338,14 @@ - + TextLabel - + QFrame::HLine @@ -329,7 +355,7 @@ - + QFrame::HLine @@ -339,7 +365,7 @@ - + QFrame::HLine @@ -349,7 +375,7 @@ - + QFrame::HLine @@ -359,7 +385,7 @@ - + QFrame::HLine @@ -369,7 +395,7 @@ - + QFrame::HLine @@ -379,14 +405,14 @@ - + TextLabel - + QFrame::HLine @@ -396,7 +422,7 @@ - + QFrame::HLine @@ -406,14 +432,14 @@ - + TextLabel - + QFrame::HLine @@ -423,14 +449,14 @@ - + TextLabel - + QFrame::VLine @@ -440,21 +466,21 @@ - + TextLabel - + TextLabel - + QFrame::HLine @@ -464,14 +490,14 @@ - + TextLabel - + QFrame::HLine @@ -481,14 +507,14 @@ - + TextLabel - + QFrame::HLine @@ -498,14 +524,14 @@ - + TextLabel - + TextLabel @@ -517,6 +543,13 @@ + + + MidiButton + QPushButton +
widgets/midibutton.h
+
+
diff --git a/widgets/synthisizerwidget.cpp b/widgets/synthisizerwidget.cpp index b2a42cf..fc9aaf3 100644 --- a/widgets/synthisizerwidget.cpp +++ b/widgets/synthisizerwidget.cpp @@ -24,6 +24,7 @@ void SynthisizerWidget::writeSamples(frame_t *begin, frame_t *end) void SynthisizerWidget::loadSettings(DrumMachineSettings &settings) { + Q_UNUSED(settings) } void SynthisizerWidget::unsendColors() @@ -34,7 +35,7 @@ void SynthisizerWidget::sendColors() { } -void SynthisizerWidget::messageReceived(const midi::MidiMessage &message) +void SynthisizerWidget::midiReceived(const midi::MidiMessage &message) { if (message.cmd == midi::Command::NoteOff || (message.cmd == midi::Command::NoteOn && message.velocity == 0)) { diff --git a/widgets/synthisizerwidget.h b/widgets/synthisizerwidget.h index 4042e9c..fc32e29 100644 --- a/widgets/synthisizerwidget.h +++ b/widgets/synthisizerwidget.h @@ -8,6 +8,7 @@ namespace Ui { class SynthisizerWidget; } class DrumMachineSettings; +namespace midi { struct MidiMessage; } class SynthisizerWidget : public QWidget { @@ -26,7 +27,7 @@ signals: void sendMidi(const midi::MidiMessage &midiMsg); public slots: - void messageReceived(const midi::MidiMessage &message); + void midiReceived(const midi::MidiMessage &message); private: const std::unique_ptr m_ui; diff --git a/widgets/trackdeck.cpp b/widgets/trackdeck.cpp index 58d334d..65290cb 100644 --- a/widgets/trackdeck.cpp +++ b/widgets/trackdeck.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include "audiodecoder.h"