Implemented loopstation widget sample downloading

This commit is contained in:
2022-12-29 00:26:57 +01:00
parent d1db1a05fd
commit c843b39110
9 changed files with 272 additions and 17 deletions

View File

@ -196,6 +196,16 @@ void DrumMachineSettings::setLoopstationNextPreset(const MidiLearnSetting &value
setLearnSetting("loopstation/nextPreset", value);
}
MidiLearnSetting DrumMachineSettings::loopstationSample(quint8 pad) const
{
return learnSetting(QString{"loopstation/pad%0"}.arg(pad));
}
void DrumMachineSettings::setLoopstationSample(quint8 pad, const MidiLearnSetting &value)
{
setLearnSetting(QString{"loopstation/pad%0"}.arg(pad), value);
}
MidiLearnSetting DrumMachineSettings::learnSetting(const QString &key) const
{
return MidiLearnSetting{

View File

@ -69,6 +69,9 @@ public:
MidiLearnSetting loopstationNextPreset() const;
void setLoopstationNextPreset(const MidiLearnSetting &value);
MidiLearnSetting loopstationSample(quint8 pad) const;
void setLoopstationSample(quint8 pad, const MidiLearnSetting &value);
private:
MidiLearnSetting learnSetting(const QString &key) const;
void setLearnSetting(const QString &key, const MidiLearnSetting &value);

View File

@ -52,9 +52,7 @@ void DrumPadSamplesWidget::midiReceived(const midi::MidiMessage &message)
}
for (DrumPadSampleWidget &widget : getWidgets())
{
widget.midiReceived(message);
}
}
void DrumPadSamplesWidget::writeSamples(frame_t *begin, frame_t *end)

View File

