Implemented loopstation widget sample downloading
This commit is contained in:
@ -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{
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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{};
|
||||
};
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
|
Reference in New Issue
Block a user