Add proper cancelation
This commit is contained in:
parent
ee30799ac7
commit
6042b479a4
5 changed files with 175 additions and 234 deletions
|
|
@ -8,269 +8,205 @@
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QRandomGenerator>
|
#include <QRandomGenerator>
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
AceStepWorker::AceStepWorker(QObject *parent)
|
AceStep::AceStep(QObject* parent): QObject(parent)
|
||||||
: QObject(parent),
|
|
||||||
currentWorker(nullptr)
|
|
||||||
{
|
{
|
||||||
|
connect(&qwenProcess, &QProcess::finished, this, &AceStep::qwenProcFinished);
|
||||||
|
connect(&ditVaeProcess, &QProcess::finished, this, &AceStep::ditProcFinished);
|
||||||
}
|
}
|
||||||
|
|
||||||
AceStepWorker::~AceStepWorker()
|
bool AceStep::isGenerateing(SongItem* song)
|
||||||
{
|
{
|
||||||
cancelGeneration();
|
if(!busy && song)
|
||||||
|
*song = this->request.song;
|
||||||
|
return busy;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AceStepWorker::generateSong(const SongItem& song, const QString &jsonTemplate,
|
void AceStep::cancleGenerateion()
|
||||||
const QString &aceStepPath, const QString &qwen3ModelPath,
|
|
||||||
const QString &textEncoderModelPath, const QString &ditModelPath,
|
|
||||||
const QString &vaeModelPath)
|
|
||||||
{
|
{
|
||||||
// Cancel any ongoing generation
|
qwenProcess.blockSignals(true);
|
||||||
cancelGeneration();
|
qwenProcess.terminate();
|
||||||
|
qwenProcess.waitForFinished();
|
||||||
|
qwenProcess.blockSignals(false);
|
||||||
|
|
||||||
// Create worker and start it
|
ditVaeProcess.blockSignals(true);
|
||||||
currentWorker = new Worker(this, song, jsonTemplate, aceStepPath, qwen3ModelPath,
|
ditVaeProcess.terminate();
|
||||||
textEncoderModelPath, ditModelPath, vaeModelPath);
|
ditVaeProcess.waitForFinished();
|
||||||
currentWorker->setAutoDelete(true);
|
ditVaeProcess.blockSignals(false);
|
||||||
QThreadPool::globalInstance()->start(currentWorker);
|
|
||||||
|
progressUpdate(100);
|
||||||
|
if(busy)
|
||||||
|
generationCancled(request.song);
|
||||||
|
|
||||||
|
busy = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AceStepWorker::cancelGeneration()
|
bool AceStep::requestGeneration(SongItem song, QString requestTemplate, QString aceStepPath,
|
||||||
|
QString qwen3ModelPath, QString textEncoderModelPath, QString ditModelPath,
|
||||||
|
QString vaeModelPath)
|
||||||
{
|
{
|
||||||
currentWorker = nullptr;
|
if(busy)
|
||||||
}
|
|
||||||
|
|
||||||
bool AceStepWorker::songGenerateing(SongItem* song)
|
|
||||||
{
|
|
||||||
workerMutex.lock();
|
|
||||||
if(!currentWorker)
|
|
||||||
{
|
{
|
||||||
workerMutex.unlock();
|
qWarning()<<"Droping song:"<<song.caption;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
busy = true;
|
||||||
|
|
||||||
|
request = {song, QRandomGenerator::global()->generate(), aceStepPath, textEncoderModelPath, ditModelPath, vaeModelPath};
|
||||||
|
|
||||||
|
QString qwen3Binary = aceStepPath + "/ace-qwen3";
|
||||||
|
QFileInfo qwen3Info(qwen3Binary);
|
||||||
|
if (!qwen3Info.exists() || !qwen3Info.isExecutable())
|
||||||
{
|
{
|
||||||
SongItem workerSong = currentWorker->getSong();
|
generationError("ace-qwen3 binary not found at: " + qwen3Binary);
|
||||||
workerMutex.unlock();
|
busy = false;
|
||||||
if(song)
|
return false;
|
||||||
*song = workerSong;
|
}
|
||||||
return true;
|
if (!QFileInfo::exists(qwen3ModelPath))
|
||||||
|
{
|
||||||
|
generationError("Qwen3 model not found: " + qwen3ModelPath);
|
||||||
|
busy = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!QFileInfo::exists(textEncoderModelPath))
|
||||||
|
{
|
||||||
|
generationError("Text encoder model not found: " + textEncoderModelPath);
|
||||||
|
busy = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!QFileInfo::exists(ditModelPath))
|
||||||
|
{
|
||||||
|
generationError("DiT model not found: " + ditModelPath);
|
||||||
|
busy = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!QFileInfo::exists(vaeModelPath))
|
||||||
|
{
|
||||||
|
generationError("VAE model not found: " + vaeModelPath);
|
||||||
|
busy = false;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Worker implementation
|
request.requestFilePath = tempDir + "/request_" + QString::number(request.uid) + ".json";
|
||||||
void AceStepWorker::Worker::run()
|
|
||||||
{
|
|
||||||
uint64_t uid = QRandomGenerator::global()->generate();
|
|
||||||
// Create temporary JSON file for the request
|
|
||||||
QString tempDir = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
|
|
||||||
QString requestFile = tempDir + "/request_" + QString::number(uid) + ".json";
|
|
||||||
|
|
||||||
// Parse and modify the template
|
|
||||||
QJsonParseError parseError;
|
QJsonParseError parseError;
|
||||||
QJsonDocument templateDoc = QJsonDocument::fromJson(jsonTemplate.toUtf8(), &parseError);
|
QJsonDocument templateDoc = QJsonDocument::fromJson(requestTemplate.toUtf8(), &parseError);
|
||||||
if (!templateDoc.isObject())
|
if (!templateDoc.isObject())
|
||||||
{
|
{
|
||||||
emit parent->generationError("Invalid JSON template: " + QString(parseError.errorString()));
|
generationError("Invalid JSON template: " + QString(parseError.errorString()));
|
||||||
return;
|
busy = false;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject requestObj = templateDoc.object();
|
QJsonObject requestObj = templateDoc.object();
|
||||||
requestObj["caption"] = song.caption;
|
requestObj["caption"] = song.caption;
|
||||||
|
|
||||||
if (!song.lyrics.isEmpty())
|
if (!song.lyrics.isEmpty())
|
||||||
{
|
|
||||||
requestObj["lyrics"] = song.lyrics;
|
requestObj["lyrics"] = song.lyrics;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Remove lyrics field if empty to let the LLM generate them
|
|
||||||
requestObj.remove("lyrics");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply vocal language override if set
|
|
||||||
if (!song.vocalLanguage.isEmpty())
|
if (!song.vocalLanguage.isEmpty())
|
||||||
{
|
|
||||||
requestObj["vocal_language"] = song.vocalLanguage;
|
requestObj["vocal_language"] = song.vocalLanguage;
|
||||||
}
|
|
||||||
|
|
||||||
// Write the request file
|
// Write the request file
|
||||||
QFile requestFileHandle(requestFile);
|
QFile requestFileHandle(request.requestFilePath);
|
||||||
if (!requestFileHandle.open(QIODevice::WriteOnly | QIODevice::Text))
|
if (!requestFileHandle.open(QIODevice::WriteOnly | QIODevice::Text))
|
||||||
{
|
{
|
||||||
emit parent->generationError("Failed to create request file: " + requestFileHandle.errorString());
|
emit generationError("Failed to create request file: " + requestFileHandle.errorString());
|
||||||
return;
|
busy = false;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
requestFileHandle.write(QJsonDocument(requestObj).toJson(QJsonDocument::Indented));
|
requestFileHandle.write(QJsonDocument(requestObj).toJson(QJsonDocument::Indented));
|
||||||
requestFileHandle.close();
|
requestFileHandle.close();
|
||||||
|
|
||||||
// Use provided paths for acestep.cpp binaries
|
QStringList qwen3Args;
|
||||||
QString qwen3Binary = this->aceStepPath + "/ace-qwen3";
|
qwen3Args << "--request" << request.requestFilePath;
|
||||||
QString ditVaeBinary = this->aceStepPath + "/dit-vae";
|
qwen3Args << "--model" << qwen3ModelPath;
|
||||||
|
|
||||||
// Check if binaries exist
|
progressUpdate(30);
|
||||||
QFileInfo qwen3Info(qwen3Binary);
|
|
||||||
QFileInfo ditVaeInfo(ditVaeBinary);
|
|
||||||
|
|
||||||
if (!qwen3Info.exists() || !qwen3Info.isExecutable())
|
qwenProcess.start(qwen3Binary, qwen3Args);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AceStep::qwenProcFinished(int code, QProcess::ExitStatus status)
|
||||||
|
{
|
||||||
|
QFile::remove(request.requestFilePath);
|
||||||
|
if(code != 0)
|
||||||
{
|
{
|
||||||
emit parent->generationError("ace-qwen3 binary not found at: " + qwen3Binary);
|
QString errorOutput = qwenProcess.readAllStandardError();
|
||||||
|
generationError("dit-vae exited with code " + QString::number(code) + ": " + errorOutput);
|
||||||
|
busy = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString ditVaeBinary = request.aceStepPath + "/dit-vae";
|
||||||
|
QFileInfo ditVaeInfo(ditVaeBinary);
|
||||||
if (!ditVaeInfo.exists() || !ditVaeInfo.isExecutable())
|
if (!ditVaeInfo.exists() || !ditVaeInfo.isExecutable())
|
||||||
{
|
{
|
||||||
emit parent->generationError("dit-vae binary not found at: " + ditVaeBinary);
|
generationError("dit-vae binary not found at: " + ditVaeBinary);
|
||||||
|
busy = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use provided model paths
|
request.requestLlmFilePath = tempDir + "/request_" + QString::number(request.uid) + "0.json";
|
||||||
QString qwen3Model = this->qwen3ModelPath;
|
if (!QFileInfo::exists(request.requestLlmFilePath))
|
||||||
QString textEncoderModel = this->textEncoderModelPath;
|
|
||||||
QString ditModel = this->ditModelPath;
|
|
||||||
QString vaeModel = this->vaeModelPath;
|
|
||||||
|
|
||||||
if (!QFileInfo::exists(qwen3Model))
|
|
||||||
{
|
{
|
||||||
emit parent->generationError("Qwen3 model not found: " + qwen3Model);
|
generationError("ace-qwen3 failed to create enhaced request file "+request.requestLlmFilePath);
|
||||||
return;
|
busy = false;
|
||||||
}
|
|
||||||
|
|
||||||
if (!QFileInfo::exists(textEncoderModel))
|
|
||||||
{
|
|
||||||
emit parent->generationError("Text encoder model not found: " + textEncoderModel);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!QFileInfo::exists(ditModel))
|
|
||||||
{
|
|
||||||
emit parent->generationError("DiT model not found: " + ditModel);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!QFileInfo::exists(vaeModel))
|
|
||||||
{
|
|
||||||
emit parent->generationError("VAE model not found: " + vaeModel);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 1: Run ace-qwen3 to generate lyrics and audio codes
|
|
||||||
QProcess qwen3Process;
|
|
||||||
QStringList qwen3Args;
|
|
||||||
qwen3Args << "--request" << requestFile;
|
|
||||||
qwen3Args << "--model" << qwen3Model;
|
|
||||||
|
|
||||||
emit parent->progressUpdate(20);
|
|
||||||
|
|
||||||
qwen3Process.start(qwen3Binary, qwen3Args);
|
|
||||||
if (!qwen3Process.waitForStarted())
|
|
||||||
{
|
|
||||||
emit parent->generationError("Failed to start ace-qwen3: " + qwen3Process.errorString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!qwen3Process.waitForFinished(60000)) // 60 second timeout
|
|
||||||
{
|
|
||||||
qwen3Process.terminate();
|
|
||||||
qwen3Process.waitForFinished(5000);
|
|
||||||
emit parent->generationError("ace-qwen3 timed out after 60 seconds");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int exitCode = qwen3Process.exitCode();
|
|
||||||
if (exitCode != 0)
|
|
||||||
{
|
|
||||||
QString errorOutput = qwen3Process.readAllStandardError();
|
|
||||||
emit parent->generationError("ace-qwen3 exited with code " + QString::number(exitCode) + ": " + errorOutput);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString requestLmOutputFile = tempDir + "/request_" + QString::number(uid) + "0.json";
|
|
||||||
if (!QFileInfo::exists(requestLmOutputFile))
|
|
||||||
{
|
|
||||||
emit parent->generationError("ace-qwen3 failed to create enhaced request file "+requestLmOutputFile);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load lyrics from the enhanced request file
|
// Load lyrics from the enhanced request file
|
||||||
QFile lmOutputFile(requestLmOutputFile);
|
QFile lmOutputFile(request.requestLlmFilePath);
|
||||||
if (lmOutputFile.open(QIODevice::ReadOnly | QIODevice::Text))
|
if (lmOutputFile.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||||
{
|
{
|
||||||
QJsonParseError parseError;
|
QJsonParseError parseError;
|
||||||
song.json = lmOutputFile.readAll();
|
request.song.json = lmOutputFile.readAll();
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(song.json.toUtf8(), &parseError);
|
QJsonDocument doc = QJsonDocument::fromJson(request.song.json.toUtf8(), &parseError);
|
||||||
lmOutputFile.close();
|
lmOutputFile.close();
|
||||||
|
|
||||||
if (doc.isObject() && !parseError.error)
|
if (doc.isObject() && !parseError.error)
|
||||||
{
|
{
|
||||||
QJsonObject obj = doc.object();
|
QJsonObject obj = doc.object();
|
||||||
if (obj.contains("lyrics") && obj["lyrics"].isString())
|
if (obj.contains("lyrics") && obj["lyrics"].isString())
|
||||||
{
|
request.song.lyrics = obj["lyrics"].toString();
|
||||||
song.lyrics = obj["lyrics"].toString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emit parent->progressUpdate(50);
|
|
||||||
|
|
||||||
// Step 2: Run dit-vae to generate audio
|
// Step 2: Run dit-vae to generate audio
|
||||||
QProcess ditVaeProcess;
|
|
||||||
QStringList ditVaeArgs;
|
QStringList ditVaeArgs;
|
||||||
ditVaeArgs << "--request" << requestLmOutputFile;
|
ditVaeArgs << "--request"<<request.requestLlmFilePath;
|
||||||
ditVaeArgs << "--text-encoder" << textEncoderModel;
|
ditVaeArgs << "--text-encoder"<<request.textEncoderModelPath;
|
||||||
ditVaeArgs << "--dit" << ditModel;
|
ditVaeArgs << "--dit"<<request.ditModelPath;
|
||||||
ditVaeArgs << "--vae" << vaeModel;
|
ditVaeArgs << "--vae"<<request.vaeModelPath;
|
||||||
|
|
||||||
emit parent->progressUpdate(60);
|
progressUpdate(60);
|
||||||
|
|
||||||
ditVaeProcess.start(ditVaeBinary, ditVaeArgs);
|
ditVaeProcess.start(ditVaeBinary, ditVaeArgs);
|
||||||
if (!ditVaeProcess.waitForStarted())
|
}
|
||||||
{
|
|
||||||
emit parent->generationError("Failed to start dit-vae: " + ditVaeProcess.errorString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ditVaeProcess.waitForFinished(120000)) // 2 minute timeout
|
void AceStep::ditProcFinished(int code, QProcess::ExitStatus status)
|
||||||
{
|
{
|
||||||
ditVaeProcess.terminate();
|
QFile::remove(request.requestLlmFilePath);
|
||||||
ditVaeProcess.waitForFinished(5000);
|
if (code != 0)
|
||||||
emit parent->generationError("dit-vae timed out after 2 minutes");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
exitCode = ditVaeProcess.exitCode();
|
|
||||||
if (exitCode != 0)
|
|
||||||
{
|
{
|
||||||
QString errorOutput = ditVaeProcess.readAllStandardError();
|
QString errorOutput = ditVaeProcess.readAllStandardError();
|
||||||
emit parent->generationError("dit-vae exited with code " + QString::number(exitCode) + ": " + errorOutput);
|
generationError("dit-vae exited with code " + QString::number(code) + ": " + errorOutput);
|
||||||
|
busy = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
emit parent->progressUpdate(90);
|
|
||||||
|
|
||||||
// Find the generated WAV file
|
// Find the generated WAV file
|
||||||
QString wavFile = QFileInfo(requestFile).absolutePath()+"/request_" + QString::number(uid) + "00.wav";
|
QString wavFile = tempDir+"/request_" + QString::number(request.uid) + "00.wav";
|
||||||
if (!QFileInfo::exists(wavFile))
|
if (!QFileInfo::exists(wavFile))
|
||||||
{
|
{
|
||||||
emit parent->generationError("No WAV file generated at "+wavFile);
|
generationError("No WAV file generated at "+wavFile);
|
||||||
|
busy = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
busy = false;
|
||||||
|
|
||||||
// Clean up temporary files
|
progressUpdate(100);
|
||||||
QFile::remove(requestLmOutputFile);
|
request.song.file = wavFile;
|
||||||
QFile::remove(requestFile);
|
songGenerated(request.song);
|
||||||
|
|
||||||
emit parent->progressUpdate(100);
|
|
||||||
song.file = wavFile;
|
|
||||||
emit parent->songGenerated(song);
|
|
||||||
parent->workerMutex.lock();
|
|
||||||
parent->currentWorker = nullptr;
|
|
||||||
parent->workerMutex.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const SongItem& AceStepWorker::Worker::getSong()
|
|
||||||
{
|
|
||||||
return song;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -2,64 +2,55 @@
|
||||||
#define ACESTEPWORKER_H
|
#define ACESTEPWORKER_H
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QRunnable>
|
|
||||||
#include <QThreadPool>
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QJsonObject>
|
#include <QProcess>
|
||||||
#include <QMutex>
|
#include <QStandardPaths>
|
||||||
|
|
||||||
#include "SongItem.h"
|
#include "SongItem.h"
|
||||||
|
|
||||||
class AceStepWorker : public QObject
|
class AceStep : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
QProcess qwenProcess;
|
||||||
explicit AceStepWorker(QObject *parent = nullptr);
|
QProcess ditVaeProcess;
|
||||||
~AceStepWorker();
|
|
||||||
|
|
||||||
void generateSong(const SongItem& song, const QString &jsonTemplate,
|
bool busy = false;
|
||||||
const QString &aceStepPath, const QString &qwen3ModelPath,
|
|
||||||
const QString &textEncoderModelPath, const QString &ditModelPath,
|
|
||||||
const QString &vaeModelPath);
|
|
||||||
void cancelGeneration();
|
|
||||||
bool songGenerateing(SongItem* song);
|
|
||||||
|
|
||||||
signals:
|
struct Request
|
||||||
void songGenerated(const SongItem& song);
|
|
||||||
void generationFinished();
|
|
||||||
void generationError(const QString &error);
|
|
||||||
void progressUpdate(int percent);
|
|
||||||
|
|
||||||
private:
|
|
||||||
class Worker : public QRunnable
|
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
Worker(AceStepWorker *parent, const SongItem& song, const QString &jsonTemplate,
|
|
||||||
const QString &aceStepPath, const QString &qwen3ModelPath,
|
|
||||||
const QString &textEncoderModelPath, const QString &ditModelPath,
|
|
||||||
const QString &vaeModelPath)
|
|
||||||
: parent(parent), song(song), jsonTemplate(jsonTemplate),
|
|
||||||
aceStepPath(aceStepPath), qwen3ModelPath(qwen3ModelPath),
|
|
||||||
textEncoderModelPath(textEncoderModelPath), ditModelPath(ditModelPath),
|
|
||||||
vaeModelPath(vaeModelPath) {}
|
|
||||||
|
|
||||||
void run() override;
|
|
||||||
|
|
||||||
const SongItem& getSong();
|
|
||||||
|
|
||||||
private:
|
|
||||||
AceStepWorker *parent;
|
|
||||||
SongItem song;
|
SongItem song;
|
||||||
QString jsonTemplate;
|
uint64_t uid;
|
||||||
QString aceStepPath;
|
QString aceStepPath;
|
||||||
QString qwen3ModelPath;
|
|
||||||
QString textEncoderModelPath;
|
QString textEncoderModelPath;
|
||||||
QString ditModelPath;
|
QString ditModelPath;
|
||||||
QString vaeModelPath;
|
QString vaeModelPath;
|
||||||
|
QString requestFilePath;
|
||||||
|
QString requestLlmFilePath;
|
||||||
};
|
};
|
||||||
|
|
||||||
QMutex workerMutex;
|
Request request;
|
||||||
Worker *currentWorker;
|
|
||||||
|
const QString tempDir = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void songGenerated(SongItem song);
|
||||||
|
void generationCancled(SongItem song);
|
||||||
|
void generationError(QString error);
|
||||||
|
void progressUpdate(int progress);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
bool requestGeneration(SongItem song, QString requestTemplate, QString aceStepPath,
|
||||||
|
QString qwen3ModelPath, QString textEncoderModelPath, QString ditModelPath,
|
||||||
|
QString vaeModelPath);
|
||||||
|
|
||||||
|
public:
|
||||||
|
AceStep(QObject* parent = nullptr);
|
||||||
|
bool isGenerateing(SongItem* song = nullptr);
|
||||||
|
void cancleGenerateion();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void qwenProcFinished(int code, QProcess::ExitStatus status);
|
||||||
|
void ditProcFinished(int code, QProcess::ExitStatus status);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ACESTEPWORKER_H
|
#endif // ACESTEPWORKER_H
|
||||||
|
|
|
||||||
|
|
@ -18,13 +18,15 @@ MainWindow::MainWindow(QWidget *parent)
|
||||||
ui(new Ui::MainWindow),
|
ui(new Ui::MainWindow),
|
||||||
songModel(new SongListModel(this)),
|
songModel(new SongListModel(this)),
|
||||||
audioPlayer(new AudioPlayer(this)),
|
audioPlayer(new AudioPlayer(this)),
|
||||||
aceStepWorker(new AceStepWorker(this)),
|
aceStep(new AceStep(this)),
|
||||||
playbackTimer(new QTimer(this)),
|
playbackTimer(new QTimer(this)),
|
||||||
isPlaying(false),
|
isPlaying(false),
|
||||||
isPaused(false),
|
isPaused(false),
|
||||||
shuffleMode(false),
|
shuffleMode(false),
|
||||||
isGeneratingNext(false)
|
isGeneratingNext(false)
|
||||||
{
|
{
|
||||||
|
aceStep->moveToThread(&aceThread);
|
||||||
|
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
// Setup lyrics display
|
// Setup lyrics display
|
||||||
|
|
@ -57,9 +59,10 @@ MainWindow::MainWindow(QWidget *parent)
|
||||||
connect(audioPlayer, &AudioPlayer::playbackStarted, this, &MainWindow::playbackStarted);
|
connect(audioPlayer, &AudioPlayer::playbackStarted, this, &MainWindow::playbackStarted);
|
||||||
connect(audioPlayer, &AudioPlayer::positionChanged, this, &MainWindow::updatePosition);
|
connect(audioPlayer, &AudioPlayer::positionChanged, this, &MainWindow::updatePosition);
|
||||||
connect(audioPlayer, &AudioPlayer::durationChanged, this, &MainWindow::updateDuration);
|
connect(audioPlayer, &AudioPlayer::durationChanged, this, &MainWindow::updateDuration);
|
||||||
connect(aceStepWorker, &AceStepWorker::songGenerated, this, &MainWindow::songGenerated);
|
connect(aceStep, &AceStep::songGenerated, this, &MainWindow::songGenerated);
|
||||||
connect(aceStepWorker, &AceStepWorker::generationError, this, &MainWindow::generationError);
|
connect(aceStep, &AceStep::generationCancled, this, &MainWindow::generationCanceld);
|
||||||
connect(aceStepWorker, &AceStepWorker::progressUpdate, ui->progressBar, &QProgressBar::setValue);
|
connect(aceStep, &AceStep::generationError, this, &MainWindow::generationError);
|
||||||
|
connect(aceStep, &AceStep::progressUpdate, ui->progressBar, &QProgressBar::setValue);
|
||||||
|
|
||||||
// Connect double-click on song list for editing (works with QTableView too)
|
// Connect double-click on song list for editing (works with QTableView too)
|
||||||
connect(ui->songListView, &QTableView::doubleClicked, this, &MainWindow::on_songListView_doubleClicked);
|
connect(ui->songListView, &QTableView::doubleClicked, this, &MainWindow::on_songListView_doubleClicked);
|
||||||
|
|
@ -269,6 +272,10 @@ void MainWindow::on_shuffleButton_clicked()
|
||||||
{
|
{
|
||||||
shuffleMode = ui->shuffleButton->isChecked();
|
shuffleMode = ui->shuffleButton->isChecked();
|
||||||
updateControls();
|
updateControls();
|
||||||
|
|
||||||
|
flushGenerationQueue();
|
||||||
|
if(isPlaying)
|
||||||
|
ensureSongsInQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_addSongButton_clicked()
|
void MainWindow::on_addSongButton_clicked()
|
||||||
|
|
@ -426,6 +433,11 @@ void MainWindow::songGenerated(const SongItem& song)
|
||||||
ensureSongsInQueue();
|
ensureSongsInQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::generationCanceld(const SongItem& song)
|
||||||
|
{
|
||||||
|
ui->statusbar->showMessage("Geneartion cancled: " + song.caption);
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::playNextSong()
|
void MainWindow::playNextSong()
|
||||||
{
|
{
|
||||||
if (!isPlaying)
|
if (!isPlaying)
|
||||||
|
|
@ -505,7 +517,7 @@ void MainWindow::ensureSongsInQueue(bool enqeueCurrent)
|
||||||
|
|
||||||
SongItem lastSong;
|
SongItem lastSong;
|
||||||
SongItem workerSong;
|
SongItem workerSong;
|
||||||
if(aceStepWorker->songGenerateing(&workerSong))
|
if(aceStep->isGenerateing(&workerSong))
|
||||||
lastSong = workerSong;
|
lastSong = workerSong;
|
||||||
else if(!generatedSongQueue.empty())
|
else if(!generatedSongQueue.empty())
|
||||||
lastSong = generatedSongQueue.last();
|
lastSong = generatedSongQueue.last();
|
||||||
|
|
@ -526,7 +538,7 @@ void MainWindow::ensureSongsInQueue(bool enqeueCurrent)
|
||||||
isGeneratingNext = true;
|
isGeneratingNext = true;
|
||||||
|
|
||||||
ui->statusbar->showMessage("Generateing: "+nextSong.caption);
|
ui->statusbar->showMessage("Generateing: "+nextSong.caption);
|
||||||
aceStepWorker->generateSong(nextSong, jsonTemplate,
|
aceStep->requestGeneration(nextSong, jsonTemplate,
|
||||||
aceStepPath, qwen3ModelPath,
|
aceStepPath, qwen3ModelPath,
|
||||||
textEncoderModelPath, ditModelPath,
|
textEncoderModelPath, ditModelPath,
|
||||||
vaeModelPath);
|
vaeModelPath);
|
||||||
|
|
@ -535,7 +547,7 @@ void MainWindow::ensureSongsInQueue(bool enqeueCurrent)
|
||||||
void MainWindow::flushGenerationQueue()
|
void MainWindow::flushGenerationQueue()
|
||||||
{
|
{
|
||||||
generatedSongQueue.clear();
|
generatedSongQueue.clear();
|
||||||
aceStepWorker->cancelGeneration();
|
aceStep->cancleGenerateion();
|
||||||
isGeneratingNext = false;
|
isGeneratingNext = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QQueue>
|
#include <QQueue>
|
||||||
#include <QPair>
|
#include <QPair>
|
||||||
#include <cstdint>
|
#include <QThread>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
|
|
@ -47,6 +47,7 @@ private slots:
|
||||||
void on_songListView_doubleClicked(const QModelIndex &index);
|
void on_songListView_doubleClicked(const QModelIndex &index);
|
||||||
|
|
||||||
void songGenerated(const SongItem& song);
|
void songGenerated(const SongItem& song);
|
||||||
|
void generationCanceld(const SongItem& song);
|
||||||
void playNextSong();
|
void playNextSong();
|
||||||
void playbackStarted();
|
void playbackStarted();
|
||||||
void updatePlaybackStatus(bool playing);
|
void updatePlaybackStatus(bool playing);
|
||||||
|
|
@ -64,7 +65,8 @@ private:
|
||||||
Ui::MainWindow *ui;
|
Ui::MainWindow *ui;
|
||||||
SongListModel *songModel;
|
SongListModel *songModel;
|
||||||
AudioPlayer *audioPlayer;
|
AudioPlayer *audioPlayer;
|
||||||
AceStepWorker *aceStepWorker;
|
QThread aceThread;
|
||||||
|
AceStep *aceStep;
|
||||||
QTimer *playbackTimer;
|
QTimer *playbackTimer;
|
||||||
|
|
||||||
QString formatTime(int milliseconds);
|
QString formatTime(int milliseconds);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#include "MainWindow.h"
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QStyleFactory>
|
#include <QThread>
|
||||||
#include <QIcon>
|
|
||||||
|
#include "MainWindow.h"
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue