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 \
midicontainers.cpp \
midiinwrapper.cpp \
midioutwrapper.cpp \
presetdetailwidget.cpp \
presets.cpp \
presetsmodel.cpp \
@ -47,6 +48,7 @@ HEADERS += \
mainwindow.h \
midicontainers.h \
midiinwrapper.h \
midioutwrapper.h \
presetdetailwidget.h \
presets.h \
presetsmodel.h \

View File

@ -14,7 +14,10 @@
#include "midicontainers.h"
namespace {
void DummyDeleter(PaStream *stream) {}
void DummyDeleter(PaStream *stream)
{
Q_UNUSED(stream);
}
void PaStreamCloser(PaStream *stream)
{
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);
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())
m_midiIn.closePort();
else
{
const auto index = m_ui->comboBoxMidiController->currentIndex();
const auto index = m_ui->comboBoxMidiIn->currentIndex();
if (index != -1)
m_midiIn.openPort(index);
m_midiIn.openPort(index, "DrumMachine");
}
m_ui->comboBoxMidiController->setDisabled(m_midiIn.isPortOpen());
m_ui->pushButtonMidiController->setText(m_midiIn.isPortOpen() ? tr("Close") : tr("Open"));
m_ui->comboBoxMidiIn->setDisabled(m_midiIn.isPortOpen());
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();
@ -114,6 +141,8 @@ MainWindow::MainWindow(const presets::PresetsConfig &presetsConfig, QWidget *par
connect(m_ui->presetsView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &MainWindow::currentRowChanged);
loadSettings();
connect(m_ui->samplesWidget, &SamplesWidget::sendMidi, this, &MainWindow::sendMidi);
}
MainWindow::~MainWindow()
@ -232,14 +261,30 @@ void MainWindow::currentRowChanged(const QModelIndex &current)
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())
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()
@ -260,3 +305,25 @@ void MainWindow::loadSettings()
m_synthisizer.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 "filesmodel.h"
#include "midiinwrapper.h"
#include "midioutwrapper.h"
#include "synthisizer.h"
#include "drummachinesettings.h"
@ -35,11 +36,14 @@ private slots:
void openAudioDevice();
void messageReceived(const midi::MidiMessage &message);
void currentRowChanged(const QModelIndex &current);
void sendMidi(const midi::MidiMessage &midiMsg);
private:
void updateMidiDevices();
void updateMidiInDevices();
void updateMidiOutDevices();
void updateAudioDevices();
void loadSettings();
void sendColors();
const std::unique_ptr<Ui::MainWindow> m_ui;
@ -48,6 +52,7 @@ private:
std::unique_ptr<PaStream, void(*)(PaStream*)> m_paStream;
MidiInWrapper m_midiIn;
MidiOutWrapper m_midiOut;
QThread m_decoderThread;

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>1017</width>
<height>712</height>
<width>1520</width>
<height>890</height>
</rect>
</property>
<property name="windowTitle">
@ -99,10 +99,10 @@
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBoxMidiController"/>
<widget class="QComboBox" name="comboBoxMidiIn"/>
</item>
<item>
<widget class="QPushButton" name="pushButtonRefreshMidiControllers">
<widget class="QPushButton" name="pushButtonRefreshMidiIn">
<property name="maximumSize">
<size>
<width>32</width>
@ -115,7 +115,7 @@
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonMidiController">
<widget class="QPushButton" name="pushButtonMidiIn">
<property name="text">
<string>Open</string>
</property>
@ -135,6 +135,49 @@
</item>
</widget>
</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>
<spacer name="horizontalSpacer">
<property name="orientation">
@ -278,8 +321,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>1017</width>
<height>20</height>
<width>1520</width>
<height>23</height>
</rect>
</property>
</widget>

View File

@ -12,9 +12,12 @@ MidiInWrapper::MidiInWrapper(RtMidi::Api api, const QString& clientName, unsigne
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)

View File

@ -17,7 +17,7 @@ public:
unsigned int queueSizeLimit = 100,
QObject *parent = nullptr);
void openPort(unsigned int portNumber);
void openPort(unsigned int portNumber, const QString &portName);
void openVirtualPort(const QString &portName);
void closePort();
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.injectNetworkAccessManager(m_networkAccessManager);
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);
}
void SamplesWidget::sendColors()
{
for (SampleWidget &widget : getWidgets())
widget.sendColor();
}
void SamplesWidget::sequencerTriggerSample(int index)
{
const auto widgets = getWidgets();

View File

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

View File

@ -11,6 +11,7 @@
#include "audiodecoder.h"
#include "drummachinesettings.h"
#include "midicontainers.h"
namespace {
QString toString(QString value) { return value; }
@ -182,9 +183,45 @@ void SampleWidget::learn(quint8 channel, quint8 note)
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()
{
QPalette pal;
if (m_file && m_file->color && m_player.buffer().isValid())
{
const auto bright = m_player.playing() ? 255 : 155;
@ -212,6 +249,8 @@ void SampleWidget::updateStatus()
}
setPalette(pal);
sendColor();
if (m_reply)
{
if (!m_reply->isFinished())

View File

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