debug optimization, connection guards, playback fixes and UI hookups

portability/boost
zetaPRIME 2018-12-19 10:18:32 -05:00
parent 6a091c5ea6
commit 76568244ff
9 changed files with 73 additions and 34 deletions

View File

@ -3,6 +3,9 @@
using namespace Xybrid::Audio;
using namespace Xybrid::Data;
#include "mainwindow.h"
#include "uisocket.h"
#include <algorithm>
#include <cmath>
@ -88,7 +91,10 @@ void AudioEngine::play(std::shared_ptr<Project> p) {
// stop and reset, then init playback
initAudio();
for (auto& b : buffer) b.reserve(static_cast<size_t>(sampleRate/4));
for (auto& b : buffer) {
b.clear();
b.reserve(static_cast<size_t>(sampleRate/4));
}
seqPos = -1;
tempo = project->tempo;
@ -99,6 +105,7 @@ void AudioEngine::play(std::shared_ptr<Project> p) {
//tickId = 0; // actually, no reason to reset this
mode = Playing;
emit this->playbackModeChanged(mode);
}, Qt::QueuedConnection);
}
@ -107,6 +114,7 @@ void AudioEngine::stop() {
project = nullptr;
deinitAudio();
mode = Stopped;
emit this->playbackModeChanged(mode);
}, Qt::QueuedConnection);
}
@ -182,6 +190,8 @@ void AudioEngine::nextTick() {
curTick = 0;
curRow++;
if (!p || curRow >= p->rows) advanceSeq();
MainWindow* w = project->socket->window;
QMetaObject::invokeMethod(w, [this, w]{ w->playbackPosition(seqPos, curRow); }, Qt::QueuedConnection);
};
curTick++;
@ -196,15 +206,13 @@ void AudioEngine::nextTick() {
size_t ts = static_cast<size_t>(tickSf);
buffer[0].resize(ts);
buffer[1].resize(ts);
qDebug() << "tick" << tickId << "contains"<<ts<<"samples";
//qDebug() << "tick" << tickId << "contains"<<ts<<"samples";
// test
const double PI = std::atan(1)*4;
const double SEMI = std::pow(2.0, 1.0/12.0);
double time = 0;
//int note = static_cast<int>(random() % 5);
static int note = -1;
note = (note+1) % 6;
int note = curRow % 4;
for (size_t i = 0; i < ts; i++) {
buffer[0][i] = static_cast<float>(std::sin(time * PI*2 * 440 * std::pow(SEMI, -6 + note * 5)) * .25);

View File

@ -5,6 +5,9 @@ using Xybrid::Data::Pattern;
#include "data/graph.h"
using Xybrid::Data::Graph;
#include "audio/audioengine.h"
using namespace Xybrid::Audio;
Project::Project() {
rootGraph = std::make_shared<Graph>();
}
@ -14,6 +17,10 @@ Project::~Project() {
for (auto& pat : patterns) pat->project = nullptr;
}
bool Project::editingLocked() {
return (audioEngine->playingProject().get() == this && (audioEngine->playbackMode() == AudioEngine::Playing || audioEngine->playbackMode() == AudioEngine::Paused));
}
void Project::updatePatternIndices() {
for (size_t i = 0; i < patterns.size(); i++) patterns[i]->index = i;
}

View File

@ -19,7 +19,7 @@ namespace Xybrid::Data {
class Graph;
class Project {
public:
bool editingLocked = false;
bool editingLocked();
UISocket* socket;

View File

@ -50,7 +50,7 @@ MainWindow::MainWindow(QWidget *parent) :
undoStack = new QUndoStack(this);
//undoStack->setUndoLimit(256);
connect(undoStack, &QUndoStack::cleanChanged, [this](bool) {
connect(undoStack, &QUndoStack::cleanChanged, this, [this](bool) {
updateTitle();
});
@ -69,7 +69,7 @@ MainWindow::MainWindow(QWidget *parent) :
// prevent right pane of pattern view from being collapsed
ui->patternViewSplitter->setCollapsible(1, false);
connect(ui->patternViewSplitter, &QSplitter::splitterMoved, [this](int, int) {
connect(ui->patternViewSplitter, &QSplitter::splitterMoved, this, [this](int, int) {
// and when the list is collapsed, make sure header size is updated
ui->patternEditor->updateHeader();
});
@ -80,33 +80,33 @@ MainWindow::MainWindow(QWidget *parent) :
// events
// on selection change
connect(ui->patternList->selectionModel(), &QItemSelectionModel::currentChanged, [this](const QModelIndex& index, const QModelIndex& old) {
connect(ui->patternList->selectionModel(), &QItemSelectionModel::currentChanged, this, [this](const QModelIndex& index, const QModelIndex& old) {
if (index == old) return; // no actual change
size_t idx = static_cast<size_t>(index.row());
if (idx >= project->patterns.size()) return;
this->selectPatternForEditing(project->patterns[idx].get());
});
// on click
connect(ui->patternList, &QListView::clicked, [this]() { // deselect on sequencer when list clicked
connect(ui->patternList, &QListView::clicked, this, [this]() { // deselect on sequencer when list clicked
ui->patternSequencer->setCurrentIndex(ui->patternSequencer->model()->index(0, -1));
});
// rightclick menu
connect(ui->patternList, &QListView::customContextMenuRequested, [this](const QPoint& pt) {
connect(ui->patternList, &QListView::customContextMenuRequested, this, [this](const QPoint& pt) {
size_t idx = static_cast<size_t>(ui->patternList->indexAt(pt).row());
std::shared_ptr<Pattern> p = nullptr;
if (idx < project->patterns.size()) p = project->patterns[idx];
QMenu* menu = new QMenu(this);
menu->addAction("New Pattern", [this, idx]() {
menu->addAction("New Pattern", this, [this, idx]() {
(new ProjectPatternAddCommand(project, static_cast<int>(idx)))->commit();
});
if (p) {
menu->addAction("Duplicate Pattern", [this, p, idx]() {
menu->addAction("Duplicate Pattern", this, [this, p, idx]() {
(new ProjectPatternAddCommand(project, static_cast<int>(idx) + 1, -1, p))->commit();
});
menu->addSeparator();
menu->addAction("Delete Pattern", [this, p]() {
menu->addAction("Delete Pattern", this, [this, p]() {
if (QMessageBox::warning(this, "Are you sure?", QString("Remove pattern %1?").arg(Util::numAndName(p->index, p->name)), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) return;
(new ProjectPatternDeleteCommand(project, p))->commit();
});
@ -126,18 +126,18 @@ MainWindow::MainWindow(QWidget *parent) :
// events
// on selection change
connect(ui->patternSequencer->selectionModel(), &QItemSelectionModel::currentChanged, [this](const QModelIndex& index, const QModelIndex&) {
connect(ui->patternSequencer->selectionModel(), &QItemSelectionModel::currentChanged, this, [this](const QModelIndex& index, const QModelIndex&) {
size_t idx = static_cast<size_t>(index.column());
if (idx >= project->sequence.size()) return;
this->selectPatternForEditing(project->sequence[idx]);
});
// rightclick menu
connect(ui->patternSequencer, &QTableView::customContextMenuRequested, [this](const QPoint& pt) {
connect(ui->patternSequencer, &QTableView::customContextMenuRequested, this, [this](const QPoint& pt) {
size_t idx = static_cast<size_t>(ui->patternSequencer->indexAt(pt).column());
QMenu* menu = new QMenu(this);
menu->addAction("Insert Pattern", [this, idx]() {
menu->addAction("Insert Pattern", this, [this, idx]() {
if (!editingPattern->validFor(project)) return; // nope
int si = static_cast<int>(std::min(idx, project->sequence.size()));
auto* c = new ProjectSequencerDeltaCommand(project);
@ -145,26 +145,26 @@ MainWindow::MainWindow(QWidget *parent) :
c->seqSel = si+1;
c->commit();
});
menu->addAction("Insert Separator", [this, idx]() {
menu->addAction("Insert Separator", this, [this, idx]() {
int si = static_cast<int>(std::min(idx, project->sequence.size()));
auto* c = new ProjectSequencerDeltaCommand(project);
c->seq.insert(c->seq.begin() + si, nullptr);
c->seqSel = si+1;
c->commit();
});
if (idx < project->sequence.size()) menu->addAction("Remove", [this, idx]() {
if (idx < project->sequence.size()) menu->addAction("Remove", this, [this, idx]() {
auto* c = new ProjectSequencerDeltaCommand(project);
c->seq.erase(c->seq.begin() + static_cast<ptrdiff_t>(idx));
c->seqSel = static_cast<int>(idx)-1;
c->commit();
});
menu->addSeparator();
menu->addAction("Create New Pattern", [this, idx]() {
menu->addAction("Create New Pattern", this, [this, idx]() {
int si = static_cast<int>(std::min(idx, project->sequence.size()));
(new ProjectPatternAddCommand(project, -1, si))->commit();
});
if (idx < project->sequence.size() && project->sequence[idx]) {
menu->addAction("Duplicate Pattern", [this, idx, p = project->patterns[project->sequence[idx]->index]]() {
menu->addAction("Duplicate Pattern", this, [this, idx, p = project->patterns[project->sequence[idx]->index]]() {
int si = static_cast<int>(std::min(idx + 1, project->sequence.size()));
(new ProjectPatternAddCommand(project, static_cast<int>(p->index) + 1, si, p))->commit();
});
@ -176,7 +176,7 @@ MainWindow::MainWindow(QWidget *parent) :
{ /* Set up keyboard shortcuts for pattern view */ } {
// Ctrl+PgUp/Down - previous or next pattern in sequencer
connect(new QShortcut(QKeySequence("Ctrl+PgUp"), ui->pattern), &QShortcut::activated, [this]() {
connect(new QShortcut(QKeySequence("Ctrl+PgUp"), ui->pattern), &QShortcut::activated, this, [this]() {
auto i = ui->patternSequencer->currentIndex();
if (!i.isValid()) {
ui->patternSequencer->setCurrentIndex(ui->patternSequencer->model()->index(ui->patternSequencer->horizontalHeader()->count() - 1, 0));
@ -185,7 +185,7 @@ MainWindow::MainWindow(QWidget *parent) :
auto count = ui->patternSequencer->horizontalHeader()->count();
ui->patternSequencer->setCurrentIndex(i.siblingAtColumn((count + i.column() - 1) % count));
});
connect(new QShortcut(QKeySequence("Ctrl+PgDown"), ui->pattern), &QShortcut::activated, [this]() {
connect(new QShortcut(QKeySequence("Ctrl+PgDown"), ui->pattern), &QShortcut::activated, this, [this]() {
auto i = ui->patternSequencer->currentIndex();
if (!i.isValid()) {
ui->patternSequencer->setCurrentIndex(ui->patternSequencer->model()->index(0, 0));
@ -196,13 +196,13 @@ MainWindow::MainWindow(QWidget *parent) :
});
// TEMP - play/stop
connect(new QShortcut(QKeySequence("Ctrl+P"), ui->pattern), &QShortcut::activated, [this]() {
connect(new QShortcut(QKeySequence("Ctrl+P"), ui->pattern), &QShortcut::activated, this, [this]() {
if (audioEngine->playbackMode() == AudioEngine::Playing) audioEngine->stop();
else audioEngine->play(project);
});
/* tmp test
connect(new QShortcut(QKeySequence("Ctrl+F1"), ui->patchboard), &QShortcut::activated, [this]() {
connect(new QShortcut(QKeySequence("Ctrl+F1"), ui->patchboard), &QShortcut::activated, this, [this]() {
auto inp = QInputDialog::getText(this, "yes", "yes");
WId id = inp.toULongLong();
auto* w = QWindow::fromWinId(id);
@ -220,11 +220,11 @@ MainWindow::MainWindow(QWidget *parent) :
socket->window = this;
socket->undoStack = undoStack;
connect(socket, &UISocket::updatePatternLists, this, &MainWindow::updatePatternLists);
connect(socket, &UISocket::patternUpdated, [this](Pattern* p) {
connect(socket, &UISocket::patternUpdated, this, [this](Pattern* p) {
if (editingPattern.get() != p) return;
ui->patternEditor->refresh();
});
connect(socket, &UISocket::rowUpdated, [this](Pattern* p, int ch, int r) {
connect(socket, &UISocket::rowUpdated, this, [this](Pattern* p, int ch, int r) {
if (editingPattern.get() != p) return;
const auto cpc = PatternEditorModel::colsPerChannel;
auto ind = ui->patternEditor->model()->index(r, ch * cpc);
@ -232,6 +232,13 @@ MainWindow::MainWindow(QWidget *parent) :
static_cast<PatternEditorModel*>(ui->patternEditor->model())->updateColumnDisplay();
});
// and from audio engine
connect(audioEngine, &AudioEngine::playbackModeChanged, this, [this, undoAction, redoAction](AudioEngine::PlaybackMode) {
bool locked = project->editingLocked();
undoAction->setEnabled(!locked);
redoAction->setEnabled(!locked);
});
// and start with a new project
menuFileNew();
}
@ -257,6 +264,7 @@ bool MainWindow::eventFilter(QObject *obj [[maybe_unused]], QEvent *event) {
void MainWindow::menuFileNew() {
auto hold = project; // keep alive until done
if (audioEngine->playingProject() == project) audioEngine->stop();
project = std::make_shared<Project>();
project->sequence.push_back(project->newPattern().get());
@ -271,6 +279,7 @@ void MainWindow::menuFileOpen() {
QMessageBox::critical(this, "Error", "Error loading project");
return;
}
if (audioEngine->playingProject() == project) audioEngine->stop();
project = np;
onNewProjectLoaded();
}
@ -316,6 +325,16 @@ int MainWindow::sequenceSelection(int n) {
return i.isValid() ? i.column() : -1;
}
void MainWindow::playbackPosition(int seq, int row) {
sequenceSelection(seq);
auto mi = ui->patternEditor->currentIndex().siblingAtRow(row);
if (!mi.isValid()) mi = ui->patternEditor->model()->index(row, 0);
ui->patternEditor->setCurrentIndex(mi);
ui->patternEditor->selectionModel()->select(QItemSelection(mi.siblingAtColumn(0), mi.siblingAtColumn(ui->patternEditor->horizontalHeader()->count()-1)), QItemSelectionModel::SelectionFlag::ClearAndSelect);
ui->patternEditor->scrollTo(mi, QAbstractItemView::PositionAtCenter);
}
void MainWindow::updatePatternLists() {
emit ui->patternList->model()->layoutChanged();
emit ui->patternSequencer->model()->layoutChanged();

View File

@ -40,6 +40,8 @@ namespace Xybrid {
int patternSelection(int = -100);
int sequenceSelection(int = -100);
void playbackPosition(int seq, int row);
protected:
bool eventFilter(QObject *obj, QEvent *event) override;

View File

@ -11,8 +11,8 @@ ChannelHeaderView::ChannelHeaderView(QWidget *parent) : QHeaderView(Qt::Horizont
//
}
QWidget* ChannelHeaderItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option [[maybe_unused]], const QModelIndex &index) const {
/*QWidget* ChannelHeaderItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option [[maybe_unused]], const QModelIndex &index) const {
qDebug() << QString("trying edit");
auto e = new QTextEdit(parent);
return e;
}
}*/

View File

@ -17,7 +17,7 @@ namespace Xybrid::UI {
//bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override;
protected:
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
//QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
//void setEditorData(QWidget *editor, const QModelIndex &index) const override;
//void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
};

View File

@ -69,7 +69,7 @@ PatternEditorView::PatternEditorView(QWidget *parent) : QTableView(parent) {
layoutCheckBox->setContentsMargins(0,0,0,0);
cornerBox->setToolTip("Expand to fit names");
cornerBox->setFocusPolicy(Qt::NoFocus);
connect(cornerBox.get(), &QCheckBox::toggled, [this](bool state) {
connect(cornerBox.get(), &QCheckBox::toggled, this, [this](bool state) {
mdl->fitHeaderToName = state;
mdl->updateColumnDisplay();
});
@ -164,15 +164,15 @@ void PatternEditorView::headerContextMenu(QPoint pt) {
std::shared_ptr<Pattern> p = mdl->getPattern();
QMenu* menu = new QMenu(this);
menu->addAction("Add Channel", [/*this,*/ idx, p]() {
menu->addAction("Add Channel", this, [/*this,*/ idx, p]() {
(new PatternChannelAddCommand(p, idx))->commit();
});
if (idx < hdr->count() - 1) {
menu->addAction("Delete Channel", [this, idx, p]() {
menu->addAction("Delete Channel", this, [this, idx, p]() {
if (QMessageBox::warning(this, "Are you sure?", QString("Remove channel %1 from pattern %2?").arg(Util::numAndName(idx, p->channel(idx).name)).arg(Util::numAndName(p->index, p->name)), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) return;
(new PatternChannelDeleteCommand(p, idx))->commit();
});
menu->addAction("Rename Channel", [this, idx, p]() {
menu->addAction("Rename Channel", this, [this, idx, p]() {
if (p != mdl->getPattern()) return; // swapped already
startRenameChannel(idx);
});

View File

@ -26,6 +26,9 @@ DISTFILES += ../.astylerc
CONFIG += c++17
# use all optimizations that won't generally interfere with debugging
QMAKE_CXXFLAGS_DEBUG += -Og
SOURCES += \
main.cpp \
mainwindow.cpp \