misc UI and audio engine things

portability/boost
zetaPRIME 2019-01-09 19:53:24 -05:00
parent bb973babc9
commit fc93dc0519
7 changed files with 55 additions and 23 deletions

7
notes
View File

@ -50,7 +50,10 @@ TODO {
- ^ channel-column splitting (partial channel copy/paste)
- pattern properties dialog (name, length, time signature)
probably move the "process all nodes" part of tick processing into its own function?
- probably move the "process all nodes" part of tick processing into its own function?
- set default suffix on save-as dialog
- have the graph scene actually keep track of keys-to-notes on preview (like pattern editor)
multithreaded audio
^ audio engine invokes workers, then QThread::wait()s on them
@ -62,7 +65,7 @@ TODO {
}
- make short connections look less wonky
misc features needed before proper release {
song metadata (title, artist, comment, default bpm)

View File

@ -31,7 +31,7 @@ void AudioEngine::init() {
// and off to the races
thread->start();
//thread->setPriority(QThread::TimeCriticalPriority);
thread->setPriority(QThread::TimeCriticalPriority);
QMetaObject::invokeMethod(audioEngine, &AudioEngine::postInit, Qt::QueuedConnection);
}
void AudioEngine::postInit() {
@ -155,7 +155,7 @@ void AudioEngine::preview(std::shared_ptr<Project> p, int16_t port, int16_t note
mode = Previewing;
emit this->playbackModeChanged();
}
if (port >= 0 && port <= 255) previewPort = static_cast<uint8_t>(port); // assign port if valid
if (port >= 0 && port <= 255 && state) previewPort_ = static_cast<uint8_t>(port); // assign port if valid (and note on)
if (note < 0) return; // invalid note (port is set before it so that setting the port can be a separate action)
// assemble message
@ -251,8 +251,7 @@ void AudioEngine::nextTick() {
tickBufPtr = tickBuf.get();
tickId++;
// (sample rate / seconds per beat) / ticks per beat
double tickSize = (1.0 * sampleRate / (static_cast<double>(tempo)/60.0)) / (4*6);
double tickSize = 0.005 * sampleRate; // 5ms fixed tick size for preview
tickSize += tickAcc; // add sample remainder from last tick
double tickSf = std::floor(tickSize);
tickAcc = tickSize - tickSf;
@ -263,12 +262,12 @@ void AudioEngine::nextTick() {
if (!queueValid) buildQueue();
// TODO: send previewing commands
if (auto p = std::static_pointer_cast<CommandPort>(project->rootGraph->port(Port::Input, Port::Command, previewPort)); p) {
if (auto p = std::static_pointer_cast<CommandPort>(project->rootGraph->port(Port::Input, Port::Command, previewPort_)); p) {
p->push(buf);
}
buf.clear();
for (auto n : queue) if (!n->try_process()) qWarning() << "Dependency check failed in single threaded mode!";
processNodes();
if (auto p = std::static_pointer_cast<AudioPort>(project->rootGraph->port(Port::Output, Port::Audio, 0)); p) {
p->pull();
size_t bufs = ts * sizeof(float);
@ -447,7 +446,7 @@ void AudioEngine::nextTick() {
buffer[0].resize(ts);
buffer[1].resize(ts);
for (auto n : queue) if (!n->try_process()) qWarning() << "Dependency check failed in single threaded mode!";
processNodes();
if (auto p = std::static_pointer_cast<AudioPort>(project->rootGraph->port(Port::Output, Port::Audio, 0)); p) {
p->pull();
size_t bufs = ts * sizeof(float);
@ -473,3 +472,7 @@ void AudioEngine::nextTick() {
}
}
}
void AudioEngine::processNodes() {
for (auto n : queue) if (!n->try_process()) qWarning() << "Dependency check failed in single threaded mode!";
}

View File

@ -65,7 +65,7 @@ namespace Xybrid::Audio {
std::unordered_map<std::string*, NoteInfo, PointerCompare<std::string>, PointerCompare<std::string>> nameTrack;
std::vector<uint8_t> buf; /// preallocated buffer for building commands
uint8_t previewPort = 0;
uint8_t previewPort_ = 0;
// playback timing and position
float tempo = 140.0;
@ -78,6 +78,7 @@ namespace Xybrid::Audio {
void initAudio(bool startNow = false);
void deinitAudio();
void nextTick();
void processNodes();
public:
static void init();
inline constexpr PlaybackMode playbackMode() const { return mode; }
@ -85,6 +86,7 @@ namespace Xybrid::Audio {
void play(std::shared_ptr<Data::Project>);
void stop();
void preview(std::shared_ptr<Data::Project>, int16_t port, int16_t note, bool state);
inline uint8_t previewPort() const { return previewPort_; }
inline void invalidateQueue(Data::Project* p) { if (p == project.get()) queueValid = false; }

View File

@ -59,7 +59,7 @@ using namespace Xybrid::Config;
using namespace Xybrid::Audio;
namespace {
constexpr const auto projectFilter = u8"Xybrid project (*.xyp)\nAll files (*)";
constexpr const auto projectFilter = u8"Xybrid project (*.xyp);;All files (*)";
}
MainWindow::MainWindow(QWidget *parent) :
@ -381,8 +381,11 @@ void MainWindow::menuFileSave() {
}
void MainWindow::menuFileSaveAs() {
auto fileName = QFileDialog::getSaveFileName(this, "Save project as...", QString(), projectFilter);
if (fileName.isEmpty()) return; // canceled
QFileDialog dlg(this, "Save project as...", QString(), projectFilter);
dlg.setDefaultSuffix("xyp");
dlg.setFileMode(QFileDialog::AnyFile);
if (!dlg.exec()) return; // canceled
auto fileName = dlg.selectedFiles()[0];
FileOps::saveProject(project, fileName);
undoStack->setClean();
updateTitle();

View File

@ -359,6 +359,9 @@ void NodeObject::paint(QPainter* painter, const QStyleOptionGraphicsItem* opt, Q
painter->drawRoundedRect(r, 8, 8);
if (showName) {
QFont f = painter->font();
f.setPointSizeF(9);
painter->setFont(f);
QRectF tr = r - QMarginsF(edgePad, edgePad - 1, edgePad, 0);
if (!node->name.empty()) {
painter->setPen(QColor(222, 222, 222));
@ -374,7 +377,7 @@ void NodeObject::paint(QPainter* painter, const QStyleOptionGraphicsItem* opt, Q
QRectF NodeObject::boundingRect() const {
if (customChrome) return QRectF(QPointF(0, 0), gadgetSize_);
if (gadgetSize_.isNull()) return QRectF(0, 0, 192, 36);// + QMarginsF(8, 8, 8, 8);
if (gadgetSize_.isNull()) return QRectF(0, 0, 128+32, 36);// + QMarginsF(8, 8, 8, 8);
if (showName) return QRectF(QPointF(), gadgetSize_ + QPointF(edgePad * 2, edgePad * 2 + nameSize()));
return QRectF(QPointF(), gadgetSize_ + QPointF(edgePad * 2, edgePad * 2));
}
@ -462,6 +465,10 @@ QPainterPath PortConnectionObject::shape(qreal width) const {
QPointF out(0, 0);
//path.lineTo(start+out);
QPointF mod(std::max((end.x() - start.x()) * .64, 96.0), 0);
if (auto vdist = std::fabs(end.y() - start.y()), hdist = fabs(end.x() - start.x()) * 0.75; hdist < 96.0 && vdist < 96.0) {
double p = std::max(vdist, hdist) / 96.0;
mod = QPointF(mod.x() * p, 0);
}
path.cubicTo(start + out + mod, end - mod, end - out);
//path.lineTo(end);

View File

@ -78,22 +78,30 @@ void PatchboardScene::keyPressEvent(QKeyEvent* e) {
if (!e->isAccepted() && !e->isAutoRepeat()) {
auto note = Util::keyToNote(e->key());
if (note >= 0) {
auto p = graph->project->shared_from_this();
if (e->modifiers() & Qt::Modifier::SHIFT) note += 24;
audioEngine->preview(p, -1, note, true);
startPreview(e->key(), note);
}
}
}
void PatchboardScene::keyReleaseEvent(QKeyEvent* e) {
QGraphicsScene::keyReleaseEvent(e);
if (!e->isAccepted() && !e->isAutoRepeat()) {
auto note = Util::keyToNote(e->key());
if (note >= 0) {
auto p = graph->project->shared_from_this();
audioEngine->preview(p, -1, note, false);
audioEngine->preview(p, -1, note+24, false);
}
if (!e->isAccepted() && !e->isAutoRepeat()) stopPreview(e->key());
}
void PatchboardScene::startPreview(int key, int16_t note) {
stopPreview(key); // end current preview first, if applicable
auto p = graph->project->shared_from_this();
audioEngine->preview(p, -1, note, true);
previewKey[key] = {audioEngine->previewPort(), note};
}
void PatchboardScene::stopPreview(int key) {
if (auto k = previewKey.find(key); k != previewKey.end()) {
auto p = graph->project->shared_from_this();
audioEngine->preview(p, k->second[0], k->second[1], false);
previewKey.erase(k);
}
}

View File

@ -1,6 +1,7 @@
#pragma once
#include <memory>
#include <unordered_map>
#include <QGraphicsScene>
#include <QGraphicsView>
@ -12,10 +13,15 @@ namespace Xybrid::UI {
std::shared_ptr<Data::Graph> graph;
QGraphicsView* view;
std::unordered_map<int, std::array<int16_t, 2>> previewKey;
bool resizeQueued = false;
void queueResize();
void autoSetSize();
void startPreview(int, int16_t);
void stopPreview(int);
public:
PatchboardScene(QGraphicsView* view, const std::shared_ptr<Data::Graph>& graph);
~PatchboardScene() override = default;