Implemented midi output for feedback on color pads

This commit is contained in:
2022-12-17 21:56:23 +01:00
parent 413be074f4
commit 68d420cf41
12 changed files with 281 additions and 23 deletions

View File

@ -23,6 +23,7 @@ SOURCES += \
mainwindow.cpp \ mainwindow.cpp \
midicontainers.cpp \ midicontainers.cpp \
midiinwrapper.cpp \ midiinwrapper.cpp \
midioutwrapper.cpp \
presetdetailwidget.cpp \ presetdetailwidget.cpp \
presets.cpp \ presets.cpp \
presetsmodel.cpp \ presetsmodel.cpp \
@ -47,6 +48,7 @@ HEADERS += \
mainwindow.h \ mainwindow.h \
midicontainers.h \ midicontainers.h \
midiinwrapper.h \ midiinwrapper.h \
midioutwrapper.h \
presetdetailwidget.h \ presetdetailwidget.h \
presets.h \ presets.h \
presetsmodel.h \ presetsmodel.h \

View File

@ -14,7 +14,10 @@
#include "midicontainers.h" #include "midicontainers.h"
namespace { namespace {
void DummyDeleter(PaStream *stream) {} void DummyDeleter(PaStream *stream)
{
Q_UNUSED(stream);
}
void PaStreamCloser(PaStream *stream) void PaStreamCloser(PaStream *stream)
{ {
if (PaError err = Pa_CloseStream(stream); err != paNoError) if (PaError err = Pa_CloseStream(stream); err != paNoError)
@ -74,22 +77,46 @@ MainWindow::MainWindow(const presets::PresetsConfig &presetsConfig, QWidget *par
connect(m_ui->sequencerWidget, &SequencerWidget::triggerSample, m_ui->samplesWidget, &SamplesWidget::sequencerTriggerSample); connect(m_ui->sequencerWidget, &SequencerWidget::triggerSample, m_ui->samplesWidget, &SamplesWidget::sequencerTriggerSample);
updateMidiDevices(); updateMidiInDevices();
connect(m_ui->pushButtonRefreshMidiControllers, &QAbstractButton::pressed, this, &MainWindow::updateMidiDevices); updateMidiOutDevices();
connect(m_ui->pushButtonMidiController, &QAbstractButton::pressed, this, [this](){ connect(m_ui->pushButtonRefreshMidiIn, &QAbstractButton::pressed, this, &MainWindow::updateMidiInDevices);
connect(m_ui->pushButtonRefreshMidiOut, &QAbstractButton::pressed, this, &MainWindow::updateMidiOutDevices);
connect(m_ui->pushButtonMidiIn, &QAbstractButton::pressed, this, [this](){
if (m_midiIn.isPortOpen()) if (m_midiIn.isPortOpen())
m_midiIn.closePort(); m_midiIn.closePort();
else else
{ {
const auto index = m_ui->comboBoxMidiController->currentIndex(); const auto index = m_ui->comboBoxMidiIn->currentIndex();
if (index != -1) if (index != -1)
m_midiIn.openPort(index); m_midiIn.openPort(index, "DrumMachine");
} }
m_ui->comboBoxMidiController->setDisabled(m_midiIn.isPortOpen()); m_ui->comboBoxMidiIn->setDisabled(m_midiIn.isPortOpen());
m_ui->pushButtonMidiController->setText(m_midiIn.isPortOpen() ? tr("Close") : tr("Open")); m_ui->pushButtonMidiIn->setText(m_midiIn.isPortOpen() ? tr("Close") : tr("Open"));
});
connect(m_ui->pushButtonMidiOut, &QAbstractButton::pressed, this, [this](){
if (m_midiOut.isPortOpen())
{
qDebug() << "closing port";
m_midiOut.closePort();
}
else
{
const auto index = m_ui->comboBoxMidiOut->currentIndex();
if (index != -1)
{
m_midiOut.openPort(index, "DrumMachine");
sendColors();
}
}
m_ui->comboBoxMidiOut->setDisabled(m_midiOut.isPortOpen());
m_ui->pushButtonMidiOut->setText(m_midiOut.isPortOpen() ? tr("Close") : tr("Open"));
}); });
updateAudioDevices(); updateAudioDevices();
@ -114,6 +141,8 @@ MainWindow::MainWindow(const presets::PresetsConfig &presetsConfig, QWidget *par
connect(m_ui->presetsView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &MainWindow::currentRowChanged); connect(m_ui->presetsView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &MainWindow::currentRowChanged);
loadSettings(); loadSettings();
connect(m_ui->samplesWidget, &SamplesWidget::sendMidi, this, &MainWindow::sendMidi);
} }
MainWindow::~MainWindow() MainWindow::~MainWindow()
@ -232,14 +261,30 @@ void MainWindow::currentRowChanged(const QModelIndex &current)
m_ui->samplesWidget->setPreset(preset); m_ui->samplesWidget->setPreset(preset);
} }
void MainWindow::updateMidiDevices() void MainWindow::sendMidi(const midi::MidiMessage &midiMsg)
{ {
m_ui->comboBoxMidiController->clear(); if (m_midiOut.isPortOpen())
m_midiOut.sendMessage(midiMsg);
}
void MainWindow::updateMidiInDevices()
{
m_ui->comboBoxMidiIn->clear();
for (const auto &name : m_midiIn.portNames()) for (const auto &name : m_midiIn.portNames())
m_ui->comboBoxMidiController->addItem(name); m_ui->comboBoxMidiIn->addItem(name);
m_ui->pushButtonMidiController->setEnabled(m_ui->comboBoxMidiController->count() > 0); m_ui->pushButtonMidiIn->setEnabled(m_ui->comboBoxMidiIn->count() > 0);
}
void MainWindow::updateMidiOutDevices()
{
m_ui->comboBoxMidiOut->clear();
for (const auto &name : m_midiOut.portNames())
m_ui->comboBoxMidiOut->addItem(name);
m_ui->pushButtonMidiOut->setEnabled(m_ui->comboBoxMidiOut->count() > 0);
} }
void MainWindow::updateAudioDevices() void MainWindow::updateAudioDevices()
@ -260,3 +305,25 @@ void MainWindow::loadSettings()
m_synthisizer.loadSettings(m_settings); m_synthisizer.loadSettings(m_settings);
m_ui->samplesWidget->loadSettings(m_settings); m_ui->samplesWidget->loadSettings(m_settings);
} }
void MainWindow::sendColors()
{
m_ui->samplesWidget->sendColors();
return;
int k{0};
for (int j = 0; j < 128; j+= 16)
{
qDebug() << k;
for (auto i = 0; i < 8; i++)
{
midi::MidiMessage midiMsg;
midiMsg.channel = 0;
midiMsg.cmd = midi::Command::NoteOn;
midiMsg.flag = true;
midiMsg.note = i + j;
midiMsg.velocity = k++;
sendMidi(midiMsg);
}
}
}

