Refactorings and preperations for loop station
This commit is contained in:
@ -16,6 +16,7 @@ SOURCES += \
|
||||
audioplayer.cpp \
|
||||
djwidget.cpp \
|
||||
drummachinesettings.cpp \
|
||||
drumpadwidget.cpp \
|
||||
filesmodel.cpp \
|
||||
graphrenderer.cpp \
|
||||
jsonconverters.cpp \
|
||||
@ -33,6 +34,7 @@ SOURCES += \
|
||||
scratchwidget.cpp \
|
||||
sequencerwidget.cpp \
|
||||
synthisizer.cpp \
|
||||
synthisizerwidget.cpp \
|
||||
trackdeck.cpp \
|
||||
treetotableproxymodel.cpp
|
||||
|
||||
@ -42,6 +44,7 @@ HEADERS += \
|
||||
audioplayer.h \
|
||||
djwidget.h \
|
||||
drummachinesettings.h \
|
||||
drumpadwidget.h \
|
||||
filesmodel.h \
|
||||
graphrenderer.h \
|
||||
jsonconverters.h \
|
||||
@ -58,16 +61,19 @@ HEADERS += \
|
||||
scratchwidget.h \
|
||||
sequencerwidget.h \
|
||||
synthisizer.h \
|
||||
synthisizerwidget.h \
|
||||
trackdeck.h \
|
||||
treetotableproxymodel.h
|
||||
|
||||
FORMS += \
|
||||
djwidget.ui \
|
||||
drumpadwidget.ui \
|
||||
mainwindow.ui \
|
||||
presetdetailwidget.ui \
|
||||
sampleswidget.ui \
|
||||
samplewidget.ui \
|
||||
sequencerwidget.ui \
|
||||
synthisizerwidget.ui \
|
||||
trackdeck.ui
|
||||
|
||||
RESOURCES += \
|
||||
|
16
djwidget.cpp
16
djwidget.cpp
@ -75,6 +75,22 @@ void DjWidget::writeSamples(frame_t *begin, frame_t *end)
|
||||
}
|
||||
}
|
||||
|
||||
void DjWidget::loadSettings(DrumMachineSettings &settings)
|
||||
{
|
||||
}
|
||||
|
||||
void DjWidget::unsendColors()
|
||||
{
|
||||
}
|
||||
|
||||
void DjWidget::sendColors()
|
||||
{
|
||||
}
|
||||
|
||||
void DjWidget::messageReceived(const midi::MidiMessage &message)
|
||||
{
|
||||
}
|
||||
|
||||
void DjWidget::directorySelected()
|
||||
{
|
||||
const auto selected = m_ui->treeViewDirectories->currentIndex();
|
||||
|
12
djwidget.h
12
djwidget.h
@ -7,9 +7,11 @@
|
||||
#include <memory>
|
||||
|
||||
#include "audioformat.h"
|
||||
#include "midicontainers.h"
|
||||
#include "treetotableproxymodel.h"
|
||||
|
||||
namespace Ui { class DjWidget; }
|
||||
class DrumMachineSettings;
|
||||
|
||||
class DjWidget : public QWidget
|
||||
{
|
||||
@ -20,8 +22,16 @@ public:
|
||||
~DjWidget() override;
|
||||
|
||||
void injectDecodingThread(QThread &thread);
|
||||
|
||||
void writeSamples(frame_t *begin, frame_t *end);
|
||||
void loadSettings(DrumMachineSettings &settings);
|
||||
void unsendColors();
|
||||
void sendColors();
|
||||
|
||||
signals:
|
||||
void sendMidi(const midi::MidiMessage &midiMsg);
|
||||
|
||||
public slots:
|
||||
void messageReceived(const midi::MidiMessage &message);
|
||||
|
||||
private slots:
|
||||
void directorySelected();
|
||||
|
152
drumpadwidget.cpp
Normal file
152
drumpadwidget.cpp
Normal file
@ -0,0 +1,152 @@
|
||||
#include "drumpadwidget.h"
|
||||
#include "ui_drumpadwidget.h"
|
||||
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "jsonconverters.h"
|
||||
|
||||
DrumPadWidget::DrumPadWidget(QWidget *parent) :
|
||||
QSplitter{parent},
|
||||
m_ui{std::make_unique<Ui::DrumPadWidget>()}
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
connect(m_ui->samplesWidget, &SamplesWidget::sendMidi, this, &DrumPadWidget::sendMidi);
|
||||
|
||||
connect(m_ui->sequencerWidget, &SequencerWidget::triggerSample, m_ui->samplesWidget, &SamplesWidget::sequencerTriggerSample);
|
||||
|
||||
m_presetsProxyModel.setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
m_presetsProxyModel.setSortRole(Qt::EditRole);
|
||||
m_presetsProxyModel.setSourceModel(&m_presetsModel);
|
||||
m_ui->presetsView->setModel(&m_presetsProxyModel);
|
||||
|
||||
m_presetsProxyModel.setFilterKeyColumn(1);
|
||||
|
||||
connect(m_ui->lineEdit, &QLineEdit::textChanged, &m_presetsProxyModel, &QSortFilterProxyModel::setFilterFixedString);
|
||||
|
||||
m_ui->filesView->setModel(&m_filesModel);
|
||||
|
||||
connect(m_ui->presetsView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &DrumPadWidget::currentRowChanged);
|
||||
|
||||
connect(m_ui->pushButtonRefresh, &QAbstractButton::pressed, this, &DrumPadWidget::loadPresets);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void DrumPadWidget::injectNetworkAccessManager(QNetworkAccessManager &networkAccessManager)
|
||||
{
|
||||
m_networkAccessManager = &networkAccessManager;
|
||||
loadPresets();
|
||||
m_ui->samplesWidget->injectNetworkAccessManager(networkAccessManager);
|
||||
}
|
||||
|
||||
void DrumPadWidget::injectDecodingThread(QThread &thread)
|
||||
{
|
||||
m_ui->samplesWidget->injectDecodingThread(thread);
|
||||
}
|
||||
|
||||
void DrumPadWidget::loadSettings(DrumMachineSettings &settings)
|
||||
{
|
||||
m_ui->samplesWidget->loadSettings(settings);
|
||||
}
|
||||
|
||||
void DrumPadWidget::unsendColors()
|
||||
{
|
||||
m_ui->samplesWidget->unsendColors();
|
||||
}
|
||||
|
||||
void DrumPadWidget::sendColors()
|
||||
{
|
||||
m_ui->samplesWidget->sendColors();
|
||||
}
|
||||
|
||||
void DrumPadWidget::messageReceived(const midi::MidiMessage &message)
|
||||
{
|
||||
m_ui->samplesWidget->messageReceived(message);
|
||||
}
|
||||
|
||||
void DrumPadWidget::currentRowChanged(const QModelIndex ¤t)
|
||||
{
|
||||
if (!current.isValid())
|
||||
return;
|
||||
|
||||
const auto &preset = m_presetsModel.getPreset(m_presetsProxyModel.mapToSource(current));
|
||||
|
||||
m_ui->presetDetailWidget->setPreset(preset);
|
||||
m_filesModel.setPreset(preset);
|
||||
m_ui->sequencerWidget->setPreset(preset);
|
||||
m_ui->samplesWidget->setPreset(preset);
|
||||
}
|
||||
|
||||
void DrumPadWidget::loadPresets()
|
||||
{
|
||||
if (!m_networkAccessManager)
|
||||
{
|
||||
qWarning() << "no network access manager available";
|
||||
return;
|
||||
}
|
||||
|
||||
m_ui->pushButtonRefresh->setEnabled(false);
|
||||
|
||||
QNetworkRequest request{QUrl{"https://brunner.ninja/komposthaufen/dpm/presets_config_v12.json"}};
|
||||
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
|
||||
request.setAttribute(QNetworkRequest::CacheSaveControlAttribute, true);
|
||||
|
||||
m_reply = std::unique_ptr<QNetworkReply>(m_networkAccessManager->get(request));
|
||||
connect(m_reply.get(), &QNetworkReply::finished, this, &DrumPadWidget::requestFinished);
|
||||
}
|
||||
|
||||
void DrumPadWidget::requestFinished()
|
||||
{
|
||||
qDebug() << "called";
|
||||
|
||||
if (!m_reply)
|
||||
{
|
||||
qWarning() << "no valid reply";
|
||||
return;
|
||||
}
|
||||
|
||||
auto reply = std::move(m_reply);
|
||||
if (reply->error() != QNetworkReply::NoError)
|
||||
{
|
||||
QMessageBox::warning(this, tr("Could not load presets!"), tr("Could not load presets!") + "\n\n" + reply->errorString());
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto result = json_converters::parsePresetsConfig(json_converters::loadJson(reply->readAll()));
|
||||
|
||||
if (!result.presets)
|
||||
throw std::runtime_error("presets missing in response");
|
||||
|
||||
m_presetsModel.setPresets(std::move(*std::move(result).presets));
|
||||
|
||||
selectFirstPreset();
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
QMessageBox::warning(this, tr("error"), tr("error") + "\n\n" + QString::fromStdString(e.what()));
|
||||
}
|
||||
}
|
60
drumpadwidget.h
Normal file
60
drumpadwidget.h
Normal file
@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
|
||||
#include <QSplitter>
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "audioformat.h"
|
||||
#include "midicontainers.h"
|
||||
#include "presetsmodel.h"
|
||||
#include "filesmodel.h"
|
||||
|
||||
namespace Ui { class DrumPadWidget; }
|
||||
class SamplesWidget;
|
||||
class SequencerWidget;
|
||||
class QModelIndex;
|
||||
class QNetworkAccessManager;
|
||||
class QThread;
|
||||
class DrumMachineSettings;
|
||||
class QNetworkReply;
|
||||
|
||||
class DrumPadWidget : public QSplitter
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
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);
|
||||
void loadSettings(DrumMachineSettings &settings);
|
||||
void unsendColors();
|
||||
void sendColors();
|
||||
|
||||
signals:
|
||||
void sendMidi(const midi::MidiMessage &midiMsg);
|
||||
|
||||
public slots:
|
||||
void messageReceived(const midi::MidiMessage &message);
|
||||
|
||||
private slots:
|
||||
void currentRowChanged(const QModelIndex ¤t);
|
||||
void loadPresets();
|
||||
void requestFinished();
|
||||
|
||||
private:
|
||||
const std::unique_ptr<Ui::DrumPadWidget> m_ui;
|
||||
|
||||
PresetsModel m_presetsModel;
|
||||
QSortFilterProxyModel m_presetsProxyModel;
|
||||
|
||||
FilesModel m_filesModel;
|
||||
|
||||
QNetworkAccessManager *m_networkAccessManager{};
|
||||
|
||||
std::unique_ptr<QNetworkReply> m_reply;
|
||||
};
|
108
drumpadwidget.ui
Normal file
108
drumpadwidget.ui
Normal file
@ -0,0 +1,108 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>DrumPadWidget</class>
|
||||
<widget class="QSplitter">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>805</width>
|
||||
<height>525</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<widget class="QWidget" name="layoutWidget">
|
||||
<layout class="QVBoxLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEdit">
|
||||
<property name="clearButtonEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButtonRefresh">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>↻</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTreeView" name="presetsView">
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<widget class="PresetDetailWidget" name="presetDetailWidget">
|
||||
<attribute name="title">
|
||||
<string>Properties</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="QTreeView" name="filesView">
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="title">
|
||||
<string>Samples</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QSplitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<widget class="SequencerWidget" name="sequencerWidget" />
|
||||
<widget class="SamplesWidget" name="samplesWidget" />
|
||||
</widget>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>SamplesWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>sampleswidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>PresetDetailWidget</class>
|
||||
<extends>QScrollArea</extends>
|
||||
<header>presetdetailwidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>SequencerWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>sequencerwidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
48
main.cpp
48
main.cpp
@ -1,17 +1,10 @@
|
||||
#include <QApplication>
|
||||
#include <QSslSocket>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
#include <QEventLoop>
|
||||
#include <QSplashScreen>
|
||||
#include <QMessageBox>
|
||||
#include <QNetworkDiskCache>
|
||||
#include <QDebug>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "portaudio.h"
|
||||
|
||||
#include "jsonconverters.h"
|
||||
#include "mainwindow.h"
|
||||
|
||||
namespace {
|
||||
@ -58,7 +51,6 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
auto helper0 = makeCleanupHelper([](){
|
||||
qDebug() << "helper0";
|
||||
if (PaError err = Pa_Terminate(); err != paNoError)
|
||||
fprintf(stderr, "Could not terminate PortAudio!\n");
|
||||
});
|
||||
@ -74,39 +66,6 @@ int main(int argc, char *argv[])
|
||||
"%{function}(): "
|
||||
"%{message}");
|
||||
|
||||
presets::PresetsConfig presetsConfig;
|
||||
|
||||
{
|
||||
QSplashScreen splashScreen{QPixmap{":/drummachine/splashscreen.png"}};
|
||||
splashScreen.showMessage(QCoreApplication::translate("main", "Loading list of presets..."));
|
||||
splashScreen.show();
|
||||
|
||||
QEventLoop eventLoop;
|
||||
|
||||
QNetworkAccessManager manager;
|
||||
|
||||
QNetworkDiskCache cache;
|
||||
cache.setCacheDirectory("cache");
|
||||
|
||||
manager.setCache(&cache);
|
||||
|
||||
QNetworkRequest request{QUrl{"https://brunner.ninja/komposthaufen/dpm/presets_config_v12.json"}};
|
||||
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
|
||||
request.setAttribute(QNetworkRequest::CacheSaveControlAttribute, true);
|
||||
|
||||
const auto reply = std::unique_ptr<QNetworkReply>(manager.get(request));
|
||||
QObject::connect(reply.get(), &QNetworkReply::finished, &eventLoop, &QEventLoop::quit);
|
||||
eventLoop.exec();
|
||||
|
||||
if (reply->error() != QNetworkReply::NoError)
|
||||
{
|
||||
QMessageBox::warning(nullptr, QCoreApplication::translate("main", "Could not load presets!"), QCoreApplication::translate("main", "Could not load presets!") + "\n\n" + reply->errorString());
|
||||
return 1;
|
||||
}
|
||||
|
||||
presetsConfig = json_converters::parsePresetsConfig(json_converters::loadJson(reply->readAll()));
|
||||
}
|
||||
|
||||
#if !defined(Q_OS_WIN)
|
||||
QPalette darkPalette;
|
||||
darkPalette.setColor(QPalette::Window, QColor(53,53,53));
|
||||
@ -127,9 +86,8 @@ int main(int argc, char *argv[])
|
||||
app.setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }");
|
||||
#endif
|
||||
|
||||
MainWindow mainWindow{presetsConfig};
|
||||
mainWindow.showMaximized();
|
||||
mainWindow.selectFirstPreset();
|
||||
MainWindow mainWindow;
|
||||
mainWindow.show();
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
204
mainwindow.cpp
204
mainwindow.cpp
@ -12,56 +12,34 @@
|
||||
#include "presets.h"
|
||||
#include "midiinwrapper.h"
|
||||
#include "midicontainers.h"
|
||||
#include "sampleswidget.h"
|
||||
#include "sequencerwidget.h"
|
||||
|
||||
namespace {
|
||||
void DummyDeleter(PaStream *stream)
|
||||
{
|
||||
Q_UNUSED(stream);
|
||||
}
|
||||
void PaStreamCloser(PaStream *stream)
|
||||
{
|
||||
if (PaError err = Pa_CloseStream(stream); err != paNoError)
|
||||
fprintf(stderr, "Could not close stream!\n");
|
||||
}
|
||||
void PaStreamStopperAndCloser(PaStream *stream)
|
||||
{
|
||||
if (PaError err = Pa_StopStream(stream); err != paNoError)
|
||||
fprintf(stderr, "Could not stop stream!\n");
|
||||
PaStreamCloser(stream);
|
||||
}
|
||||
|
||||
void paStreamFinished(void* userData)
|
||||
{
|
||||
printf("Stream Completed\n");
|
||||
}
|
||||
|
||||
void DummyDeleter(PaStream *stream);
|
||||
void PaStreamCloser(PaStream *stream);
|
||||
void PaStreamStopperAndCloser(PaStream *stream);
|
||||
void paStreamFinished(void* userData);
|
||||
int paCallback(const void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
const PaStreamCallbackTimeInfo* timeInfo,
|
||||
PaStreamCallbackFlags statusFlags,
|
||||
void *userData)
|
||||
{
|
||||
Q_UNUSED(inputBuffer)
|
||||
Q_ASSERT(outputBuffer);
|
||||
Q_ASSERT(framesPerBuffer);
|
||||
Q_UNUSED(timeInfo)
|
||||
Q_UNUSED(statusFlags)
|
||||
Q_ASSERT(userData);
|
||||
void *userData);
|
||||
} // namespace
|
||||
|
||||
auto begin = static_cast<frame_t*>(outputBuffer);
|
||||
|
||||
static_cast<MainWindow*>(userData)->writeSamples(begin, begin+framesPerBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
MainWindow::MainWindow(const presets::PresetsConfig &presetsConfig, QWidget *parent) :
|
||||
MainWindow::MainWindow(QWidget *parent) :
|
||||
QMainWindow{parent},
|
||||
m_ui{std::make_unique<Ui::MainWindow>()},
|
||||
m_paStream{nullptr, DummyDeleter},
|
||||
m_presetsModel{*presetsConfig.presets}
|
||||
m_midiIn{RtMidi::UNSPECIFIED, "DrumMachine"},
|
||||
m_midiOut{RtMidi::UNSPECIFIED, "DrumMachine"}
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
m_ui->splitter->setSizes({99999, 1});
|
||||
|
||||
m_cache.setCacheDirectory("cache");
|
||||
m_networkAccessManager.setCache(&m_cache);
|
||||
|
||||
m_ui->drumPadWidget->injectNetworkAccessManager(m_networkAccessManager);
|
||||
|
||||
connect(&m_midiIn, &MidiInWrapper::messageReceived, this, &MainWindow::messageReceived);
|
||||
|
||||
@ -72,11 +50,9 @@ MainWindow::MainWindow(const presets::PresetsConfig &presetsConfig, QWidget *par
|
||||
eventLoop.exec();
|
||||
}
|
||||
|
||||
m_ui->samplesWidget->injectDecodingThread(m_decoderThread);
|
||||
m_ui->drumPadWidget->injectDecodingThread(m_decoderThread);
|
||||
m_ui->djWidget->injectDecodingThread(m_decoderThread);
|
||||
|
||||
connect(m_ui->sequencerWidget, &SequencerWidget::triggerSample, m_ui->samplesWidget, &SamplesWidget::sequencerTriggerSample);
|
||||
|
||||
updateMidiInDevices();
|
||||
|
||||
updateMidiOutDevices();
|
||||
@ -92,7 +68,7 @@ MainWindow::MainWindow(const presets::PresetsConfig &presetsConfig, QWidget *par
|
||||
{
|
||||
const auto index = m_ui->comboBoxMidiIn->currentIndex();
|
||||
if (index != -1)
|
||||
m_midiIn.openPort(index, "DrumMachine");
|
||||
m_midiIn.openPort(index, "Input");
|
||||
}
|
||||
|
||||
m_ui->comboBoxMidiIn->setDisabled(m_midiIn.isPortOpen());
|
||||
@ -103,6 +79,7 @@ MainWindow::MainWindow(const presets::PresetsConfig &presetsConfig, QWidget *par
|
||||
if (m_midiOut.isPortOpen())
|
||||
{
|
||||
qDebug() << "closing port";
|
||||
unsendColors(m_ui->tabWidget->currentIndex());
|
||||
m_midiOut.closePort();
|
||||
}
|
||||
else
|
||||
@ -110,8 +87,8 @@ MainWindow::MainWindow(const presets::PresetsConfig &presetsConfig, QWidget *par
|
||||
const auto index = m_ui->comboBoxMidiOut->currentIndex();
|
||||
if (index != -1)
|
||||
{
|
||||
m_midiOut.openPort(index, "DrumMachine");
|
||||
sendColors();
|
||||
m_midiOut.openPort(index, "Output");
|
||||
sendColors(m_ui->tabWidget->currentIndex());
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,22 +104,14 @@ MainWindow::MainWindow(const presets::PresetsConfig &presetsConfig, QWidget *par
|
||||
|
||||
connect(m_ui->pushButtonAudioDevice, &QAbstractButton::pressed, this, &MainWindow::openAudioDevice);
|
||||
|
||||
m_presetsProxyModel.setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
m_presetsProxyModel.setSortRole(Qt::EditRole);
|
||||
m_presetsProxyModel.setSourceModel(&m_presetsModel);
|
||||
m_ui->presetsView->setModel(&m_presetsProxyModel);
|
||||
|
||||
m_presetsProxyModel.setFilterKeyColumn(1);
|
||||
|
||||
connect(m_ui->lineEdit, &QLineEdit::textChanged, &m_presetsProxyModel, &QSortFilterProxyModel::setFilterFixedString);
|
||||
|
||||
m_ui->filesView->setModel(&m_filesModel);
|
||||
|
||||
connect(m_ui->presetsView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &MainWindow::currentRowChanged);
|
||||
|
||||
loadSettings();
|
||||
|
||||
connect(m_ui->samplesWidget, &SamplesWidget::sendMidi, this, &MainWindow::sendMidi);
|
||||
connect(m_ui->drumPadWidget, &DrumPadWidget::sendMidi, this, &MainWindow::sendMidi);
|
||||
connect(m_ui->djWidget, &DjWidget::sendMidi, this, &MainWindow::sendMidi);
|
||||
connect(m_ui->synthisizerWidget, &SynthisizerWidget::sendMidi, this, &MainWindow::sendMidi);
|
||||
|
||||
m_lastIndex = m_ui->tabWidget->currentIndex();
|
||||
connect(m_ui->tabWidget, &QTabWidget::currentChanged, this, &MainWindow::currentChanged);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
@ -151,26 +120,13 @@ MainWindow::~MainWindow()
|
||||
m_decoderThread.wait();
|
||||
}
|
||||
|
||||
void MainWindow::selectFirstPreset()
|
||||
{
|
||||
if (m_presetsProxyModel.rowCount())
|
||||
{
|
||||
const auto index = m_presetsProxyModel.index(0, 0);
|
||||
if (index.isValid())
|
||||
{
|
||||
m_ui->presetsView->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
|
||||
currentRowChanged(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::writeSamples(frame_t *begin, frame_t *end)
|
||||
int MainWindow::writeSamples(frame_t *begin, frame_t *end)
|
||||
{
|
||||
std::fill(begin, end, frame_t{0.,0.});
|
||||
|
||||
m_ui->samplesWidget->writeSamples(begin, end);
|
||||
m_ui->drumPadWidget->writeSamples(begin, end);
|
||||
m_ui->djWidget->writeSamples(begin, end);
|
||||
m_synthisizer.writeSamples(begin, end);
|
||||
m_ui->synthisizerWidget->writeSamples(begin, end);
|
||||
|
||||
std::transform(begin, end, begin, [factor=float(m_ui->horizontalSliderMaster->value())/100.f](frame_t frame){
|
||||
std::transform(std::cbegin(frame), std::cend(frame), std::begin(frame), [&factor](const sample_t &sample){
|
||||
@ -178,6 +134,8 @@ void MainWindow::writeSamples(frame_t *begin, frame_t *end)
|
||||
});
|
||||
return frame;
|
||||
});
|
||||
|
||||
return paContinue;
|
||||
}
|
||||
|
||||
void MainWindow::openAudioDevice()
|
||||
@ -242,23 +200,12 @@ void MainWindow::messageReceived(const midi::MidiMessage &message)
|
||||
.arg(message.flag?"true":"false", QMetaEnum::fromType<midi::Command>().valueToKey(int(message.cmd)))
|
||||
.arg(message.channel).arg(message.note).arg(message.velocity), 1000);
|
||||
|
||||
if (m_ui->comboBoxMidiType->currentIndex() == 0)
|
||||
m_ui->samplesWidget->messageReceived(message);
|
||||
else if (m_ui->comboBoxMidiType->currentIndex() == 1)
|
||||
m_synthisizer.messageReceived(message);
|
||||
}
|
||||
|
||||
void MainWindow::currentRowChanged(const QModelIndex ¤t)
|
||||
{
|
||||
if (!current.isValid())
|
||||
return;
|
||||
|
||||
const auto &preset = m_presetsModel.getPreset(m_presetsProxyModel.mapToSource(current));
|
||||
|
||||
m_ui->presetDetailWidget->setPreset(preset);
|
||||
m_filesModel.setPreset(preset);
|
||||
m_ui->sequencerWidget->setPreset(preset);
|
||||
m_ui->samplesWidget->setPreset(preset);
|
||||
if (m_ui->tabWidget->currentIndex() == 0)
|
||||
m_ui->drumPadWidget->messageReceived(message);
|
||||
else if (m_ui->tabWidget->currentIndex() == 1)
|
||||
m_ui->djWidget->messageReceived(message);
|
||||
else if (m_ui->tabWidget->currentIndex() == 2)
|
||||
m_ui->synthisizerWidget->messageReceived(message);
|
||||
}
|
||||
|
||||
void MainWindow::sendMidi(const midi::MidiMessage &midiMsg)
|
||||
@ -267,6 +214,13 @@ void MainWindow::sendMidi(const midi::MidiMessage &midiMsg)
|
||||
m_midiOut.sendMessage(midiMsg);
|
||||
}
|
||||
|
||||
void MainWindow::currentChanged(int index)
|
||||
{
|
||||
unsendColors(m_lastIndex);
|
||||
m_lastIndex = index;
|
||||
sendColors(index);
|
||||
}
|
||||
|
||||
void MainWindow::updateMidiInDevices()
|
||||
{
|
||||
m_ui->comboBoxMidiIn->clear();
|
||||
@ -302,15 +256,34 @@ void MainWindow::updateAudioDevices()
|
||||
|
||||
void MainWindow::loadSettings()
|
||||
{
|
||||
m_synthisizer.loadSettings(m_settings);
|
||||
m_ui->samplesWidget->loadSettings(m_settings);
|
||||
m_ui->drumPadWidget->loadSettings(m_settings);
|
||||
m_ui->djWidget->loadSettings(m_settings);
|
||||
m_ui->synthisizerWidget->loadSettings(m_settings);
|
||||
}
|
||||
|
||||
void MainWindow::sendColors()
|
||||
void MainWindow::unsendColors(int index)
|
||||
{
|
||||
m_ui->samplesWidget->sendColors();
|
||||
if (index == 0)
|
||||
m_ui->drumPadWidget->unsendColors();
|
||||
else if (index == 1)
|
||||
m_ui->djWidget->unsendColors();
|
||||
else if (index == 2)
|
||||
m_ui->synthisizerWidget->unsendColors();
|
||||
}
|
||||
|
||||
void MainWindow::sendColors(int index)
|
||||
{
|
||||
if (index == 0)
|
||||
m_ui->drumPadWidget->sendColors();
|
||||
else if (index == 1)
|
||||
m_ui->djWidget->sendColors();
|
||||
else if (index == 2)
|
||||
m_ui->synthisizerWidget->sendColors();
|
||||
|
||||
return;
|
||||
|
||||
// this was just for debugging all the available colors on novation launchpad mk1
|
||||
|
||||
int k{0};
|
||||
for (int j = 0; j < 128; j+= 16)
|
||||
{
|
||||
@ -327,3 +300,44 @@ void MainWindow::sendColors()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
void DummyDeleter(PaStream *stream)
|
||||
{
|
||||
Q_UNUSED(stream);
|
||||
}
|
||||
void PaStreamCloser(PaStream *stream)
|
||||
{
|
||||
if (PaError err = Pa_CloseStream(stream); err != paNoError)
|
||||
fprintf(stderr, "Could not close stream!\n");
|
||||
}
|
||||
void PaStreamStopperAndCloser(PaStream *stream)
|
||||
{
|
||||
if (PaError err = Pa_StopStream(stream); err != paNoError)
|
||||
fprintf(stderr, "Could not stop stream!\n");
|
||||
PaStreamCloser(stream);
|
||||
}
|
||||
|
||||
void paStreamFinished(void* userData)
|
||||
{
|
||||
printf("Stream Completed\n");
|
||||
}
|
||||
|
||||
int paCallback(const void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
const PaStreamCallbackTimeInfo* timeInfo,
|
||||
PaStreamCallbackFlags statusFlags,
|
||||
void *userData)
|
||||
{
|
||||
Q_UNUSED(inputBuffer)
|
||||
Q_ASSERT(outputBuffer);
|
||||
Q_ASSERT(framesPerBuffer);
|
||||
Q_UNUSED(timeInfo)
|
||||
Q_UNUSED(statusFlags)
|
||||
Q_ASSERT(userData);
|
||||
|
||||
auto begin = static_cast<frame_t*>(outputBuffer);
|
||||
|
||||
return static_cast<MainWindow*>(userData)->writeSamples(begin, begin+framesPerBuffer);
|
||||
}
|
||||
} // namespace
|
||||
|
27
mainwindow.h
27
mainwindow.h
@ -3,17 +3,15 @@
|
||||
#include <memory>
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QThread>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkDiskCache>
|
||||
|
||||
#include "portaudio.h"
|
||||
|
||||
#include "audioformat.h"
|
||||
#include "presetsmodel.h"
|
||||
#include "filesmodel.h"
|
||||
#include "midiinwrapper.h"
|
||||
#include "midioutwrapper.h"
|
||||
#include "synthisizer.h"
|
||||
#include "drummachinesettings.h"
|
||||
|
||||
namespace Ui { class MainWindow; }
|
||||
@ -25,25 +23,24 @@ class MainWindow : public QMainWindow
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(const presets::PresetsConfig &presetsConfig, QWidget *parent = nullptr);
|
||||
explicit MainWindow(QWidget *parent = nullptr);
|
||||
~MainWindow() override;
|
||||
|
||||
void selectFirstPreset();
|
||||
|
||||
void writeSamples(frame_t *begin, frame_t *end);
|
||||
int writeSamples(frame_t *begin, frame_t *end);
|
||||
|
||||
private slots:
|
||||
void openAudioDevice();
|
||||
void messageReceived(const midi::MidiMessage &message);
|
||||
void currentRowChanged(const QModelIndex ¤t);
|
||||
void sendMidi(const midi::MidiMessage &midiMsg);
|
||||
void currentChanged(int index);
|
||||
|
||||
private:
|
||||
void updateMidiInDevices();
|
||||
void updateMidiOutDevices();
|
||||
void updateAudioDevices();
|
||||
void loadSettings();
|
||||
void sendColors();
|
||||
void unsendColors(int index);
|
||||
void sendColors(int index);
|
||||
|
||||
const std::unique_ptr<Ui::MainWindow> m_ui;
|
||||
|
||||
@ -54,12 +51,10 @@ private:
|
||||
MidiInWrapper m_midiIn;
|
||||
MidiOutWrapper m_midiOut;
|
||||
|
||||
QNetworkAccessManager m_networkAccessManager;
|
||||
QNetworkDiskCache m_cache;
|
||||
|
||||
QThread m_decoderThread;
|
||||
|
||||
Synthisizer m_synthisizer;
|
||||
|
||||
PresetsModel m_presetsModel;
|
||||
QSortFilterProxyModel m_presetsProxyModel;
|
||||
|
||||
FilesModel m_filesModel;
|
||||
int m_lastIndex;
|
||||
};
|
||||
|
226
mainwindow.ui
226
mainwindow.ui
@ -14,7 +14,7 @@
|
||||
<string>MainWindow</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="widget">
|
||||
<layout class="QVBoxLayout" stretch="0,1">
|
||||
<layout class="QVBoxLayout" stretch="0,0,0">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
@ -91,6 +91,43 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboBoxAudioDevice"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButtonRefreshAudioDevices">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>↻</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="spinBoxBufferSize">
|
||||
<property name="maximum">
|
||||
<number>65535</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>32</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButtonAudioDevice">
|
||||
<property name="text">
|
||||
<string>Open</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelMidiIn">
|
||||
<property name="text">
|
||||
@ -121,20 +158,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboBoxMidiType">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Samples</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Synthisizer</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
@ -178,139 +201,24 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<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="QComboBox" name="comboBoxAudioDevice"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButtonRefreshAudioDevices">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>↻</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="spinBoxBufferSize">
|
||||
<property name="maximum">
|
||||
<number>65535</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>32</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButtonAudioDevice">
|
||||
<property name="text">
|
||||
<string>Open</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<widget class="QSplitter" name="splitter_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<widget class="QWidget" name="layoutWidget">
|
||||
<layout class="QVBoxLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEdit">
|
||||
<property name="clearButtonEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<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="QPushButton" name="selectRandomPushButton">
|
||||
<property name="text">
|
||||
<string>Select random</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<widget class="QTreeView" name="presetsView">
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<widget class="PresetDetailWidget" name="presetDetailWidget">
|
||||
<attribute name="title">
|
||||
<string>Properties</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="QTreeView" name="filesView">
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="title">
|
||||
<string>Samples</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="DjWidget" name="djWidget" native="true"/>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<widget class="DrumPadWidget" name="drumPadWidget">
|
||||
<attribute name="title">
|
||||
<string>DrumPad</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="QWidget" name="layoutWidget">
|
||||
<layout class="QVBoxLayout" stretch="1,0">
|
||||
<item>
|
||||
<widget class="SequencerWidget" name="sequencerWidget" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="SamplesWidget" name="samplesWidget" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
<widget class="DjWidget" name="djWidget">
|
||||
<attribute name="title">
|
||||
<string>DJ</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="SynthisizerWidget" name="synthisizerWidget">
|
||||
<attribute name="title">
|
||||
<string>Synthisizer</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
@ -329,30 +237,24 @@
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>SamplesWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>sampleswidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>PresetDetailWidget</class>
|
||||
<extends>QScrollArea</extends>
|
||||
<header>presetdetailwidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>SequencerWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>sequencerwidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>DjWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>djwidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>DrumPadWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>drumpadwidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>SynthisizerWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>synthisizerwidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
@ -13,9 +13,9 @@ class MidiInWrapper : public QObject
|
||||
|
||||
public:
|
||||
MidiInWrapper(RtMidi::Api api = RtMidi::UNSPECIFIED,
|
||||
const QString &clientName = "RtMidi Input Client",
|
||||
unsigned int queueSizeLimit = 100,
|
||||
QObject *parent = nullptr);
|
||||
const QString &clientName = "RtMidi Input Client",
|
||||
unsigned int queueSizeLimit = 100,
|
||||
QObject *parent = nullptr);
|
||||
|
||||
void openPort(unsigned int portNumber, const QString &portName);
|
||||
void openVirtualPort(const QString &portName);
|
||||
|
@ -32,6 +32,11 @@ enum {
|
||||
NumberOfColumns
|
||||
};
|
||||
|
||||
PresetsModel::PresetsModel(QObject *parent) :
|
||||
QAbstractTableModel{parent}
|
||||
{
|
||||
}
|
||||
|
||||
PresetsModel::PresetsModel(const std::map<QString, presets::Preset> &presets, QObject *parent) :
|
||||
QAbstractTableModel{parent}
|
||||
{
|
||||
@ -40,8 +45,44 @@ PresetsModel::PresetsModel(const std::map<QString, presets::Preset> &presets, QO
|
||||
m_presets.emplace_back(pair.second);
|
||||
}
|
||||
|
||||
PresetsModel::PresetsModel(std::vector<presets::Preset> &&presets, QObject *parent) :
|
||||
QAbstractTableModel{parent}
|
||||
{
|
||||
m_presets = std::move(presets);
|
||||
}
|
||||
|
||||
PresetsModel::PresetsModel(const std::vector<presets::Preset> &presets, QObject *parent) :
|
||||
QAbstractTableModel{parent}
|
||||
{
|
||||
m_presets = presets;
|
||||
}
|
||||
|
||||
PresetsModel::~PresetsModel() = default;
|
||||
|
||||
void PresetsModel::setPresets(const std::map<QString, presets::Preset> &presets)
|
||||
{
|
||||
beginResetModel();
|
||||
m_presets.clear();
|
||||
m_presets.reserve(std::size(presets));
|
||||
for (const auto &pair : presets)
|
||||
m_presets.emplace_back(pair.second);
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void PresetsModel::setPresets(std::vector<presets::Preset> &&presets)
|
||||
{
|
||||
beginResetModel();
|
||||
m_presets = std::move(presets);
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void PresetsModel::setPresets(const std::vector<presets::Preset> &presets)
|
||||
{
|
||||
beginResetModel();
|
||||
m_presets = presets;
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
const presets::Preset &PresetsModel::getPreset(const QModelIndex &index) const
|
||||
{
|
||||
return getPreset(index.row());
|
||||
|
@ -11,9 +11,16 @@ class PresetsModel : public QAbstractTableModel
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PresetsModel(QObject *parent = nullptr);
|
||||
PresetsModel(const std::map<QString, presets::Preset> &presets, QObject *parent = nullptr);
|
||||
PresetsModel(std::vector<presets::Preset> &&presets, QObject *parent = nullptr);
|
||||
PresetsModel(const std::vector<presets::Preset> &presets, QObject *parent = nullptr);
|
||||
~PresetsModel() override;
|
||||
|
||||
void setPresets(const std::map<QString, presets::Preset> &presets);
|
||||
void setPresets(std::vector<presets::Preset> &&presets);
|
||||
void setPresets(const std::vector<presets::Preset> &presets);
|
||||
|
||||
const presets::Preset &getPreset(const QModelIndex &index) const;
|
||||
const presets::Preset &getPreset(int row) const;
|
||||
|
||||
|
@ -13,9 +13,6 @@ SamplesWidget::SamplesWidget(QWidget *parent) :
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
m_cache.setCacheDirectory("cache");
|
||||
m_networkAccessManager.setCache(&m_cache);
|
||||
|
||||
connect(m_ui->checkBox, &QCheckBox::toggled, this, &SamplesWidget::updateWidgets);
|
||||
|
||||
connect(m_ui->pushButtonStopAll, &QAbstractButton::pressed, this, &SamplesWidget::stopAll);
|
||||
@ -24,7 +21,6 @@ SamplesWidget::SamplesWidget(QWidget *parent) :
|
||||
for (SampleWidget &widget : getWidgets())
|
||||
{
|
||||
widget.setPadNr(padNr++);
|
||||
widget.injectNetworkAccessManager(m_networkAccessManager);
|
||||
connect(&widget, &SampleWidget::chokeTriggered, this, &SamplesWidget::chokeTriggered);
|
||||
connect(&widget, &SampleWidget::sendMidi, this, &SamplesWidget::sendMidi);
|
||||
}
|
||||
@ -78,12 +74,24 @@ void SamplesWidget::writeSamples(frame_t *begin, frame_t *end)
|
||||
widget.writeSamples(begin, end);
|
||||
}
|
||||
|
||||
void SamplesWidget::injectNetworkAccessManager(QNetworkAccessManager &networkAccessManager)
|
||||
{
|
||||
for (SampleWidget &widget : getWidgets())
|
||||
widget.injectNetworkAccessManager(networkAccessManager);
|
||||
}
|
||||
|
||||
void SamplesWidget::injectDecodingThread(QThread &thread)
|
||||
{
|
||||
for (SampleWidget &widget : getWidgets())
|
||||
widget.injectDecodingThread(thread);
|
||||
}
|
||||
|
||||
void SamplesWidget::unsendColors()
|
||||
{
|
||||
for (SampleWidget &widget : getWidgets())
|
||||
widget.unsendColor();
|
||||
}
|
||||
|
||||
void SamplesWidget::sendColors()
|
||||
{
|
||||
for (SampleWidget &widget : getWidgets())
|
||||
|
@ -5,14 +5,13 @@
|
||||
#include <functional>
|
||||
|
||||
#include <QWidget>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkDiskCache>
|
||||
|
||||
#include "audioformat.h"
|
||||
#include "presets.h"
|
||||
|
||||
namespace Ui { class SamplesWidget; }
|
||||
namespace midi { class MidiMessage; }
|
||||
class QNetworkAccessManager;
|
||||
class SampleWidget;
|
||||
class DrumMachineSettings;
|
||||
|
||||
@ -32,8 +31,10 @@ public:
|
||||
|
||||
void writeSamples(frame_t *begin, frame_t *end);
|
||||
|
||||
void injectNetworkAccessManager(QNetworkAccessManager &networkAccessManager);
|
||||
void injectDecodingThread(QThread &thread);
|
||||
|
||||
void unsendColors();
|
||||
void sendColors();
|
||||
|
||||
signals:
|
||||
@ -52,8 +53,5 @@ private:
|
||||
|
||||
const std::unique_ptr<Ui::SamplesWidget> m_ui;
|
||||
|
||||
QNetworkDiskCache m_cache;
|
||||
QNetworkAccessManager m_networkAccessManager;
|
||||
|
||||
presets::Preset m_preset;
|
||||
};
|
||||
|
@ -183,6 +183,19 @@ void SampleWidget::learn(quint8 channel, quint8 note)
|
||||
learnPressed();
|
||||
}
|
||||
|
||||
void SampleWidget::unsendColor()
|
||||
{
|
||||
midi::MidiMessage midiMsg;
|
||||
|
||||
midiMsg.channel = m_ui->channelSpinBox->value();
|
||||
midiMsg.cmd = midi::Command::NoteOn;
|
||||
midiMsg.flag = true;
|
||||
midiMsg.note = m_ui->noteSpinBox->value();
|
||||
midiMsg.velocity = 0;
|
||||
|
||||
emit sendMidi(midiMsg);
|
||||
}
|
||||
|
||||
void SampleWidget::sendColor()
|
||||
{
|
||||
midi::MidiMessage midiMsg;
|
||||
|
@ -58,14 +58,14 @@ public:
|
||||
bool isLearning() const { return m_learning; }
|
||||
void learn(quint8 channel, quint8 note);
|
||||
|
||||
void unsendColor();
|
||||
void sendColor();
|
||||
|
||||
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();
|
||||
void requestFinished();
|
||||
|
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
<width>140</width>
|
||||
<height>157</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -63,8 +63,14 @@
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>PushButton</string>
|
||||
<string>▶</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -119,9 +125,6 @@
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Sunken</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
@ -132,9 +135,6 @@
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Sunken</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
@ -145,9 +145,6 @@
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Sunken</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -42,6 +42,6 @@ private:
|
||||
|
||||
QTimer m_timer;
|
||||
|
||||
int m_pos;
|
||||
int m_pos{};
|
||||
std::array<QLabel*, 24> m_sampleLabels;
|
||||
};
|
||||
|
@ -6,10 +6,6 @@
|
||||
|
||||
constexpr double pi = std::acos(-1);
|
||||
|
||||
void Synthisizer::loadSettings(const DrumMachineSettings &settings)
|
||||
{
|
||||
}
|
||||
|
||||
void Synthisizer::writeSamples(frame_t *begin, frame_t *end)
|
||||
{
|
||||
const auto frequency = m_frequency;
|
||||
|
@ -9,14 +9,15 @@ class DrumMachineSettings;
|
||||
class Synthisizer
|
||||
{
|
||||
public:
|
||||
void loadSettings(const DrumMachineSettings &settings);
|
||||
|
||||
void setFrequency(int16_t frequency) { m_frequency = frequency; }
|
||||
|
||||
void writeSamples(frame_t *begin, frame_t *end);
|
||||
|
||||
void messageReceived(const midi::MidiMessage &message);
|
||||
|
||||
signals:
|
||||
void sendMidi(const midi::MidiMessage &midiMsg);
|
||||
|
||||
private:
|
||||
int16_t m_frequency{};
|
||||
int16_t m_actualFrequency{};
|
||||
|
33
synthisizerwidget.cpp
Normal file
33
synthisizerwidget.cpp
Normal file
@ -0,0 +1,33 @@
|
||||
#include "synthisizerwidget.h"
|
||||
#include "ui_synthisizerwidget.h"
|
||||
|
||||
SynthisizerWidget::SynthisizerWidget(QWidget *parent) :
|
||||
QWidget{parent},
|
||||
m_ui{std::make_unique<Ui::SynthisizerWidget>()}
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
}
|
||||
|
||||
SynthisizerWidget::~SynthisizerWidget() = default;
|
||||
|
||||
void SynthisizerWidget::writeSamples(frame_t *begin, frame_t *end)
|
||||
{
|
||||
m_synthisizer.writeSamples(begin, end);
|
||||
}
|
||||
|
||||
void SynthisizerWidget::loadSettings(DrumMachineSettings &settings)
|
||||
{
|
||||
}
|
||||
|
||||
void SynthisizerWidget::unsendColors()
|
||||
{
|
||||
}
|
||||
|
||||
void SynthisizerWidget::sendColors()
|
||||
{
|
||||
}
|
||||
|
||||
void SynthisizerWidget::messageReceived(const midi::MidiMessage &message)
|
||||
{
|
||||
m_synthisizer.messageReceived(message);
|
||||
}
|
35
synthisizerwidget.h
Normal file
35
synthisizerwidget.h
Normal file
@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <memory>
|
||||
|
||||
#include "synthisizer.h"
|
||||
#include "audioformat.h"
|
||||
|
||||
namespace Ui { class SynthisizerWidget; }
|
||||
class DrumMachineSettings;
|
||||
|
||||
class SynthisizerWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SynthisizerWidget(QWidget *parent = nullptr);
|
||||
~SynthisizerWidget() override;
|
||||
|
||||
void writeSamples(frame_t *begin, frame_t *end);
|
||||
void loadSettings(DrumMachineSettings &settings);
|
||||
void unsendColors();
|
||||
void sendColors();
|
||||
|
||||
signals:
|
||||
void sendMidi(const midi::MidiMessage &midiMsg);
|
||||
|
||||
public slots:
|
||||
void messageReceived(const midi::MidiMessage &message);
|
||||
|
||||
private:
|
||||
const std::unique_ptr<Ui::SynthisizerWidget> m_ui;
|
||||
|
||||
Synthisizer m_synthisizer;
|
||||
};
|
32
synthisizerwidget.ui
Normal file
32
synthisizerwidget.ui
Normal file
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SynthisizerWidget</class>
|
||||
<widget class="QWidget" name="SynthisizerWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>30</x>
|
||||
<y>60</y>
|
||||
<width>221</width>
|
||||
<height>31</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Hier könnte ihr Synthisizer stehen.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
Reference in New Issue
Block a user