From a0efca01c7078fae3dd08f0af8cd36afb06b6d80 Mon Sep 17 00:00:00 2001 From: 0xFEEDC0DE64 Date: Sun, 18 Dec 2022 03:30:46 +0100 Subject: [PATCH] Refactorings and preperations for loop station --- DrumMachine.pro | 6 ++ djwidget.cpp | 16 +++ djwidget.h | 12 ++- drumpadwidget.cpp | 152 ++++++++++++++++++++++++++++ drumpadwidget.h | 60 +++++++++++ drumpadwidget.ui | 108 ++++++++++++++++++++ main.cpp | 48 +-------- mainwindow.cpp | 204 ++++++++++++++++++++------------------ mainwindow.h | 27 ++--- mainwindow.ui | 226 ++++++++++++------------------------------ midiinwrapper.h | 6 +- presetsmodel.cpp | 41 ++++++++ presetsmodel.h | 7 ++ sampleswidget.cpp | 16 ++- sampleswidget.h | 8 +- samplewidget.cpp | 13 +++ samplewidget.h | 6 +- samplewidget.ui | 21 ++-- sequencerwidget.h | 2 +- synthisizer.cpp | 4 - synthisizer.h | 5 +- synthisizerwidget.cpp | 33 ++++++ synthisizerwidget.h | 35 +++++++ synthisizerwidget.ui | 32 ++++++ 24 files changed, 735 insertions(+), 353 deletions(-) create mode 100644 drumpadwidget.cpp create mode 100644 drumpadwidget.h create mode 100644 drumpadwidget.ui create mode 100644 synthisizerwidget.cpp create mode 100644 synthisizerwidget.h create mode 100644 synthisizerwidget.ui diff --git a/DrumMachine.pro b/DrumMachine.pro index 4224bd3..e707c5f 100755 --- a/DrumMachine.pro +++ b/DrumMachine.pro @@ -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 += \ diff --git a/djwidget.cpp b/djwidget.cpp index cb4e626..f75e52f 100644 --- a/djwidget.cpp +++ b/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(); diff --git a/djwidget.h b/djwidget.h index 41b9d27..3497458 100644 --- a/djwidget.h +++ b/djwidget.h @@ -7,9 +7,11 @@ #include #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(); diff --git a/drumpadwidget.cpp b/drumpadwidget.cpp new file mode 100644 index 0000000..1a287c8 --- /dev/null +++ b/drumpadwidget.cpp @@ -0,0 +1,152 @@ +#include "drumpadwidget.h" +#include "ui_drumpadwidget.h" + +#include +#include +#include +#include + +#include "jsonconverters.h" + +DrumPadWidget::DrumPadWidget(QWidget *parent) : + QSplitter{parent}, + m_ui{std::make_unique()} +{ + 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(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())); + } +} diff --git a/drumpadwidget.h b/drumpadwidget.h new file mode 100644 index 0000000..c46e6ef --- /dev/null +++ b/drumpadwidget.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include + +#include + +#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 m_ui; + + PresetsModel m_presetsModel; + QSortFilterProxyModel m_presetsProxyModel; + + FilesModel m_filesModel; + + QNetworkAccessManager *m_networkAccessManager{}; + + std::unique_ptr m_reply; +}; diff --git a/drumpadwidget.ui b/drumpadwidget.ui new file mode 100644 index 0000000..2119ad0 --- /dev/null +++ b/drumpadwidget.ui @@ -0,0 +1,108 @@ + + + DrumPadWidget + + + + 0 + 0 + 805 + 525 + + + + Qt::Horizontal + + + + + + + + + + + true + + + + + + + + 32 + 16777215 + + + + + + + + + + + + + false + + + true + + + + + + + + + 2 + + + + Properties + + + + + false + + + Samples + + + + + + + + + Qt::Vertical + + + + + + + + SamplesWidget + QWidget +
sampleswidget.h
+ 1 +
+ + PresetDetailWidget + QScrollArea +
presetdetailwidget.h
+ 1 +
+ + SequencerWidget + QWidget +
sequencerwidget.h
+ 1 +
+
+ + +
diff --git a/main.cpp b/main.cpp index c2f48c9..741d6a8 100755 --- a/main.cpp +++ b/main.cpp @@ -1,17 +1,10 @@ #include #include -#include -#include -#include -#include -#include -#include -#include #include +#include #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(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(); } diff --git a/mainwindow.cpp b/mainwindow.cpp index cf644f8..91fb165 100755 --- a/mainwindow.cpp +++ b/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(outputBuffer); - - static_cast(userData)->writeSamples(begin, begin+framesPerBuffer); -} -} - -MainWindow::MainWindow(const presets::PresetsConfig &presetsConfig, QWidget *parent) : +MainWindow::MainWindow(QWidget *parent) : QMainWindow{parent}, m_ui{std::make_unique()}, 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().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(outputBuffer); + + return static_cast(userData)->writeSamples(begin, begin+framesPerBuffer); +} +} // namespace diff --git a/mainwindow.h b/mainwindow.h index 3d3c1da..27cd9e8 100755 --- a/mainwindow.h +++ b/mainwindow.h @@ -3,17 +3,15 @@ #include #include -#include #include +#include +#include #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 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; }; diff --git a/mainwindow.ui b/mainwindow.ui index 3f90aee..5ece7c0 100755 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -14,7 +14,7 @@ MainWindow - + 0 @@ -91,6 +91,43 @@ + + + + + + + + 32 + 16777215 + + + + + + + + + + + 65535 + + + 32 + + + + + + + Open + + + + + + + @@ -121,20 +158,6 @@ - - - - - Samples - - - - - Synthisizer - - - - @@ -178,139 +201,24 @@ - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - 32 - 16777215 - - - - - - - - - - - 65535 - - - 32 - - - - - - - Open - - - - - - Qt::Horizontal - - - - Qt::Vertical - - - - - - - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Select random - - - - - - - - - Qt::Horizontal - - - - false - - - true - - - - - - Properties - - - - - false - - - Samples - - - - - - - - + + + + DrumPad + - - - - - - - - - + + + DJ + + + + + Synthisizer + @@ -329,30 +237,24 @@ - - SamplesWidget - QWidget -
sampleswidget.h
- 1 -
- - PresetDetailWidget - QScrollArea -
presetdetailwidget.h
- 1 -
- - SequencerWidget - QWidget -
sequencerwidget.h
- 1 -
DjWidget QWidget
djwidget.h
1
+ + DrumPadWidget + QWidget +
drumpadwidget.h
+ 1 +
+ + SynthisizerWidget + QWidget +
synthisizerwidget.h
+ 1 +
diff --git a/midiinwrapper.h b/midiinwrapper.h index 9f1c117..6f19f34 100755 --- a/midiinwrapper.h +++ b/midiinwrapper.h @@ -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); diff --git a/presetsmodel.cpp b/presetsmodel.cpp index 0c7e5d2..d710351 100755 --- a/presetsmodel.cpp +++ b/presetsmodel.cpp @@ -32,6 +32,11 @@ enum { NumberOfColumns }; +PresetsModel::PresetsModel(QObject *parent) : + QAbstractTableModel{parent} +{ +} + PresetsModel::PresetsModel(const std::map &presets, QObject *parent) : QAbstractTableModel{parent} { @@ -40,8 +45,44 @@ PresetsModel::PresetsModel(const std::map &presets, QO m_presets.emplace_back(pair.second); } +PresetsModel::PresetsModel(std::vector &&presets, QObject *parent) : + QAbstractTableModel{parent} +{ + m_presets = std::move(presets); +} + +PresetsModel::PresetsModel(const std::vector &presets, QObject *parent) : + QAbstractTableModel{parent} +{ + m_presets = presets; +} + PresetsModel::~PresetsModel() = default; +void PresetsModel::setPresets(const std::map &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) +{ + beginResetModel(); + m_presets = std::move(presets); + endResetModel(); +} + +void PresetsModel::setPresets(const std::vector &presets) +{ + beginResetModel(); + m_presets = presets; + endResetModel(); +} + const presets::Preset &PresetsModel::getPreset(const QModelIndex &index) const { return getPreset(index.row()); diff --git a/presetsmodel.h b/presetsmodel.h index ecbd711..8778dfc 100755 --- a/presetsmodel.h +++ b/presetsmodel.h @@ -11,9 +11,16 @@ class PresetsModel : public QAbstractTableModel Q_OBJECT public: + PresetsModel(QObject *parent = nullptr); PresetsModel(const std::map &presets, QObject *parent = nullptr); + PresetsModel(std::vector &&presets, QObject *parent = nullptr); + PresetsModel(const std::vector &presets, QObject *parent = nullptr); ~PresetsModel() override; + void setPresets(const std::map &presets); + void setPresets(std::vector &&presets); + void setPresets(const std::vector &presets); + const presets::Preset &getPreset(const QModelIndex &index) const; const presets::Preset &getPreset(int row) const; diff --git a/sampleswidget.cpp b/sampleswidget.cpp index 646b52d..245a5d7 100755 --- a/sampleswidget.cpp +++ b/sampleswidget.cpp @@ -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()) diff --git a/sampleswidget.h b/sampleswidget.h index b700f78..8917cb2 100755 --- a/sampleswidget.h +++ b/sampleswidget.h @@ -5,14 +5,13 @@ #include #include -#include -#include #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 m_ui; - QNetworkDiskCache m_cache; - QNetworkAccessManager m_networkAccessManager; - presets::Preset m_preset; }; diff --git a/samplewidget.cpp b/samplewidget.cpp index 951b6f7..1f60203 100755 --- a/samplewidget.cpp +++ b/samplewidget.cpp @@ -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; diff --git a/samplewidget.h b/samplewidget.h index 495d46f..a638f06 100755 --- a/samplewidget.h +++ b/samplewidget.h @@ -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 &device); void sendMidi(const midi::MidiMessage &midiMsg); -public slots: - void sendColor(); - private slots: void updateStatus(); void requestFinished(); diff --git a/samplewidget.ui b/samplewidget.ui index 28f4140..0ff0027 100755 --- a/samplewidget.ui +++ b/samplewidget.ui @@ -6,8 +6,8 @@ 0 0 - 400 - 300 + 140 + 157 @@ -63,8 +63,14 @@ + + + 32 + 16777215 + + - PushButton + @@ -119,9 +125,6 @@ QFrame::Sunken - - TextLabel - @@ -132,9 +135,6 @@ QFrame::Sunken - - TextLabel - @@ -145,9 +145,6 @@ QFrame::Sunken - - TextLabel - diff --git a/sequencerwidget.h b/sequencerwidget.h index d58f7dc..4614578 100755 --- a/sequencerwidget.h +++ b/sequencerwidget.h @@ -42,6 +42,6 @@ private: QTimer m_timer; - int m_pos; + int m_pos{}; std::array m_sampleLabels; }; diff --git a/synthisizer.cpp b/synthisizer.cpp index f740133..1021388 100644 --- a/synthisizer.cpp +++ b/synthisizer.cpp @@ -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; diff --git a/synthisizer.h b/synthisizer.h index 4d8a400..67b6f2d 100644 --- a/synthisizer.h +++ b/synthisizer.h @@ -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{}; diff --git a/synthisizerwidget.cpp b/synthisizerwidget.cpp new file mode 100644 index 0000000..df87c08 --- /dev/null +++ b/synthisizerwidget.cpp @@ -0,0 +1,33 @@ +#include "synthisizerwidget.h" +#include "ui_synthisizerwidget.h" + +SynthisizerWidget::SynthisizerWidget(QWidget *parent) : + QWidget{parent}, + m_ui{std::make_unique()} +{ + 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); +} diff --git a/synthisizerwidget.h b/synthisizerwidget.h new file mode 100644 index 0000000..4042e9c --- /dev/null +++ b/synthisizerwidget.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + +#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 m_ui; + + Synthisizer m_synthisizer; +}; diff --git a/synthisizerwidget.ui b/synthisizerwidget.ui new file mode 100644 index 0000000..ea3b59d --- /dev/null +++ b/synthisizerwidget.ui @@ -0,0 +1,32 @@ + + + SynthisizerWidget + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + 30 + 60 + 221 + 31 + + + + Hier könnte ihr Synthisizer stehen. + + + + + +