View File

@ -12,6 +12,7 @@
#include "presetsmodel.h" #include "presetsmodel.h"
#include "filesmodel.h" #include "filesmodel.h"
#include "midiinwrapper.h" #include "midiinwrapper.h"
#include "midioutwrapper.h"
#include "synthisizer.h" #include "synthisizer.h"
#include "drummachinesettings.h" #include "drummachinesettings.h"
@ -35,11 +36,14 @@ private slots:
void openAudioDevice(); void openAudioDevice();
void messageReceived(const midi::MidiMessage &message); void messageReceived(const midi::MidiMessage &message);
void currentRowChanged(const QModelIndex &current); void currentRowChanged(const QModelIndex &current);
void sendMidi(const midi::MidiMessage &midiMsg);
private: private:
void updateMidiDevices(); void updateMidiInDevices();
void updateMidiOutDevices();
void updateAudioDevices(); void updateAudioDevices();
void loadSettings(); void loadSettings();
void sendColors();
const std::unique_ptr<Ui::MainWindow> m_ui; const std::unique_ptr<Ui::MainWindow> m_ui;
@ -48,6 +52,7 @@ private:
std::unique_ptr<PaStream, void(*)(PaStream*)> m_paStream; std::unique_ptr<PaStream, void(*)(PaStream*)> m_paStream;
MidiInWrapper m_midiIn; MidiInWrapper m_midiIn;
MidiOutWrapper m_midiOut;
QThread m_decoderThread; QThread m_decoderThread;

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1017</width> <width>1520</width>
<height>712</height> <height>890</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -99,10 +99,10 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QComboBox" name="comboBoxMidiController"/> <widget class="QComboBox" name="comboBoxMidiIn"/>
</item> </item>
<item> <item>
<widget class="QPushButton" name="pushButtonRefreshMidiControllers"> <widget class="QPushButton" name="pushButtonRefreshMidiIn">
<property name="maximumSize"> <property name="maximumSize">
<size> <size>
<width>32</width> <width>32</width>
@ -115,7 +115,7 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="pushButtonMidiController"> <widget class="QPushButton" name="pushButtonMidiIn">
<property name="text"> <property name="text">
<string>Open</string> <string>Open</string>
</property> </property>
@ -135,6 +135,49 @@
</item> </item>
</widget> </widget>
</item> </item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="labelMidiOut">
<property name="text">
<string>Midi out:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBoxMidiOut"/>
</item>
<item>
<widget class="QPushButton" name="pushButtonRefreshMidiOut">
<property name="maximumSize">
<size>
<width>32</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>↻</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonMidiOut">
<property name="text">
<string>Open</string>
</property>
</widget>
</item>
<item> <item>
<spacer name="horizontalSpacer"> <spacer name="horizontalSpacer">
<property name="orientation"> <property name="orientation">
@ -278,8 +321,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1017</width> <width>1520</width>
<height>20</height> <height>23</height>
</rect> </rect>
</property> </property>
</widget> </widget>

