Implemented midi output for feedback on color pads
This commit is contained in:
@ -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 \
|
||||
|
@ -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 ¤t)
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 ¤t);
|
||||
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;
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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)
|
||||
|
@ -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
58
midioutwrapper.cpp
Normal 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
24
midioutwrapper.h
Normal 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;
|
||||
};
|
@ -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();
|
||||
|
@ -34,6 +34,11 @@ public:
|
||||
|
||||
void injectDecodingThread(QThread &thread);
|
||||
|
||||
void sendColors();
|
||||
|
||||
signals:
|
||||
void sendMidi(const midi::MidiMessage &midiMsg);
|
||||
|
||||
public slots:
|
||||
void sequencerTriggerSample(int index);
|
||||
|
||||
|
@ -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())
|
||||
|
@ -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();
|
||||
|
Reference in New Issue
Block a user