diff --git a/src/converter/internal/convertercontroller.cpp b/src/converter/internal/convertercontroller.cpp index 980941c466eab..92c809eed9f59 100644 --- a/src/converter/internal/convertercontroller.cpp +++ b/src/converter/internal/convertercontroller.cpp @@ -34,6 +34,7 @@ #include "internal/converterutils.h" #include "log.h" +#include using namespace mu::converter; using namespace mu::project; @@ -44,6 +45,7 @@ using namespace muse::io; static const std::string PDF_SUFFIX = "pdf"; static const std::string PNG_SUFFIX = "png"; static const std::string SVG_SUFFIX = "svg"; +static const std::string MP3_SUFFIX = "mp3"; Ret ConverterController::batchConvert(const muse::io::path_t& batchJobFile, const muse::io::path_t& stylePath, bool forceMode, const String& soundProfile, const muse::UriQuery& extensionUri, muse::ProgressPtr progress) @@ -131,12 +133,6 @@ Ret ConverterController::fileConvert(const muse::io::path_t& in, const muse::io: return make_ret(Err::UnknownError); } - std::string suffix = io::suffix(out); - auto writer = writers()->writer(suffix); - if (!writer) { - return make_ret(Err::ConvertTypeUnknown); - } - Ret ret = notationProject->load(in, stylePath, forceMode); if (!ret) { LOGE() << "failed load notation, err: " << ret.toString() << ", path: " << in; @@ -158,6 +154,19 @@ Ret ConverterController::fileConvert(const muse::io::path_t& in, const muse::io: globalContext()->setCurrentProject(notationProject); + // Check if this is a part conversion job + QString baseName = QString::fromStdString(io::completeBasename(out).toStdString()); + if (baseName.contains('*')) { + return convertScoreParts(in, out, stylePath, forceMode); + } + + std::string suffix = io::suffix(out); + + auto writer = writers()->writer(suffix); + if (!writer) { + return make_ret(Err::ConvertTypeUnknown); + } + // use a extension for convert if (extensionUri.isValid()) { ret = convertByExtension(writer, notationProject->masterNotation()->notation(), out, extensionUri); @@ -215,6 +224,8 @@ Ret ConverterController::convertScoreParts(const muse::io::path_t& in, const mus ret = convertScorePartsToPdf(writer, notationProject->masterNotation(), out); } else if (suffix == PNG_SUFFIX) { ret = convertScorePartsToPngs(writer, notationProject->masterNotation(), out); + } else if (suffix == MP3_SUFFIX) { + ret = convertScorePartsToMp3(writer, notationProject->masterNotation(), out); } else { ret = make_ret(Ret::Code::NotSupported); } @@ -252,7 +263,6 @@ RetVal ConverterController::parseBatchJob(const m Job job; job.in = correctUserInputPath(obj["in"].toString()); - job.out = correctUserInputPath(obj["out"].toString()); QJsonObject transposeOptionsObj = obj["transpose"].toObject(); if (!transposeOptionsObj.isEmpty()) { @@ -261,12 +271,27 @@ RetVal ConverterController::parseBatchJob(const m rv.ret = transposeOptions.ret; return rv; } - job.transposeOptions = transposeOptions.val; } - if (!job.in.empty() && !job.out.empty()) { + QJsonValue outValue = obj["out"]; + if (outValue.isString()) { + job.out = correctUserInputPath(outValue.toString()); rv.val.push_back(std::move(job)); + } else if (outValue.isArray()) { + QJsonArray outArray = outValue.toArray(); + for (const QJsonValue& outItem : outArray) { + Job partJob = job; // Copy the input path + if (outItem.isString()) { + partJob.out = correctUserInputPath(outItem.toString()); + } else if (outItem.isArray() && outItem.toArray().size() == 2) { + QJsonArray partOutArray = outItem.toArray(); + QString prefix = correctUserInputPath(partOutArray[0].toString()); + QString suffix = partOutArray[1].toString(); + partJob.out = prefix + "*" + suffix; // Use "*" as a placeholder for part names + } + rv.val.push_back(std::move(partJob)); + } } } @@ -368,28 +393,32 @@ Ret ConverterController::convertScorePartsToPdf(INotationWriterPtr writer, IMast TRACEFUNC; INotationPtrList notations; - notations.push_back(masterNotation->notation()); - for (IExcerptNotationPtr e : masterNotation->excerpts()) { notations.push_back(e->notation()); } - File file(out); - if (!file.open(File::WriteOnly)) { - return make_ret(Err::OutFileFailedOpen); - } + for (size_t i = 0; i < notations.size(); ++i) { + QString partName = notations[i]->name(); + QString baseName = QString::fromStdString(io::completeBasename(out).toStdString()); + muse::io::path_t partOut = io::dirpath(out) + "/" + baseName.replace("*", partName).toStdString() + ".pdf"; - INotationWriter::Options options { - { INotationWriter::OptionKey::UNIT_TYPE, Val(INotationWriter::UnitType::MULTI_PART) }, - }; + File file(partOut); + if (!file.open(File::WriteOnly)) { + return make_ret(Err::OutFileFailedOpen); + } - Ret ret = writer->writeList(notations, file, options); - if (!ret) { - LOGE() << "failed write, err: " << ret.toString() << ", path: " << out; - return make_ret(Err::OutFileFailedWrite); - } + INotationWriter::Options options { + { INotationWriter::OptionKey::UNIT_TYPE, Val(INotationWriter::UnitType::PER_PART) }, + }; - file.close(); + Ret ret = writer->write(notations[i], file, options); + if (!ret) { + LOGE() << "failed write, err: " << ret.toString() << ", path: " << partOut; + return make_ret(Err::OutFileFailedWrite); + } + + file.close(); + } return make_ret(Ret::Code::Ok); } @@ -399,23 +428,55 @@ Ret ConverterController::convertScorePartsToPngs(INotationWriterPtr writer, mu:: { TRACEFUNC; - Ret ret = convertPageByPage(writer, masterNotation->notation(), out); - if (!ret) { - return ret; + INotationPtrList notations; + for (IExcerptNotationPtr e : masterNotation->excerpts()) { + notations.push_back(e->notation()); } - INotationPtrList excerpts; + for (size_t i = 0; i < notations.size(); i++) { + QString partName = notations[i]->name(); + QString baseName = QString::fromStdString(io::completeBasename(out).toStdString()); + muse::io::path_t pngFilePath = io::dirpath(out) + "/" + baseName.replace("*", partName).toStdString() + ".png"; + Ret ret2 = convertPageByPage(writer, notations[i], pngFilePath); + if (!ret2) { + return ret2; + } + } + + return make_ret(Ret::Code::Ok); +} + +Ret ConverterController::convertScorePartsToMp3(INotationWriterPtr writer, IMasterNotationPtr masterNotation, + const muse::io::path_t& out) const +{ + TRACEFUNC; + + INotationPtrList notations; for (IExcerptNotationPtr e : masterNotation->excerpts()) { - excerpts.push_back(e->notation()); + notations.push_back(e->notation()); } - muse::io::path_t pngFilePath = io::dirpath(out) + "/" + muse::io::path_t(io::completeBasename(out) + "-excerpt.png"); + for (size_t i = 0; i < notations.size(); ++i) { + QString partName = notations[i]->name(); + QString baseName = QString::fromStdString(io::completeBasename(out).toStdString()); + muse::io::path_t partOut = io::dirpath(out) + "/" + baseName.replace("*", partName).toStdString() + ".mp3"; - for (size_t i = 0; i < excerpts.size(); i++) { - Ret ret2 = convertPageByPage(writer, excerpts[i], pngFilePath); - if (!ret2) { - return ret2; + File file(partOut); + if (!file.open(File::WriteOnly)) { + return make_ret(Err::OutFileFailedOpen); + } + + INotationWriter::Options options { + { INotationWriter::OptionKey::UNIT_TYPE, Val(INotationWriter::UnitType::PER_PART) }, + }; + file.setMeta("file_path", partOut.toStdString()); + Ret ret = writer->write(notations[i], file, options); + if (!ret) { + LOGE() << "failed write, err: " << ret.toString() << ", path: " << partOut; + return make_ret(Err::OutFileFailedWrite); } + + file.close(); } return make_ret(Ret::Code::Ok); diff --git a/src/converter/internal/convertercontroller.h b/src/converter/internal/convertercontroller.h index 475783191bd69..bf43affd4f445 100644 --- a/src/converter/internal/convertercontroller.h +++ b/src/converter/internal/convertercontroller.h @@ -103,5 +103,7 @@ class ConverterController : public IConverterController, public muse::Injectable const muse::io::path_t& out) const; muse::Ret convertScorePartsToPngs(project::INotationWriterPtr writer, notation::IMasterNotationPtr masterNotation, const muse::io::path_t& out) const; + muse::Ret convertScorePartsToMp3(project::INotationWriterPtr writer, notation::IMasterNotationPtr masterNotation, + const muse::io::path_t& out) const; }; }