rendering to mp3!

portability/boost
zetaPRIME 2019-06-26 16:44:55 -04:00
parent 98249b553a
commit 212e46b67d
3 changed files with 74 additions and 2 deletions

View File

@ -8,6 +8,8 @@ using namespace Xybrid::Data;
#include "mainwindow.h"
#include "uisocket.h"
#include "util/strings.h"
#include <algorithm>
#include <cmath>
@ -15,6 +17,7 @@ using namespace Xybrid::Data;
#include <QThread>
#include <QMutex>
#include <QTimer>
#include <QProcess>
// zero-initialize
AudioEngine* Xybrid::Audio::audioEngine = nullptr;
@ -142,6 +145,10 @@ void AudioEngine::play(std::shared_ptr<Project> p, int fromPos) {
}
void AudioEngine::stop() {
if (mode == Rendering) {
mode = Stopped;
return;
}
QMetaObject::invokeMethod(this, [this] {
if (project) project->rootGraph->release();
project = nullptr;
@ -196,6 +203,61 @@ uint16_t AudioEngine::preview(std::shared_ptr<Project> p, int16_t port, int16_t
return nId;
}
void AudioEngine::render(std::shared_ptr<Project> p, QString filename) {
if (!p) return; // yeah, no
if (mode != Stopped) {
stop();
while (output) QThread::yieldCurrentThread();
}
project = p;
queueValid = false;
queue.clear();
portLastNoteId.fill(0);
project->rootGraph->reset();
for (auto& b : buffer) {
b.clear();
b.reserve(static_cast<size_t>(sampleRate/4));
}
seqPos = -1;
curRow = 0;
curTick = -2;
tempo = project->tempo;
tickAcc = 0;
initAudio(); // we actually need the period size. whoops.
QProcess enc;
QStringList param;
param << "-y" << "-f" << "s16le" << "-ac" << "2" << "-ar" << QString::number(sampleRate) << "-i" << "pipe:";
if (!project->title.isEmpty()) param << "-metadata" << qs("title=%1").arg(project->title);
if (!project->artist.isEmpty()) param << "-metadata" << qs("artist=%1").arg(project->artist);
param << "-f" << "mp3" << filename;
enc.start("ffmpeg", param);
enc.waitForStarted();
std::vector<char> dat;
dat.resize(1024);
mode = Rendering;
while (mode == Rendering) {
enc.write(&dat[0], readData(&dat[0], 1024));
//enc.write(read(1024));
}
enc.closeWriteChannel();
enc.waitForFinished();
stop();
//qDebug() << enc.readAllStandardOutput();
//qDebug() << enc.readAllStandardError();
}
void AudioEngine::buildQueue() {
// keep track of what was there before
std::unordered_set<Node*> prev;
@ -328,7 +390,7 @@ void AudioEngine::nextTick() {
memcpy(buffer[0].data(), p->bufL, bufs);
memcpy(buffer[1].data(), p->bufR, bufs);
}
} else if (mode == Playing) {
} else if (mode == Playing || mode == Rendering) {
// reset raw buffer
tickBufPtr = tickBuf.get();
tickId++;
@ -354,7 +416,9 @@ void AudioEngine::nextTick() {
p = nullptr;
int tries = 0;
while (!p) {
seqPos = (seqPos+1) % static_cast<int>(project->sequence.size());
seqPos++;
if (mode == Playing) seqPos %= static_cast<int>(project->sequence.size());
else if (seqPos > static_cast<int>(project->sequence.size())) { stop(); return; } // stop at end if rendering
setP();
if (++tries > 25) return; // either you have 25 separators in a row, or you have no patterns
}
@ -369,6 +433,7 @@ void AudioEngine::nextTick() {
curTick = 0;
curRow++;
if (!p || curRow >= p->rows) advanceSeq();
if (!p) return; // stopping
MainWindow* w = project->socket->window;
QMetaObject::invokeMethod(w, [this, w]{ w->playbackPosition(seqPos, curRow); }, Qt::QueuedConnection);

View File

@ -103,6 +103,7 @@ namespace Xybrid::Audio {
void play(std::shared_ptr<Data::Project>, int fromPos = -1);
void stop();
uint16_t preview(std::shared_ptr<Data::Project>, int16_t port, int16_t note, uint16_t nId = 0, Data::Node* node = nullptr);
void render(std::shared_ptr<Data::Project>, QString);
inline uint8_t previewPort() const { return previewPort_; }
inline void invalidateQueue(Data::Project* p) { if (p == project.get()) queueValid = false; }

View File

@ -119,6 +119,12 @@ MainWindow::MainWindow(QWidget *parent) :
else audioEngine->play(project);
}
});
// TODO: temp: render shortcut
connect(new QShortcut(QKeySequence("Ctrl+E"), this), &QShortcut::activated, this, [this]() {
audioEngine->render(project, Config::Directories::projects % "/testOut.mp3");
});
//ui->menuBar->setStyleSheet("QMenuBar { background: transparent; vertical-align: center; } QMenuBar::item { } QMenuBar::item:!pressed { background: transparent; }");
}