View File

@ -12,9 +12,12 @@ MidiInWrapper::MidiInWrapper(RtMidi::Api api, const QString& clientName, unsigne
midiIn.setCallback(&mycallback, this); midiIn.setCallback(&mycallback, this);
} }
void MidiInWrapper::openPort(unsigned int portNumber) void MidiInWrapper::openPort(unsigned int portNumber, const QString &portName)
{ {
midiIn.openPort(portNumber); qDebug() << "opening" << QString::fromStdString(midiIn.getPortName(portNumber));
midiIn.openPort(portNumber, portName.toStdString());
if (!midiIn.isPortOpen())
qFatal("port did not open");
} }
void MidiInWrapper::openVirtualPort(const QString &portName) void MidiInWrapper::openVirtualPort(const QString &portName)

View File

@ -17,7 +17,7 @@ public:
unsigned int queueSizeLimit = 100, unsigned int queueSizeLimit = 100,
QObject *parent = nullptr); QObject *parent = nullptr);
void openPort(unsigned int portNumber); void openPort(unsigned int portNumber, const QString &portName);
void openVirtualPort(const QString &portName); void openVirtualPort(const QString &portName);
void closePort(); void closePort();
bool isPortOpen() const; bool isPortOpen() const;

58
midioutwrapper.cpp Normal file
View File

@ -0,0 +1,58 @@
#include "midioutwrapper.h"
#include <QDebug>
MidiOutWrapper::MidiOutWrapper(RtMidi::Api api, const QString &clientName) :
midiOut{api, clientName.toStdString()}
{
}
void MidiOutWrapper::openPort(unsigned int portNumber, const QString &portName)
{
qDebug() << "opening" << QString::fromStdString(midiOut.getPortName(portNumber));
midiOut.openPort(portNumber, portName.toStdString());
if (!midiOut.isPortOpen())
qFatal("port did not open");
}
void MidiOutWrapper::openVirtualPort(const QString &portName)
{
midiOut.openVirtualPort(portName.toStdString());
}
void MidiOutWrapper::closePort()
{
midiOut.closePort();
}
bool MidiOutWrapper::isPortOpen() const
{
return midiOut.isPortOpen();
}
QStringList MidiOutWrapper::portNames()
{
QStringList names;
const auto count = midiOut.getPortCount();
for (unsigned int i = 0; i < count; i++)
names.append(QString::fromStdString(midiOut.getPortName(i)));
return names;
}
void MidiOutWrapper::sendMessage(const midi::MidiMessage &midiMsg)
{
union Helper {
Helper() {}
midi::MidiMessage midiMsg;
unsigned char buf[sizeof(midiMsg)];
};
Helper helper;
helper.midiMsg = midiMsg;
midiOut.sendMessage(helper.buf, sizeof(helper.buf));
}

24
midioutwrapper.h Normal file
View File

@ -0,0 +1,24 @@
#pragma once
#include "rtmidi/RtMidi.h"
#include "midicontainers.h"
class MidiOutWrapper
{
public:
MidiOutWrapper(RtMidi::Api api = RtMidi::UNSPECIFIED,
const QString &clientName = "RtMidi Input Client");
void openPort(unsigned int portNumber, const QString &portName);
void openVirtualPort(const QString &portName);
void closePort();
bool isPortOpen() const;
QStringList portNames();
void sendMessage(const midi::MidiMessage &midiMsg);
private:
RtMidiOut midiOut;
};

View File

@ -26,6 +26,7 @@ SamplesWidget::SamplesWidget(QWidget *parent) :
widget.setPadNr(padNr++); widget.setPadNr(padNr++);
widget.injectNetworkAccessManager(m_networkAccessManager); widget.injectNetworkAccessManager(m_networkAccessManager);
connect(&widget, &SampleWidget::chokeTriggered, this, &SamplesWidget::chokeTriggered); connect(&widget, &SampleWidget::chokeTriggered, this, &SamplesWidget::chokeTriggered);
connect(&widget, &SampleWidget::sendMidi, this, &SamplesWidget::sendMidi);
} }
} }
@ -83,6 +84,12 @@ void SamplesWidget::injectDecodingThread(QThread &thread)
widget.injectDecodingThread(thread); widget.injectDecodingThread(thread);
} }
void SamplesWidget::sendColors()
{
for (SampleWidget &widget : getWidgets())
widget.sendColor();
}
void SamplesWidget::sequencerTriggerSample(int index) void SamplesWidget::sequencerTriggerSample(int index)
{ {
const auto widgets = getWidgets(); const auto widgets = getWidgets();

View File

@ -34,6 +34,11 @@ public:
void injectDecodingThread(QThread &thread); void injectDecodingThread(QThread &thread);
void sendColors();
signals:
void sendMidi(const midi::MidiMessage &midiMsg);
public slots: public slots:
void sequencerTriggerSample(int index); void sequencerTriggerSample(int index);

View File

@ -11,6 +11,7 @@
#include "audiodecoder.h" #include "audiodecoder.h"
#include "drummachinesettings.h" #include "drummachinesettings.h"
#include "midicontainers.h"
namespace { namespace {
QString toString(QString value) { return value; } QString toString(QString value) { return value; }
@ -182,9 +183,45 @@ void SampleWidget::learn(quint8 channel, quint8 note)
learnPressed(); learnPressed();
} }
void SampleWidget::sendColor()
{
midi::MidiMessage midiMsg;
midiMsg.channel = m_ui->channelSpinBox->value();
midiMsg.cmd = midi::Command::NoteOn;
midiMsg.flag = true;
midiMsg.note = m_ui->noteSpinBox->value();
if (m_file && m_file->color && m_player.buffer().isValid())
{
const auto &color = *m_file->color;
if (color == "purple")
midiMsg.velocity = m_player.playing() ? 43 : 18;
else if (color == "red")
midiMsg.velocity = m_player.playing() ? 3 : 1;
else if (color == "yellow")
midiMsg.velocity = m_player.playing() ? 58 : 33;
else if (color == "green")
midiMsg.velocity = m_player.playing() ? 56 : 16;
else if (color == "blue")
midiMsg.velocity = m_player.playing() ? 49 : 51;
else
goto noColor;
}
else
{
noColor:
midiMsg.velocity = 0;
}
emit sendMidi(midiMsg);
}
void SampleWidget::updateStatus() void SampleWidget::updateStatus()
{ {
QPalette pal; QPalette pal;
if (m_file && m_file->color && m_player.buffer().isValid()) if (m_file && m_file->color && m_player.buffer().isValid())
{ {
const auto bright = m_player.playing() ? 255 : 155; const auto bright = m_player.playing() ? 255 : 155;
@ -212,6 +249,8 @@ void SampleWidget::updateStatus()
} }
setPalette(pal); setPalette(pal);
sendColor();
if (m_reply) if (m_reply)
{ {
if (!m_reply->isFinished()) if (!m_reply->isFinished())

View File

@ -7,6 +7,7 @@
#include "audioformat.h" #include "audioformat.h"
#include "presets.h" #include "presets.h"
#include "audioplayer.h" #include "audioplayer.h"
#include "midicontainers.h"
namespace Ui { class SampleWidget; } namespace Ui { class SampleWidget; }
class QNetworkAccessManager; class QNetworkAccessManager;
@ -60,6 +61,10 @@ public:
signals: signals:
void chokeTriggered(int choke); void chokeTriggered(int choke);
void startDecoding(const std::shared_ptr<QIODevice> &device); void startDecoding(const std::shared_ptr<QIODevice> &device);
void sendMidi(const midi::MidiMessage &midiMsg);
public slots:
void sendColor();
private slots: private slots:
void updateStatus(); void updateStatus();