Created
September 21, 2023 20:55
-
-
Save zopieux/63eb756002d12284b7a75457c7b67443 to your computer and use it in GitHub Desktop.
MuseScore audio export hack for training aids
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From bbd014bf407b2ec97fa18cca89d46d1867f62c83 Mon Sep 17 00:00:00 2001 | |
From: Alexandre Macabies <[email protected]> | |
Date: Thu, 21 Sep 2023 22:53:31 +0200 | |
Subject: [PATCH] Audio export conversion hack for training aids. | |
--- | |
default.nix | 174 +++++++++++++ | |
flake.lock | 61 +++++ | |
flake.nix | 27 ++ | |
src/app/app.cpp | 2 + | |
src/app/commandlineparser.cpp | 13 + | |
src/app/commandlineparser.h | 6 + | |
.../internal/convertercontroller.cpp | 243 ++++++++++++++++-- | |
src/converter/internal/convertercontroller.h | 6 + | |
.../audioexport/iaudioexportconfiguration.h | 6 + | |
.../internal/audioexportconfiguration.cpp | 20 ++ | |
.../internal/audioexportconfiguration.h | 8 + | |
11 files changed, 548 insertions(+), 18 deletions(-) | |
create mode 100644 default.nix | |
create mode 100644 flake.lock | |
create mode 100644 flake.nix | |
diff --git a/default.nix b/default.nix | |
new file mode 100644 | |
index 0000000000..da80ce24ee | |
--- /dev/null | |
+++ b/default.nix | |
@@ -0,0 +1,174 @@ | |
+{ stdenv | |
+, lib | |
+, fetchFromGitHub | |
+, fetchpatch | |
+, cmake | |
+, ffmpeg | |
+, wrapQtAppsHook | |
+, pkg-config | |
+, ninja | |
+, alsa-lib | |
+, alsa-plugins | |
+, freetype | |
+, libjack2 | |
+, lame | |
+, libogg | |
+, libpulseaudio | |
+, libsndfile | |
+, libvorbis | |
+, portaudio | |
+, portmidi | |
+, qtbase | |
+, qtdeclarative | |
+, qtgraphicaleffects | |
+, flac | |
+, qtquickcontrols | |
+, qtquickcontrols2 | |
+, qtscript | |
+, qtsvg | |
+, qtxmlpatterns | |
+, qtnetworkauth | |
+, qtx11extras | |
+, darwin | |
+}: | |
+ | |
+let | |
+ stdenv' = if stdenv.isDarwin then darwin.apple_sdk_11_0.stdenv else stdenv; | |
+ # portaudio propagates Darwin frameworks. Rebuild it using the 11.0 stdenv | |
+ # from Qt and the 11.0 SDK frameworks. | |
+ portaudio' = | |
+ if stdenv.isDarwin then | |
+ portaudio.override | |
+ { | |
+ stdenv = stdenv'; | |
+ inherit (darwin.apple_sdk_11_0.frameworks) | |
+ AudioUnit | |
+ AudioToolbox | |
+ CoreAudio | |
+ CoreServices | |
+ Carbon | |
+ ; | |
+ } else portaudio; | |
+in | |
+stdenv'.mkDerivation rec { | |
+ pname = "musescore"; | |
+ version = "4.1.1"; | |
+ | |
+ src = ./.; | |
+ | |
+ patches = [ | |
+ # Upstream from some reason wants to install qml files from qtbase in | |
+ # installPhase, this patch removes this behavior. See: | |
+ # https://github.com/musescore/MuseScore/issues/18665 | |
+ # (fetchpatch { | |
+ # url = "https://github.com/doronbehar/MuseScore/commit/f48448a3ede46f5a7ef470940072fbfb6742487c.patch"; | |
+ # hash = "sha256-UEc7auscnW0KMfWkLKQtm+UstuTNsuFeoNJYIidIlwM="; | |
+ # }) | |
+ # Upstream removed the option to use system freetype library in v4.1.0, | |
+ # causing the app to crash on systems when the outdated bundled freetype | |
+ # tries to load the Noto Sans font. For more info on the crash itself, | |
+ # see #244409 and https://github.com/musescore/MuseScore/issues/18795. | |
+ # For now, re-add the option ourselves. The fix has been merged upstream, | |
+ # so we can remove this patch with the next version. In the future, we | |
+ # may replace the other bundled thirdparty libs with system libs, see | |
+ # https://github.com/musescore/MuseScore/issues/11572. | |
+ # (fetchpatch { | |
+ # url = "https://github.com/musescore/MuseScore/commit/9ab6b32b1c3b990cfa7bb172ee8112521dc2269c.patch"; | |
+ # hash = "sha256-5GA29Z+o3I/uDTTDbkauZ8/xSdCE6yY93phMSY0ea7s="; | |
+ # }) | |
+ ]; | |
+ | |
+ cmakeFlags = [ | |
+ "-DMUSESCORE_BUILD_MODE=debug" | |
+ # Disable the build and usage of the `/bin/crashpad_handler` utility - it's | |
+ # not useful on NixOS, see: | |
+ # https://github.com/musescore/MuseScore/issues/15571 | |
+ "-DMUE_BUILD_CRASHPAD_CLIENT=OFF" | |
+ # Use our freetype | |
+ "-DMUE_COMPILE_USE_SYSTEM_FREETYPE=ON" | |
+ # From some reason, in $src/build/cmake/SetupBuildEnvironment.cmake, | |
+ # upstream defaults to compiling to x86_64 only, unless this cmake flag is | |
+ # set | |
+ "-DMUE_COMPILE_BUILD_MACOS_APPLE_SILICON=ON" | |
+ # Don't bundle qt qml files, relevant really only for darwin, but we set | |
+ # this for all platforms anyway. | |
+ "-DMUE_COMPILE_INSTALL_QTQML_FILES=OFF" | |
+ "-DMUE_BUILD_VIDEOEXPORT_MODULE=ON" | |
+ # "-DCMAKE_INSTALL_PREFIX=/tmp/muselol" | |
+ ]; | |
+ | |
+ qtWrapperArgs = [ | |
+ # MuseScore JACK backend loads libjack at runtime. | |
+ "--prefix ${lib.optionalString stdenv.isDarwin "DY"}LD_LIBRARY_PATH : ${lib.makeLibraryPath [ ffmpeg libjack2 ]}" | |
+ ] ++ lib.optionals (stdenv.isLinux) [ | |
+ "--set ALSA_PLUGIN_DIR ${alsa-plugins}/lib/alsa-lib" | |
+ ] ++ lib.optionals (!stdenv.isDarwin) [ | |
+ # There are some issues with using the wayland backend, see: | |
+ # https://musescore.org/en/node/321936 | |
+ "--set-default QT_QPA_PLATFORM xcb" | |
+ ]; | |
+ | |
+ # HACK `propagatedSandboxProfile` does not appear to actually propagate the | |
+ # sandbox profile from `qtbase`, see: | |
+ # https://github.com/NixOS/nixpkgs/issues/237458 | |
+ sandboxProfile = toString qtbase.__propagatedSandboxProfile or null; | |
+ | |
+ nativeBuildInputs = [ | |
+ wrapQtAppsHook | |
+ cmake | |
+ pkg-config | |
+ ninja | |
+ ]; | |
+ | |
+ buildInputs = [ | |
+ libjack2 | |
+ freetype | |
+ lame | |
+ libogg | |
+ libpulseaudio | |
+ libsndfile | |
+ libvorbis | |
+ ffmpeg | |
+ portaudio' | |
+ portmidi | |
+ flac | |
+ qtbase | |
+ qtdeclarative | |
+ qtgraphicaleffects | |
+ qtquickcontrols | |
+ qtquickcontrols2 | |
+ qtscript | |
+ qtsvg | |
+ qtxmlpatterns | |
+ qtnetworkauth | |
+ qtx11extras | |
+ ] ++ lib.optionals stdenv.isLinux [ | |
+ alsa-lib | |
+ ]; | |
+ | |
+ postInstall = '' | |
+ # Remove unneeded bundled libraries and headers | |
+ rm -r $out/{include,lib} | |
+ '' + lib.optionalString stdenv.isDarwin '' | |
+ mkdir -p "$out/Applications" | |
+ mv "$out/mscore.app" "$out/Applications/mscore.app" | |
+ mkdir -p $out/bin | |
+ ln -s $out/Applications/mscore.app/Contents/MacOS/mscore $out/bin/mscore. | |
+ ''; | |
+ | |
+ # Don't run bundled upstreams tests, as they require a running X window system. | |
+ doCheck = false; | |
+ | |
+ # passthru.tests = nixosTests.musescore; | |
+ | |
+ meta = with lib; { | |
+ description = "Music notation and composition software"; | |
+ homepage = "https://musescore.org/"; | |
+ license = licenses.gpl3Only; | |
+ maintainers = with maintainers; [ vandenoever doronbehar ]; | |
+ # on aarch64-linux: | |
+ # error: cannot convert '<brace-enclosed initializer list>' to 'float32x4_t' in assignment | |
+ broken = (stdenv.isLinux && stdenv.isAarch64); | |
+ mainProgram = "mscore"; | |
+ }; | |
+} | |
diff --git a/flake.lock b/flake.lock | |
new file mode 100644 | |
index 0000000000..66fbd3ba68 | |
--- /dev/null | |
+++ b/flake.lock | |
@@ -0,0 +1,61 @@ | |
+{ | |
+ "nodes": { | |
+ "flake-utils": { | |
+ "inputs": { | |
+ "systems": "systems" | |
+ }, | |
+ "locked": { | |
+ "lastModified": 1694529238, | |
+ "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", | |
+ "owner": "numtide", | |
+ "repo": "flake-utils", | |
+ "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", | |
+ "type": "github" | |
+ }, | |
+ "original": { | |
+ "owner": "numtide", | |
+ "repo": "flake-utils", | |
+ "type": "github" | |
+ } | |
+ }, | |
+ "nixpkgs": { | |
+ "locked": { | |
+ "lastModified": 1694887919, | |
+ "narHash": "sha256-VSBAjjQGLKYG/8dpC/9V7/dLlimkUzdYcibDKjwnzDE=", | |
+ "owner": "NixOS", | |
+ "repo": "nixpkgs", | |
+ "rev": "b2e813ada825ef491546ea81f42ef2d37ce71bf6", | |
+ "type": "github" | |
+ }, | |
+ "original": { | |
+ "owner": "NixOS", | |
+ "ref": "nixpkgs-unstable", | |
+ "repo": "nixpkgs", | |
+ "type": "github" | |
+ } | |
+ }, | |
+ "root": { | |
+ "inputs": { | |
+ "flake-utils": "flake-utils", | |
+ "nixpkgs": "nixpkgs" | |
+ } | |
+ }, | |
+ "systems": { | |
+ "locked": { | |
+ "lastModified": 1681028828, | |
+ "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", | |
+ "owner": "nix-systems", | |
+ "repo": "default", | |
+ "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", | |
+ "type": "github" | |
+ }, | |
+ "original": { | |
+ "owner": "nix-systems", | |
+ "repo": "default", | |
+ "type": "github" | |
+ } | |
+ } | |
+ }, | |
+ "root": "root", | |
+ "version": 7 | |
+} | |
diff --git a/flake.nix b/flake.nix | |
new file mode 100644 | |
index 0000000000..4142b7021f | |
--- /dev/null | |
+++ b/flake.nix | |
@@ -0,0 +1,27 @@ | |
+{ | |
+ inputs = { | |
+ nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; | |
+ flake-utils.url = "github:numtide/flake-utils"; | |
+ }; | |
+ | |
+ outputs = { self, nixpkgs, flake-utils }: | |
+ { | |
+ overlay = final: prev: { | |
+ musescore = prev.libsForQt5.callPackage.callPackage ./default.nix { }; | |
+ }; | |
+ } // flake-utils.lib.eachDefaultSystem (system: | |
+ let | |
+ pkgs = import nixpkgs { inherit system; }; | |
+ musescore = pkgs.libsForQt5.callPackage ./default.nix { }; | |
+ in | |
+ { | |
+ packages = { inherit musescore; default = musescore; }; | |
+ devShells.default = pkgs.mkShell | |
+ rec { | |
+ inputsFrom = [ musescore ]; | |
+ buildInputs = [ pkgs.ffmpeg ]; | |
+ nativeBuildInputs = with pkgs; [ cmake clang clang-tools ]; | |
+ LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [ pkgs.ffmpeg ]; | |
+ }; | |
+ }); | |
+} | |
diff --git a/src/app/app.cpp b/src/app/app.cpp | |
index 22722ea9b4..ca323582a5 100644 | |
--- a/src/app/app.cpp | |
+++ b/src/app/app.cpp | |
@@ -420,6 +420,8 @@ void App::applyCommandLineOptions(const CommandLineParser::Options& options, fra | |
#ifdef MUE_BUILD_IMPORTEXPORT_MODULE | |
audioExportConfiguration()->setExportMp3BitrateOverride(options.exportAudio.mp3Bitrate); | |
+ audioExportConfiguration()->setSplitVoices(options.exportSplit.splitVoices.value_or(false)); | |
+ audioExportConfiguration()->setSoloStaffs(options.exportSplit.soloStaffs); | |
midiImportExportConfiguration()->setMidiImportOperationsFile(options.importMidi.operationsFile); | |
guitarProConfiguration()->setLinkedTabStaffCreated(options.guitarPro.linkedTabStaffCreated); | |
guitarProConfiguration()->setExperimental(options.guitarPro.experimental); | |
diff --git a/src/app/commandlineparser.cpp b/src/app/commandlineparser.cpp | |
index 7468abcf5a..293b8792fe 100644 | |
--- a/src/app/commandlineparser.cpp | |
+++ b/src/app/commandlineparser.cpp | |
@@ -77,6 +77,9 @@ void CommandLineParser::init() | |
m_parser.addOption(QCommandLineOption("template-mode", "Save template mode, no page size")); // and no platform and creationDate tags | |
m_parser.addOption(QCommandLineOption({ "t", "test-mode" }, "Set test mode flag for all files")); // this includes --template-mode | |
+ m_parser.addOption(QCommandLineOption("split-voices", "Explode staff voices before exporting")); | |
+ m_parser.addOption(QCommandLineOption("solo-staff", "Make staff(s) louder than others")); | |
+ | |
m_parser.addOption(QCommandLineOption("session-type", "Startup with given session type", "type")); // see StartupScenario::sessionTypeTromString | |
// Converter mode | |
@@ -231,6 +234,16 @@ void CommandLineParser::parse(int argc, char** argv) | |
} | |
} | |
+ if (m_parser.isSet("split-voices")) | |
+ { | |
+ m_options.exportSplit.splitVoices = true; | |
+ } | |
+ | |
+ if (m_parser.isSet("solo-staff")) | |
+ { | |
+ m_options.exportSplit.soloStaffs = m_parser.values("solo-staff"); | |
+ } | |
+ | |
if (m_parser.isSet("template-mode")) { | |
m_options.notation.templateModeEnabled = true; | |
} | |
diff --git a/src/app/commandlineparser.h b/src/app/commandlineparser.h | |
index c0c03f4753..78134912dd 100644 | |
--- a/src/app/commandlineparser.h | |
+++ b/src/app/commandlineparser.h | |
@@ -64,6 +64,12 @@ public: | |
std::optional<int> mp3Bitrate; | |
} exportAudio; | |
+ struct | |
+ { | |
+ std::optional<bool> splitVoices; | |
+ QStringList soloStaffs; | |
+ } exportSplit; | |
+ | |
struct { | |
std::optional<std::string> resolution; | |
std::optional<int> fps; | |
diff --git a/src/converter/internal/convertercontroller.cpp b/src/converter/internal/convertercontroller.cpp | |
index 849d056c00..5713f1d82b 100644 | |
--- a/src/converter/internal/convertercontroller.cpp | |
+++ b/src/converter/internal/convertercontroller.cpp | |
@@ -30,8 +30,13 @@ | |
#include "io/dir.h" | |
#include "stringutils.h" | |
+// #include "async/async.h" | |
+// #include "async/asyncable.h" | |
#include "convertercodes.h" | |
#include "compat/backendapi.h" | |
+#include "dom/masterscore.h" | |
+#include "notation/imasternotation.h" | |
+// #include "audio/itracks.h" | |
#include "log.h" | |
@@ -68,30 +73,232 @@ mu::Ret ConverterController::fileConvert(const io::path_t& in, const io::path_t& | |
{ | |
TRACEFUNC; | |
- LOGI() << "in: " << in << ", out: " << out; | |
- auto notationProject = notationCreator()->newProject(); | |
- IF_ASSERT_FAILED(notationProject) { | |
- return make_ret(Err::UnknownError); | |
- } | |
- | |
std::string suffix = io::suffix(out); | |
+ auto dir = io::dirpath(out); | |
+ auto filenameNoExt = io::filename(out, false); | |
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; | |
- return make_ret(Err::InFileFailedLoad); | |
+ LOGI() << "in: " << in << ", out: " << out; | |
+ | |
+ String focusIns = {getenv("FOCUSINS")}; | |
+ String quietIns = {getenv("QUIETINS")}; | |
+ | |
+ if (!QString(getenv("EXPLODE")).isEmpty()) | |
+ { | |
+ auto notationProject = notationCreator()->newProject(); | |
+ IF_ASSERT_FAILED(notationProject) | |
+ { | |
+ return make_ret(Err::UnknownError); | |
+ } | |
+ | |
+ 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; | |
+ return make_ret(Err::InFileFailedLoad); | |
+ } | |
+ | |
+ globalContext()->setCurrentProject(notationProject); | |
+ mu::notation::IMasterNotationPtr master = notationProject->masterNotation(); | |
+ mu::notation::INotationPtr notation = notationProject->masterNotation()->notation(); | |
+ mu::engraving::MasterScore *score = notationProject->masterNotation()->masterScore(); | |
+ mu::notation::INotationPartsPtr notationParts = notation->parts(); | |
+ | |
+ PartInstrumentList partList; | |
+ IDList originalIDs; | |
+ size_t idx = 0; | |
+ for (const auto *p : notationParts->partList()) | |
+ { | |
+ originalIDs.emplace_back(p->id()); | |
+ partList.append(PartInstrument{ | |
+ .partId = p->id(), | |
+ .instrumentTemplate = instrumentsRepository()->instrumentTemplate("men"), | |
+ .isExistingPart = true, | |
+ .isSoloist = p->soloist(), | |
+ }); | |
+ partList.append(PartInstrument{ | |
+ .partId = notationParts->partList().size() + (idx++), | |
+ .instrumentTemplate = instrumentsRepository()->instrumentTemplate("men"), | |
+ .isExistingPart = false, | |
+ .isSoloist = p->soloist(), | |
+ }); | |
+ } | |
+ master->parts()->setParts(partList, master->parts()->scoreOrder()); | |
+ | |
+ score->doLayout(); | |
+ | |
+#if 1 | |
+ // Select all dynamics. | |
+ ElementPattern pattern; | |
+ pattern.type = int(ElementType::DYNAMIC); | |
+ pattern.subtype = 0; | |
+ pattern.staffStart = mu::nidx; | |
+ pattern.staffEnd = mu::nidx; | |
+ pattern.voice = mu::nidx; | |
+ pattern.system = 0; | |
+ pattern.subtypeValid = false; | |
+ pattern.durationTicks = Fraction(-1, 1); | |
+ score->scanElements(&pattern, score->collectMatch); | |
+ LOGI() << "SO MANY DYNAMICS " << pattern.el.size(); | |
+ score->select(0, SelectType::SINGLE, 0); | |
+ score->select(pattern.el, SelectType::ADD, 0); | |
+ score->setUpdateAll(); | |
+ score->update(); | |
+ // And delete them. | |
+ score->startCmd(); | |
+ score->cmdDeleteSelection(); | |
+ score->endCmd(); | |
+#endif | |
+ | |
+ for (const auto &id : originalIDs) | |
+ { | |
+ // Select all. | |
+ mu::engraving::Selection sel(score); | |
+ const auto idx = score->staffIdx(score->partById(id)); | |
+ sel.setRange(score->firstMeasureMM()->first(), score->lastMeasureMM()->last(), idx, idx + 1); | |
+ score->setSelection(sel); | |
+ score->setUpdateAll(); | |
+ score->update(); | |
+ // And explode them. | |
+ score->startCmd(); | |
+ score->cmdExplode(); | |
+ score->endCmd(); | |
+ score->doLayout(); | |
+ } | |
+ | |
+ QStringList names; | |
+ for (const auto &id : originalIDs) | |
+ { | |
+ auto lg = notationParts->part(id)->longName(); | |
+ auto pp = lg.toQString().split("\n"); | |
+ names.append(pp); | |
+ } | |
+ LOGI() << "SPLIT NAMES ARE " << names; | |
+ if (size_t(names.size()) != notationParts->partList().size()) | |
+ { | |
+ LOGI() << "Split name list " << names.size() << " does not match exploded part size " << notationParts->partList().size(); | |
+ // globalContext()->setCurrentProject(nullptr); | |
+ // return make_ret(Ret::Code::UnknownError); | |
+ } | |
+ | |
+#if 0 | |
+ for (const auto *part : notationParts->partList()) | |
+ { | |
+ for (const auto &ins : part->instrumentTrackIdList()) | |
+ { | |
+ auto outP = notationProject->audioSettings()->trackOutputParams(ins); | |
+ outP.volume = 0.0; | |
+ outP.balance = 0.0; | |
+ outP.auxSends.clear(); | |
+ notationProject->audioSettings()->setTrackOutputParams(ins, outP); | |
+ auto inP = notationProject->audioSettings()->trackInputParams(ins); | |
+ inP.resourceMeta.type = mu::audio::AudioResourceType::FluidSoundfont; | |
+ inP.resourceMeta.vendor = "Fluid"; | |
+ inP.resourceMeta.id = focusIns.append(String("\0\0")).toStdString(); | |
+ inP.resourceMeta.attributes.emplace(audio::synth::SOUNDFONT_NAME_ATTRIBUTE, focusIns); | |
+ inP.resourceMeta.attributes.emplace(audio::synth::PRESET_NAME_ATTRIBUTE, focusIns); | |
+ inP.resourceMeta.attributes.emplace(audio::synth::PRESET_BANK_ATTRIBUTE, "0"); | |
+ inP.resourceMeta.attributes.emplace(audio::synth::PRESET_PROGRAM_ATTRIBUTE, "0"); | |
+ notationProject->audioSettings()->setTrackInputParams(ins, inP); | |
+ } | |
+ } | |
+#endif | |
+ notationProject->save(out.appendingSuffix("mscz"), SaveMode::Save); | |
} | |
+ else | |
+ { | |
+ auto notationProject = notationCreator()->newProject(); | |
+ IF_ASSERT_FAILED(notationProject) | |
+ { | |
+ return make_ret(Err::UnknownError); | |
+ } | |
- globalContext()->setCurrentProject(notationProject); | |
+ Ret ret = notationProject->load(in, stylePath, forceMode); | |
+ if (!ret) | |
+ { | |
+ LOGE() << "failed load notation, err: " << ret.toString() << ", path: " << in; | |
+ return make_ret(Err::InFileFailedLoad); | |
+ } | |
- if (isConvertPageByPage(suffix)) { | |
- ret = convertPageByPage(writer, notationProject->masterNotation()->notation(), out); | |
- } else { | |
- ret = convertFullNotation(writer, notationProject->masterNotation()->notation(), out); | |
+ globalContext()->setCurrentProject(notationProject); | |
+ auto master = notationProject->masterNotation(); | |
+ auto notation = notationProject->masterNotation()->notation(); | |
+ auto score = notationProject->masterNotation()->masterScore(); | |
+ auto notationParts = notation->parts(); | |
+ | |
+ QString focusRaw(getenv("FOCUS")); | |
+ QSet<size_t> focusIdx; | |
+ for (const auto &p : focusRaw.split(",")) | |
+ { | |
+ focusIdx << p.toInt(); | |
+ } | |
+ | |
+ QString verbatimRaw(getenv("VERBATIM")); | |
+ QSet<size_t> verbatimIdx; | |
+ for (const auto &p : verbatimRaw.split(",")) | |
+ { | |
+ verbatimIdx << p.toInt(); | |
+ } | |
+ | |
+ score->doLayout(); | |
+ ElementPattern pattern; | |
+ pattern.type = int(ElementType::DYNAMIC); | |
+ pattern.subtype = 0; | |
+ pattern.staffStart = mu::nidx; | |
+ pattern.staffEnd = mu::nidx; | |
+ pattern.voice = mu::nidx; | |
+ pattern.system = 0; | |
+ pattern.subtypeValid = false; | |
+ pattern.durationTicks = Fraction(-1, 1); | |
+ score->scanElements(&pattern, score->collectMatch); | |
+ LOGI() << "SO MANY DYNAMICS " << pattern.el.size(); | |
+ score->select(0, SelectType::SINGLE, 0); | |
+ score->select(pattern.el, SelectType::ADD, 0); | |
+ score->setUpdateAll(); | |
+ score->update(); | |
+ // And delete them. | |
+ score->startCmd(); | |
+ score->cmdDeleteSelection(); | |
+ score->endCmd(); | |
+ | |
+ size_t partIdx = 0; | |
+ for (const auto *part : notationParts->partList()) | |
+ { | |
+ for (const auto &ins : part->instrumentTrackIdList()) | |
+ { | |
+ if (verbatimIdx.contains(partIdx)) | |
+ continue; | |
+ const bool isFocus = focusIdx.contains(partIdx); | |
+ auto outP = notationProject->audioSettings()->trackOutputParams(ins); | |
+ outP.volume = isFocus ? 0.0 : -6.0; | |
+ // outP.balance = isFocus ? +0.5 : -0.5; | |
+ outP.auxSends.clear(); | |
+ notationProject->audioSettings()->setTrackOutputParams(ins, outP); | |
+ auto inP = notationProject->audioSettings()->trackInputParams(ins); | |
+ inP.resourceMeta.type = mu::audio::AudioResourceType::FluidSoundfont; | |
+ inP.resourceMeta.vendor = "Fluid"; | |
+ inP.resourceMeta.id = (isFocus ? focusIns : quietIns).append(String("\0\0")).toStdString(); | |
+ inP.resourceMeta.attributes.emplace(audio::synth::SOUNDFONT_NAME_ATTRIBUTE, isFocus ? focusIns : quietIns); | |
+ inP.resourceMeta.attributes.emplace(audio::synth::PRESET_NAME_ATTRIBUTE, isFocus ? focusIns : quietIns); | |
+ inP.resourceMeta.attributes.emplace(audio::synth::PRESET_BANK_ATTRIBUTE, "0"); | |
+ inP.resourceMeta.attributes.emplace(audio::synth::PRESET_PROGRAM_ATTRIBUTE, "0"); | |
+ notationProject->audioSettings()->setTrackInputParams(ins, inP); | |
+ } | |
+ partIdx++; | |
+ } | |
+ if (isConvertPageByPage(suffix)) | |
+ { | |
+ ret = convertPageByPage(writer, notation, out); | |
+ } | |
+ else | |
+ { | |
+ ret = convertFullNotation(writer, notation, out); | |
+ } | |
} | |
globalContext()->setCurrentProject(nullptr); | |
diff --git a/src/converter/internal/convertercontroller.h b/src/converter/internal/convertercontroller.h | |
index 94d7e229f7..c9194f3551 100644 | |
--- a/src/converter/internal/convertercontroller.h | |
+++ b/src/converter/internal/convertercontroller.h | |
@@ -31,6 +31,9 @@ | |
#include "project/inotationwritersregister.h" | |
#include "project/iprojectrwregister.h" | |
#include "context/iglobalcontext.h" | |
+#include "notation/iinstrumentsrepository.h" | |
+#include "audio/isoundfontrepository.h" | |
+#include "audio/iplayback.h" | |
#include "types/retval.h" | |
@@ -41,6 +44,9 @@ class ConverterController : public IConverterController | |
INJECT(project::INotationWritersRegister, writers) | |
INJECT(project::IProjectRWRegister, projectRW) | |
INJECT(context::IGlobalContext, globalContext) | |
+ INJECT(notation::IInstrumentsRepository, instrumentsRepository) | |
+ INJECT(audio::ISoundFontRepository, soundFontRepository) | |
+ INJECT(audio::IPlayback, playback) | |
public: | |
ConverterController() = default; | |
diff --git a/src/importexport/audioexport/iaudioexportconfiguration.h b/src/importexport/audioexport/iaudioexportconfiguration.h | |
index 7c81ee1dc6..2ca23564fa 100644 | |
--- a/src/importexport/audioexport/iaudioexportconfiguration.h | |
+++ b/src/importexport/audioexport/iaudioexportconfiguration.h | |
@@ -42,6 +42,12 @@ public: | |
virtual int exportSampleRate() const = 0; | |
virtual void setExportSampleRate(int rate) = 0; | |
virtual const std::vector<int>& availableSampleRates() const = 0; | |
+ | |
+ virtual bool splitVoices() const = 0; | |
+ virtual void setSplitVoices(bool split) = 0; | |
+ | |
+ virtual QStringList soloStaffs() const = 0; | |
+ virtual void setSoloStaffs(QStringList staffs) = 0; | |
}; | |
} | |
diff --git a/src/importexport/audioexport/internal/audioexportconfiguration.cpp b/src/importexport/audioexport/internal/audioexportconfiguration.cpp | |
index fcdfb10616..ff0f40171b 100644 | |
--- a/src/importexport/audioexport/internal/audioexportconfiguration.cpp | |
+++ b/src/importexport/audioexport/internal/audioexportconfiguration.cpp | |
@@ -72,3 +72,23 @@ const std::vector<int>& AudioExportConfiguration::availableSampleRates() const | |
static const std::vector<int> rates { 32000, 44100, 48000 }; | |
return rates; | |
} | |
+ | |
+bool AudioExportConfiguration::splitVoices() const | |
+{ | |
+ return m_splitVoices; | |
+} | |
+ | |
+void AudioExportConfiguration::setSplitVoices(bool split) | |
+{ | |
+ m_splitVoices = split; | |
+} | |
+ | |
+QStringList mu::iex::audioexport::AudioExportConfiguration::soloStaffs() const | |
+{ | |
+ return m_soloStaffs; | |
+} | |
+ | |
+void mu::iex::audioexport::AudioExportConfiguration::setSoloStaffs(QStringList staffs) | |
+{ | |
+ m_soloStaffs = staffs; | |
+} | |
diff --git a/src/importexport/audioexport/internal/audioexportconfiguration.h b/src/importexport/audioexport/internal/audioexportconfiguration.h | |
index 85a00562df..e9b416911e 100644 | |
--- a/src/importexport/audioexport/internal/audioexportconfiguration.h | |
+++ b/src/importexport/audioexport/internal/audioexportconfiguration.h | |
@@ -39,8 +39,16 @@ public: | |
void setExportSampleRate(int rate) override; | |
const std::vector<int>& availableSampleRates() const override; | |
+ virtual bool splitVoices() const override; | |
+ virtual void setSplitVoices(bool split) override; | |
+ | |
+ virtual QStringList soloStaffs() const override; | |
+ virtual void setSoloStaffs(QStringList staffs) override; | |
+ | |
private: | |
std::optional<int> m_exportMp3BitrateOverride = std::nullopt; | |
+ bool m_splitVoices = false; | |
+ QStringList m_soloStaffs; | |
}; | |
} | |
-- | |
2.41.0 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment