From 64357be451573be4da7128d55ad6f943d52cc53b Mon Sep 17 00:00:00 2001 From: Carl Philipp Klemm Date: Sat, 21 Mar 2026 23:09:06 +0100 Subject: [PATCH 1/2] Icon: make card opaque --- res/xyz.uvos.aceradio.png | Bin 3747 -> 3626 bytes res/xyz.uvos.aceradio.svg | 16 ++++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/res/xyz.uvos.aceradio.png b/res/xyz.uvos.aceradio.png index 603d014365b2ee8d9f2d6a42793782dfe4ac037f..032045e18afa712fe3969fe82388ac862ed33d0d 100644 GIT binary patch delta 3625 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 N002ovPDHLkV1j;o63_qu diff --git a/res/xyz.uvos.aceradio.svg b/res/xyz.uvos.aceradio.svg index 23516fd..70a6ec5 100644 --- a/res/xyz.uvos.aceradio.svg +++ b/res/xyz.uvos.aceradio.svg @@ -7,7 +7,7 @@ width="594" height="594" viewBox="0 0 594 594" - sodipodi:docname="icon.svg" + sodipodi:docname="xyz.uvos.aceradio.svg" inkscape:export-filename="icon64.png" inkscape:export-xdpi="10.343434" inkscape:export-ydpi="10.343434" @@ -28,9 +28,9 @@ inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" showguides="true" - inkscape:zoom="0.58212013" - inkscape:cx="-146.01797" - inkscape:cy="490.44859" + inkscape:zoom="0.82324218" + inkscape:cx="38.8707" + inkscape:cy="407.535" inkscape:window-width="1920" inkscape:window-height="1054" inkscape:window-x="0" @@ -45,6 +45,14 @@ + Date: Sat, 21 Mar 2026 23:09:39 +0100 Subject: [PATCH 2/2] Refactor json handling for SongItems and add new fields. --- CMakeLists.txt | 2 + src/AceStepWorker.cpp | 9 +--- src/MainWindow.cpp | 49 +++---------------- src/SongDialog.cpp | 111 +++++++++++++++++++++++++++++------------- src/SongDialog.h | 11 ++--- src/SongDialog.ui | 67 ++++++++++++++++++++----- src/SongItem.cpp | 43 ++++++++++++++++ src/SongItem.h | 19 +++++--- src/SongListModel.cpp | 20 ++++++++ src/SongListModel.h | 1 + 10 files changed, 224 insertions(+), 108 deletions(-) create mode 100644 src/SongItem.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a542e75..191c922 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,8 @@ add_executable(${PROJECT_NAME} ${MusicGeneratorGUI_H} res/resources.qrc src/elidedlabel.h src/elidedlabel.cpp + src/SongItem.cpp + ) # UI file diff --git a/src/AceStepWorker.cpp b/src/AceStepWorker.cpp index 837247b..50d940c 100644 --- a/src/AceStepWorker.cpp +++ b/src/AceStepWorker.cpp @@ -102,14 +102,7 @@ bool AceStep::requestGeneration(SongItem song, QString requestTemplate, QString } QJsonObject requestObj = templateDoc.object(); - requestObj["caption"] = song.caption; - - if (!song.lyrics.isEmpty()) - requestObj["lyrics"] = song.lyrics; - if (!song.vocalLanguage.isEmpty()) - requestObj["vocal_language"] = song.vocalLanguage; - - requestObj["use_cot_caption"] = song.cotCaption; + song.store(requestObj); // Write the request file QFile requestFileHandle(request.requestFilePath); diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index d2dd51d..d09e473 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -291,12 +291,7 @@ void MainWindow::on_addSongButton_clicked() if (dialog.exec() == QDialog::Accepted) { - QString caption = dialog.getCaption(); - QString lyrics = dialog.getLyrics(); - QString vocalLanguage = dialog.getVocalLanguage(); - - SongItem newSong(caption, lyrics); - newSong.vocalLanguage = vocalLanguage; + SongItem newSong = dialog.getSong(); songModel->addSong(newSong); // Select the new item @@ -335,18 +330,10 @@ void MainWindow::on_songListView_doubleClicked(const QModelIndex &index) { SongItem song = songModel->getSong(row); - SongDialog dialog(this, song.caption, song.lyrics, song.vocalLanguage); + SongDialog dialog(this, song); if (dialog.exec() == QDialog::Accepted) - { - QString caption = dialog.getCaption(); - QString lyrics = dialog.getLyrics(); - QString vocalLanguage = dialog.getVocalLanguage(); - - songModel->setData(songModel->index(row, 1), caption, SongListModel::CaptionRole); - songModel->setData(songModel->index(row, 1), lyrics, SongListModel::LyricsRole); - songModel->setData(songModel->index(row, 1), vocalLanguage, SongListModel::VocalLanguageRole); - } + songModel->updateSong(songModel->index(row, 1), dialog.getSong()); } connect(ui->songListView, &QTableView::doubleClicked, this, &MainWindow::on_songListView_doubleClicked); @@ -695,17 +682,13 @@ bool MainWindow::savePlaylistToJson(const QString &filePath, const QList(song.uniqueId); - songObj["use_cot_caption"] = song.cotCaption; + song.store(songObj); songsArray.append(songObj); } QJsonObject rootObj; rootObj["songs"] = songsArray; - rootObj["version"] = "1.0"; + rootObj["version"] = "1.1"; QJsonDocument doc(rootObj); QByteArray jsonData = doc.toJson(); @@ -754,8 +737,7 @@ bool MainWindow::loadPlaylistFromJson(const QString &filePath, QList & QJsonObject rootObj = doc.object(); - // Check for version compatibility - if (rootObj.contains("version") && rootObj["version"].toString() != "1.0") + if (rootObj.contains("version") && rootObj["version"].toString() != "1.0" && rootObj["version"].toString() != "1.1") { qWarning() << "Unsupported playlist version:" << rootObj["version"].toString(); return false; @@ -777,24 +759,7 @@ bool MainWindow::loadPlaylistFromJson(const QString &filePath, QList & continue; QJsonObject songObj = value.toObject(); - SongItem song; - - if (songObj.contains("caption")) - song.caption = songObj["caption"].toString(); - - if (songObj.contains("lyrics")) - song.lyrics = songObj["lyrics"].toString(); - - if (songObj.contains("vocalLanguage")) - song.vocalLanguage = songObj["vocalLanguage"].toString(); - - if (songObj.contains("uniqueId")) - song.uniqueId = static_cast(songObj["uniqueId"].toInteger()); - else - song.uniqueId = QRandomGenerator::global()->generate64(); - - song.cotCaption = songObj["use_cot_caption"].toBool(true); - + SongItem song(songObj); songs.append(song); } diff --git a/src/SongDialog.cpp b/src/SongDialog.cpp index 6f5db6b..58f4ec6 100644 --- a/src/SongDialog.cpp +++ b/src/SongDialog.cpp @@ -5,19 +5,19 @@ #include "ui_SongDialog.h" #include -SongDialog::SongDialog(QWidget *parent, const QString &caption, const QString &lyrics, const QString &vocalLanguage, bool cotEnabled) +SongDialog::SongDialog(QWidget *parent, const SongItem &song) : QDialog(parent), - ui(new Ui::SongDialog) + song(song), + ui(new Ui::SongDialog) { ui->setupUi(this); - ui->captionEdit->setPlainText(caption); - ui->lyricsEdit->setPlainText(lyrics); - - ui->checkBoxEnhanceCaption->setChecked(cotEnabled); + ui->captionEdit->setPlainText(song.caption); + ui->lyricsEdit->setPlainText(song.lyrics); + ui->checkBoxEnhanceCaption->setChecked(song.cotCaption); // Setup vocal language combo box - ui->vocalLanguageCombo->addItem("--", ""); // Unset + ui->vocalLanguageCombo->addItem("--", ""); ui->vocalLanguageCombo->addItem("English (en)", "en"); ui->vocalLanguageCombo->addItem("Chinese (zh)", "zh"); ui->vocalLanguageCombo->addItem("Japanese (ja)", "ja"); @@ -70,19 +70,69 @@ SongDialog::SongDialog(QWidget *parent, const QString &caption, const QString &l ui->vocalLanguageCombo->addItem("Cantonese (yue)", "yue"); ui->vocalLanguageCombo->addItem("Unknown", "unknown"); - // Set current language if provided - if (!vocalLanguage.isEmpty()) + ui->keyScaleCombo->addItem("--"); + ui->keyScaleCombo->addItem("C major"); + ui->keyScaleCombo->addItem("C# major"); + ui->keyScaleCombo->addItem("D major"); + ui->keyScaleCombo->addItem("D# major"); + ui->keyScaleCombo->addItem("E major"); + ui->keyScaleCombo->addItem("F major"); + ui->keyScaleCombo->addItem("F# major"); + ui->keyScaleCombo->addItem("G major"); + ui->keyScaleCombo->addItem("G# major"); + ui->keyScaleCombo->addItem("A major"); + ui->keyScaleCombo->addItem("A# major"); + ui->keyScaleCombo->addItem("B major"); + ui->keyScaleCombo->addItem("C minor"); + ui->keyScaleCombo->addItem("C# minor"); + ui->keyScaleCombo->addItem("D minor"); + ui->keyScaleCombo->addItem("D# minor"); + ui->keyScaleCombo->addItem("E minor"); + ui->keyScaleCombo->addItem("F minor"); + ui->keyScaleCombo->addItem("F# minor"); + ui->keyScaleCombo->addItem("G minor"); + ui->keyScaleCombo->addItem("G# minor"); + ui->keyScaleCombo->addItem("A minor"); + ui->keyScaleCombo->addItem("A# minor"); + ui->keyScaleCombo->addItem("B minor"); + + if (!song.vocalLanguage.isEmpty()) { - int index = ui->vocalLanguageCombo->findData(vocalLanguage); + int index = ui->vocalLanguageCombo->findData(song.vocalLanguage); if (index >= 0) { ui->vocalLanguageCombo->setCurrentIndex(index); } + else + { + ui->vocalLanguageCombo->addItem(song.vocalLanguage); + ui->vocalLanguageCombo->setCurrentIndex(ui->vocalLanguageCombo->count()-1); + } } else { - ui->vocalLanguageCombo->setCurrentIndex(0); // Default to unset + ui->vocalLanguageCombo->setCurrentIndex(0); } + + if (!song.key.isEmpty()) + { + int index = ui->keyScaleCombo->findText(song.key); + if (index >= 0) + { + ui->keyScaleCombo->setCurrentIndex(index); + } + else + { + ui->keyScaleCombo->addItem(song.key); + ui->keyScaleCombo->setCurrentIndex(ui->keyScaleCombo->count()-1); + } + } + else + { + ui->keyScaleCombo->setCurrentIndex(0); + } + + ui->bpmSpinBox->setValue(song.bpm); } SongDialog::~SongDialog() @@ -90,30 +140,10 @@ SongDialog::~SongDialog() delete ui; } -QString SongDialog::getCaption() const -{ - return ui->captionEdit->toPlainText(); -} - -QString SongDialog::getLyrics() const -{ - return ui->lyricsEdit->toPlainText(); -} - -QString SongDialog::getVocalLanguage() const -{ - return ui->vocalLanguageCombo->currentData().toString(); -} - -bool SongDialog::getCotEnabled() const -{ - return ui->checkBoxEnhanceCaption->isChecked(); -} - void SongDialog::on_okButton_clicked() { // Validate that caption is not empty - QString caption = getCaption(); + QString caption = ui->captionEdit->toPlainText(); if (caption.trimmed().isEmpty()) { QMessageBox::warning(this, "Invalid Input", "Caption cannot be empty."); @@ -126,4 +156,19 @@ void SongDialog::on_okButton_clicked() void SongDialog::on_cancelButton_clicked() { reject(); -} \ No newline at end of file +} + +const SongItem& SongDialog::getSong() +{ + song.caption = ui->captionEdit->toPlainText(); + song.lyrics = ui->lyricsEdit->toPlainText(); + song.vocalLanguage = ui->vocalLanguageCombo->currentData().toString(); + song.cotCaption = ui->checkBoxEnhanceCaption->isChecked(); + if(ui->keyScaleCombo->currentIndex() > 0) + song.key = ui->keyScaleCombo->currentText(); + else + song.key = ""; + song.bpm = ui->bpmSpinBox->value(); + return song; +} + diff --git a/src/SongDialog.h b/src/SongDialog.h index 7230814..1ab08de 100644 --- a/src/SongDialog.h +++ b/src/SongDialog.h @@ -9,6 +9,8 @@ #include #include +#include "SongItem.h" + namespace Ui { class SongDialog; @@ -17,16 +19,13 @@ class SongDialog; class SongDialog : public QDialog { Q_OBJECT + SongItem song; public: - explicit SongDialog(QWidget *parent = nullptr, const QString &caption = "", const QString &lyrics = "", - const QString &vocalLanguage = "", bool cotEnabled = true); + explicit SongDialog(QWidget *parent = nullptr, const SongItem& song = SongItem()); ~SongDialog(); - QString getCaption() const; - QString getLyrics() const; - QString getVocalLanguage() const; - bool getCotEnabled() const; + const SongItem& getSong(); private slots: void on_okButton_clicked(); diff --git a/src/SongDialog.ui b/src/SongDialog.ui index 8c0710a..56e0842 100644 --- a/src/SongDialog.ui +++ b/src/SongDialog.ui @@ -58,21 +58,66 @@ - - - Vocal Language: + + + 5 - - Qt::AlignmentFlag::AlignTop - - + + + + Keyscale + + + + + + + true + + + + + + + Vocal Language: + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + true + + + + + + + Bpm (0 for llm chooses) + + + + + + + 9999 + + + + - - - true + + + 5 - + + 0 + + diff --git a/src/SongItem.cpp b/src/SongItem.cpp new file mode 100644 index 0000000..68eef23 --- /dev/null +++ b/src/SongItem.cpp @@ -0,0 +1,43 @@ +#include "SongItem.h" + +SongItem::SongItem(const QString &caption, const QString &lyrics) + : caption(caption), lyrics(lyrics), cotCaption(true) +{ + uniqueId = QRandomGenerator::global()->generate64(); +} + +SongItem::SongItem(const QJsonObject& json) +{ + load(json); +} + +void SongItem::store(QJsonObject &json) const +{ + if(!caption.isEmpty()) + json["caption"] = caption; + if(!lyrics.isEmpty()) + json["lyrics"] = lyrics; + json["unique_id"] = QString::number(uniqueId); + json["use_cot_caption"] = cotCaption; + if(!vocalLanguage.isEmpty()) + json["vocal_language"] = vocalLanguage; + if(!key.isEmpty()) + json["keyscale"] = key; + if(bpm != 0) + json["bpm"] = static_cast(bpm); +} + +void SongItem::load(const QJsonObject &json) +{ + caption = json["caption"].toString(); + lyrics = json["lyrics"].toString(); + if(json.contains("unique_id")) + uniqueId = json["unique_id"].toString().toULongLong(); + else + uniqueId = QRandomGenerator::global()->generate64(); + cotCaption = json["use_cot_caption"].toBool(true); + vocalLanguage = json["vocal_language"].toString(); + key = json["keyscale"].toString(); + bpm = json["bpm"].toInt(0); +} + diff --git a/src/SongItem.h b/src/SongItem.h index 7084e0f..a8e993c 100644 --- a/src/SongItem.h +++ b/src/SongItem.h @@ -7,22 +7,25 @@ #include #include #include +#include class SongItem { public: QString caption; QString lyrics; - uint64_t uniqueId; - QString file; + unsigned int bpm; + QString key; QString vocalLanguage; bool cotCaption; + + uint64_t uniqueId; + QString file; QString json; - inline SongItem(const QString &caption = "", const QString &lyrics = "") - : caption(caption), lyrics(lyrics), cotCaption(true) - { - // Generate a unique ID using a cryptographically secure random number - uniqueId = QRandomGenerator::global()->generate64(); - } + SongItem(const QString &caption = "", const QString &lyrics = ""); + SongItem(const QJsonObject& json); + + void store(QJsonObject& json) const; + void load(const QJsonObject& json); }; diff --git a/src/SongListModel.cpp b/src/SongListModel.cpp index a77747a..0b40f05 100644 --- a/src/SongListModel.cpp +++ b/src/SongListModel.cpp @@ -111,6 +111,26 @@ bool SongListModel::setData(const QModelIndex &index, const QVariant &value, int return true; } +void SongListModel::updateSong(const QModelIndex &index, const SongItem& song) +{ + const SongItem &oldSong = songList[index.row()]; + + if(song.caption != oldSong.caption) + { + emit dataChanged(index, index, {CaptionRole}); + } + if(song.lyrics != oldSong.lyrics) + { + emit dataChanged(index, index, {LyricsRole}); + } + if(song.vocalLanguage != oldSong.vocalLanguage) + { + emit dataChanged(index, index, {VocalLanguageRole}); + } + + songList[index.row()] = song; +} + Qt::ItemFlags SongListModel::flags(const QModelIndex &index) const { if (!index.isValid()) diff --git a/src/SongListModel.h b/src/SongListModel.h index c13ff4c..bf1e22e 100644 --- a/src/SongListModel.h +++ b/src/SongListModel.h @@ -37,6 +37,7 @@ public: // Editable: bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + void updateSong(const QModelIndex &index, const SongItem& song); Qt::ItemFlags flags(const QModelIndex& index) const override; // Add/remove songs