@ -2,7 +2,6 @@
#include "ui_drumpadsamplewidget.h"
#include <QAbstractEventDispatcher>
#include <QSoundEffect>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QNetworkAccessManager>
@ -17,7 +16,7 @@ namespace {
QString toString(QString value) { return value; }
QString toString(int value) { return QString::number(value); }
QString toString(bool value) { return value?"true":"false"; }
}
} // namespace
DrumPadSampleWidget::DrumPadSampleWidget(QWidget *parent) :
QFrame{parent},
@ -212,7 +211,6 @@ void DrumPadSampleWidget::updateStatus()
{
QPalette pal;
if (m_file && m_file->color && m_player.buffer().isValid())
{
const auto bright = m_player.playing() ? 255 : 155;
@ -264,9 +262,7 @@ void DrumPadSampleWidget::updateStatus()
void DrumPadSampleWidget::requestFinished()
{
if (m_reply->error() == QNetworkReply::NoError)
{
emit startDecoding(m_reply);
}
updateStatus();
}

View File

@ -1,54 +1,82 @@
#include "loopstationsampleswidget.h"
#include "ui_loopstationsampleswidget.h"
#include "loopstationpresets.h"
LoopStationSamplesWidget::LoopStationSamplesWidget(QWidget *parent) :
QWidget{parent},
m_ui{std::make_unique<Ui::LoopStationSamplesWidget>()}
{
m_ui->setupUi(this);
quint8 padNr{};
for (LoopStationSampleWidget &widget : getWidgets())
{
widget.setPadNr(padNr++);
connect(&widget, &LoopStationSampleWidget::sendMidi, this, &LoopStationSamplesWidget::sendMidi);
}
}
LoopStationSamplesWidget::~LoopStationSamplesWidget() = default;
void LoopStationSamplesWidget::loadSettings(DrumMachineSettings &settings)
{
Q_UNUSED(settings)
for (LoopStationSampleWidget &widget : getWidgets())
widget.loadSettings(settings);
}
void LoopStationSamplesWidget::setPreset(const loopstation_presets::Preset &preset)
{
Q_UNUSED(preset)
assert(preset.id);
assert(preset.pads);
const auto &presetId = *preset.id;
const auto &widgets = getWidgets();
const auto &pads = *preset.pads;
auto iter = std::begin(widgets);
auto iter2 = std::begin(pads);
int i{};
for (; iter != std::end(widgets) && iter2 != std::end(pads); iter++, iter2++)
{
((*iter)).get().setSample(presetId, QString{"%0_%1.wav"}.arg(presetId).arg(++i, 2, 10, QLatin1Char('0')), *iter2);
}
}
void LoopStationSamplesWidget::midiReceived(const midi::MidiMessage &message)
{
Q_UNUSED(message)
for (LoopStationSampleWidget &widget : getWidgets())
widget.midiReceived(message);
}
void LoopStationSamplesWidget::writeSamples(frame_t *begin, frame_t *end)
{
Q_UNUSED(begin)
Q_UNUSED(end)
for (LoopStationSampleWidget &widget : getWidgets())
widget.writeSamples(begin, end);
}
void LoopStationSamplesWidget::injectNetworkAccessManager(QNetworkAccessManager &networkAccessManager)
{
Q_UNUSED(networkAccessManager)
for (LoopStationSampleWidget &widget : getWidgets())
widget.injectNetworkAccessManager(networkAccessManager);
}
void LoopStationSamplesWidget::injectDecodingThread(QThread &thread)
{
Q_UNUSED(thread)
for (LoopStationSampleWidget &widget : getWidgets())
widget.injectDecodingThread(thread);
}
void LoopStationSamplesWidget::unsendColors()
{
for (LoopStationSampleWidget &widget : getWidgets())
widget.unsendColor();
}
void LoopStationSamplesWidget::sendColors()
{
for (LoopStationSampleWidget &widget : getWidgets())
widget.sendColor();
}
std::array<std::reference_wrapper<LoopStationSampleWidget>, 48> LoopStationSamplesWidget::getWidgets()

View File

@ -1,11 +1,166 @@
#include "loopstationsamplewidget.h"
#include "ui_loopstationsamplewidget.h"
#include <QAbstractEventDispatcher>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QMetaEnum>
#include "audioformat.h"
#include "audiodecoder.h"
#include "drummachinesettings.h"
#include "midicontainers.h"
LoopStationSampleWidget::LoopStationSampleWidget(QWidget *parent) :
QFrame{parent},
m_ui{std::make_unique<Ui::LoopStationSampleWidget>()}
{
m_ui->setupUi(this);
connect(&m_player, &AudioPlayer::playingChanged, this, &LoopStationSampleWidget::updateStatus);
connect(m_ui->pushButtonPlay, &QAbstractButton::pressed, this, &LoopStationSampleWidget::pressed);
updateStatus();
}
LoopStationSampleWidget::~LoopStationSampleWidget() = default;
void LoopStationSampleWidget::loadSettings(DrumMachineSettings &settings)
{
m_settings = &settings;
m_ui->pushButtonPlay->setLearnSetting(m_settings->loopstationSample(m_padNr));
connect(m_ui->pushButtonPlay, &MidiButton::learnSettingChanged, this, [this](const MidiLearnSetting &learnSetting){
Q_ASSERT(m_settings);
if (m_settings)
m_settings->setLoopstationSample(m_padNr, learnSetting);
else
qWarning() << "no settings available";
});
}
void LoopStationSampleWidget::setSample(const QString &presetId, const QString &filename, const QString &type)
{
m_presetId = presetId;
m_filename = filename;
m_player.setBuffer({});
startRequest();
m_ui->labelFilename->setText(filename);
m_ui->labelType->setText(type);
}
void LoopStationSampleWidget::pressed()
{
m_player.restart();
}
void LoopStationSampleWidget::injectNetworkAccessManager(QNetworkAccessManager &networkAccessManager)
{
m_networkAccessManager = &networkAccessManager;
if (!m_presetId.isEmpty() && !m_filename.isEmpty())
startRequest();
}
void LoopStationSampleWidget::injectDecodingThread(QThread &thread)
{
QMetaObject::invokeMethod(QAbstractEventDispatcher::instance(&thread), [this](){
m_decoder = std::make_unique<AudioDecoder>();
connect(this, &LoopStationSampleWidget::startDecoding, m_decoder.get(), &AudioDecoder::startDecodingDevice);
connect(m_decoder.get(), &AudioDecoder::decodingFinished, this, &LoopStationSampleWidget::decodingFinished);
if (m_reply && m_reply->isFinished() && m_reply->error() == QNetworkReply::NoError)
m_decoder->startDecodingDevice(m_reply);
});
}
void LoopStationSampleWidget::writeSamples(frame_t *begin, frame_t *end)
{
m_player.writeSamples(begin, end);
}
void LoopStationSampleWidget::midiReceived(const midi::MidiMessage &message)
{
m_ui->pushButtonPlay->midiReceived(message);
}
void LoopStationSampleWidget::unsendColor()
{
m_sendColors = false;
emit sendMidi(midi::MidiMessage {
.channel = m_ui->pushButtonPlay->learnSetting().channel,
.cmd = m_ui->pushButtonPlay->learnSetting().cmd,
.flag = true,
.note = m_ui->pushButtonPlay->learnSetting().note,
.velocity = 0
});
}
void LoopStationSampleWidget::sendColor()
{
m_sendColors = true;
emit sendMidi(midi::MidiMessage {
.channel = m_ui->pushButtonPlay->learnSetting().channel,
.cmd = m_ui->pushButtonPlay->learnSetting().cmd,
.flag = true,
.note = m_ui->pushButtonPlay->learnSetting().note,
.velocity = uint8_t(m_padNr+1)
});
}
void LoopStationSampleWidget::updateStatus()
{
if (m_sendColors)
sendColor();
if (m_reply)
{
if (!m_reply->isFinished())
m_ui->labelStatus->setText(tr("Downloading..."));
else if (m_reply->error() != QNetworkReply::NoError)
m_ui->labelStatus->setText(QMetaEnum::fromType<QNetworkReply::NetworkError>().valueToKey(m_reply->error()));
else
{
if (!m_decoder)
m_ui->labelStatus->setText(tr("Waiting for decoder thread..."));
else
m_ui->labelStatus->setText(tr("Decoding"));
}
}
else
m_ui->labelStatus->setText(m_player.playing() ? tr("Playing") : tr("Ready"));
}
void LoopStationSampleWidget::requestFinished()
{
if (m_reply->error() == QNetworkReply::NoError)
emit startDecoding(m_reply);
updateStatus();
}
void LoopStationSampleWidget::decodingFinished(const QAudioBuffer &buffer)
{
m_reply = nullptr;
m_player.setBuffer(buffer);
updateStatus();
}
void LoopStationSampleWidget::startRequest()
{
if (m_networkAccessManager && !m_presetId.isEmpty() && !m_filename.isEmpty())
{
QNetworkRequest request{QUrl{QString{"https://brunner.ninja/komposthaufen/groovepad/presets/extracted/%0/%1"}.arg(m_presetId, m_filename)}};
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
request.setAttribute(QNetworkRequest::CacheSaveControlAttribute, true);
m_reply = std::shared_ptr<QNetworkReply>{m_networkAccessManager->get(request)};
connect(m_reply.get(), &QNetworkReply::finished, this, &LoopStationSampleWidget::requestFinished);
}
updateStatus();
}

View File

@ -4,7 +4,16 @@
#include <memory>
#include "audioplayer.h"
namespace Ui { class LoopStationSampleWidget; }
class QNetworkAccessManager;
class QNetworkReply;
class QAudioBuffer;
class AudioDecoder;
class DrumMachineSettings;
namespace midi { struct MidiMessage; }
struct frame_t;
class LoopStationSampleWidget : public QFrame
{
@ -12,8 +21,55 @@ class LoopStationSampleWidget : public QFrame
public:
explicit LoopStationSampleWidget(QWidget *parent = nullptr);
~LoopStationSampleWidget();
~LoopStationSampleWidget() override;
quint8 padNr() const { return m_padNr; }
void setPadNr(quint8 padNr) { m_padNr = padNr; }
void loadSettings(DrumMachineSettings &settings);
void setSample(const QString &presetId, const QString &filename, const QString &type);
void pressed();
void injectNetworkAccessManager(QNetworkAccessManager &networkAccessManager);
void injectDecodingThread(QThread &thread);
void writeSamples(frame_t *begin, frame_t *end);
void midiReceived(const midi::MidiMessage &message);
void unsendColor();
void sendColor();
signals:
void startDecoding(std::shared_ptr<QIODevice> device);
void sendMidi(const midi::MidiMessage &midiMsg);
private slots:
void updateStatus();
void requestFinished();
void decodingFinished(const QAudioBuffer &buffer);
private:
void startRequest();
const std::unique_ptr<Ui::LoopStationSampleWidget> m_ui;
DrumMachineSettings *m_settings{};
std::shared_ptr<QNetworkReply> m_reply;
std::unique_ptr<AudioDecoder> m_decoder;
AudioPlayer m_player;
QString m_presetId;
QString m_filename;
QNetworkAccessManager *m_networkAccessManager{};
quint8 m_padNr{};
bool m_sendColors{};
};

View File

@ -51,6 +51,13 @@
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelType">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>

View File

@ -22,6 +22,8 @@ LoopStationWidget::LoopStationWidget(QWidget *parent) :
connect(m_ui->pushButtonDown, &QAbstractButton::pressed, this, &LoopStationWidget::selectNextPreset);
connect(m_ui->pushButtonRefresh, &QAbstractButton::pressed, this, &LoopStationWidget::loadPresets);
connect(m_ui->samplesWidget, &LoopStationSamplesWidget::sendMidi, this, &LoopStationWidget::sendMidi);
m_presetsProxyModel.setFilterCaseSensitivity(Qt::CaseInsensitive);
m_presetsProxyModel.setSortRole(Qt::EditRole);
m_presetsProxyModel.setSourceModel(&m_presetsModel);