Files
DrumMachine/widgets/loopstationsamplewidget.cpp

281 lines
8.3 KiB
C++

#include "loopstationsamplewidget.h"
#include "ui_loopstationsamplewidget.h"
#include <QAbstractEventDispatcher>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QMetaEnum>
#include <QPalette>
#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_player{this}
{
m_ui->setupUi(this);
connect(&m_player, &AudioPlayer::playingChanged, this, &LoopStationSampleWidget::updateStatus);
connect(m_ui->pushButtonPlay, &QAbstractButton::toggled, this, [this](bool toggled){
emit loopEnabledChanged(toggled, m_category);
});
updateStatus();
}
LoopStationSampleWidget::~LoopStationSampleWidget() = default;
void LoopStationSampleWidget::setCategory(quint8 category)
{
m_category = category;
m_ui->labelCategory->setText(QString::number(category));
updateStatus();
}
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::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;
const quint8 color = m_settings ? m_settings->colorOff() : quint8{0};
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 = color
});
m_lastMidiColor = color;
}
void LoopStationSampleWidget::sendColor(bool force)
{
m_sendColors = true;
quint8 newColor;
if (false) // testing colors on launchpad mk2
newColor = m_padNr + 48;
else
{
if (m_player.buffer().isValid())
{
if (m_category == 0)
newColor = m_player.playing() ? 44 : 47; //dunkelblue
else if (m_category == 1)
newColor = m_player.playing() ? 16 : 19; // green
else if (m_category == 2)
newColor = m_player.playing() ? 48 : 51; // violet
else if (m_category == 3)
newColor = m_player.playing() ? 36 : 39; // hellblue
else if (m_category == 4)
newColor = m_player.playing() ? 8 : 11; // orange
else if (m_category == 5)
newColor = m_player.playing() ? 52 : 55; // pink
else
newColor = m_player.playing() ? 4 : 7; // red
}
else
{
newColor = 0;
}
}
if (force || newColor != m_lastMidiColor)
{
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 = newColor
});
m_lastMidiColor = newColor;
}
}
bool LoopStationSampleWidget::loopEnabled() const
{
return m_ui->pushButtonPlay->isChecked();
}
void LoopStationSampleWidget::timeout()
{
if (m_ui->pushButtonPlay->isChecked())
{
// if is not playing or position > 80%
if (!m_player.playing() || m_player.position() >= m_player.buffer().frameCount() * 8 / 10)
m_player.restart();
}
else
{
// if is playing and position < 60%
if (m_player.playing() && m_player.position() < m_player.buffer().frameCount() * 6 / 10)
m_player.stop();
}
}
void LoopStationSampleWidget::setLoopEnabled(bool enabled)
{
m_ui->pushButtonPlay->setChecked(enabled);
}
void LoopStationSampleWidget::stop()
{
m_player.stop();
}
void LoopStationSampleWidget::updateStatus()
{
QColor newColor;
if (m_player.buffer().isValid())
{
const auto bright = m_player.playing() ? 255 : 155;
const auto dark = m_player.playing() ?
#if !defined(Q_OS_WIN)
80 : 0
#else
180 : 80
#endif
;
if (m_category == 0)
newColor = QColor{bright, dark, bright};
else if (m_category == 1)
newColor = QColor{bright, dark, dark};
else if (m_category == 2)
newColor = QColor{bright, bright, dark};
else if (m_category == 3)
newColor = QColor{dark, bright, dark};
else if (m_category == 4)
newColor = QColor{dark, dark, bright};
else if (m_category == 5)
newColor = QColor{dark, bright, bright};
else
{
qWarning() << "unknown category:" << m_category;
newColor = QColor{dark, dark, dark};
}
}
if (newColor.isValid() && (!m_lastColor.isValid() || newColor != m_lastColor))
{
QPalette pal;
pal.setColor(QPalette::Window, newColor);
m_lastColor = newColor;
setPalette(pal);
}
if (m_sendColors)
sendColor(false);
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();
}