rendering to mp3!
parent
98249b553a
commit
212e46b67d
|
@ -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);
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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; }");
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue