LCOV - code coverage report
Current view: top level - muse/framework/audioplugins/internal - registeraudiopluginsscenario.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 83.1 % 320 266
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2021 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : 
      23              : #include "registeraudiopluginsscenario.h"
      24              : 
      25              : #include <QCoreApplication>
      26              : #include <algorithm>
      27              : #include <atomic>
      28              : #include <chrono>
      29              : #include <condition_variable>
      30              : #include <map>
      31              : #include <memory>
      32              : #include <mutex>
      33              : #include <thread>
      34              : #include <vector>
      35              : 
      36              : #include "global/containers.h"
      37              : #include "global/translation.h"
      38              : 
      39              : #include "audiopluginserrors.h"
      40              : 
      41              : #include "log.h"
      42              : 
      43              : using namespace muse;
      44              : using namespace muse::audioplugins;
      45              : 
      46              : #ifdef MUSE_MODULE_AUDIOPLUGINS_SCAN_TRACE
      47              : #define SCAN_TRACE() LOGI()
      48              : #else
      49              : #define SCAN_TRACE() LOGN()
      50              : #endif
      51              : 
      52              : namespace {
      53              : // completeBasename() is empty for LV2 "<uri>@<bundle>/" composites and
      54              : // load() rejects empty ids; fall back to the full path.
      55           25 : std::string placeholderIdFromPath(const io::path_t& path)
      56              : {
      57           25 :     std::string id = io::completeBasename(path).toStdString();
      58           25 :     if (id.empty()) {
      59            1 :         id = path.toStdString();
      60              :     }
      61           25 :     return id;
      62            0 : }
      63              : 
      64            6 : int64_t pluginScanConcurrency()
      65              : {
      66            6 :     const unsigned int concurrency = std::thread::hardware_concurrency();
      67            6 :     return concurrency > 0 ? static_cast<int64_t>(concurrency) : 1;
      68              : }
      69              : 
      70           26 : void processProgressEvents()
      71              : {
      72           26 :     if (QCoreApplication::instance()) {
      73           26 :         QCoreApplication::processEvents();
      74              :     }
      75           26 : }
      76              : 
      77              : constexpr int AUDIO_PLUGIN_REGISTRATION_TIMEOUT_MS = 15000;
      78              : }
      79              : 
      80            2 : void RegisterAudioPluginsScenario::init()
      81              : {
      82            3 :     TRACEFUNC;
      83              : 
      84            2 :     m_progress.canceled().onNotify(this, [this]() {
      85              :         SCAN_TRACE() << "Audio plugin scan cancellation requested";
      86            0 :         m_aborted = true;
      87            0 :     });
      88              : 
      89            2 :     Ret ret = knownPluginsRegister()->load();
      90            2 :     if (!ret) {
      91            0 :         LOGE() << ret.toString();
      92              :     }
      93            2 : }
      94              : 
      95            2 : Ret RegisterAudioPluginsScenario::markCrashedPluginsAsBroken()
      96              : {
      97            3 :     TRACEFUNC;
      98              : 
      99            2 :     const PluginResourceIdList crashedIds = loadGuard()->danglingLoads();
     100            2 :     if (crashedIds.empty()) {
     101            1 :         return make_ok();
     102              :     }
     103              : 
     104            1 :     AudioPluginInfoList brokenInfos;
     105              : 
     106            3 :     for (const AudioPluginInfo& info : knownPluginsRegister()->pluginInfoList()) {
     107            2 :         if (!muse::contains(crashedIds, info.meta.id)) {
     108            1 :             continue;
     109              :         }
     110              : 
     111            1 :         LOGI() << "Plugin crashed the application while loading in a previous run, marking as broken: " << info.meta.id;
     112              : 
     113            1 :         AudioPluginInfo broken = info;
     114            1 :         broken.state = AudioPluginState::Error;
     115            1 :         broken.errorCode = static_cast<int>(Err::PluginCrashedOnLoad);
     116            1 :         brokenInfos.push_back(std::move(broken));
     117            2 :     }
     118              : 
     119            1 :     Ret ret = make_ok();
     120              : 
     121            1 :     if (!brokenInfos.empty()) {
     122            1 :         PluginResourceIdList brokenIds;
     123            1 :         brokenIds.reserve(brokenInfos.size());
     124            2 :         for (const AudioPluginInfo& info : brokenInfos) {
     125            1 :             brokenIds.push_back(info.meta.id);
     126              :         }
     127              : 
     128            1 :         ret = knownPluginsRegister()->unregisterPlugins(brokenIds);
     129            1 :         if (ret) {
     130            1 :             ret = knownPluginsRegister()->registerPlugins(brokenInfos);
     131              :         }
     132              : 
     133            1 :         if (!ret) {
     134            0 :             LOGE() << "Failed to mark crashed plugins as broken: " << ret.toString();
     135            0 :             return ret;
     136              :         }
     137            1 :     }
     138              : 
     139            1 :     return loadGuard()->clearDanglingLoads();
     140            2 : }
     141              : 
     142            7 : PluginScanResult RegisterAudioPluginsScenario::scanPlugins(Progress* progress) const
     143              : {
     144            8 :     TRACEFUNC;
     145              : 
     146            7 :     PluginScanResult result;
     147              : 
     148              :     // one binary path can host several plugin ids (shell bundles)
     149            7 :     std::map<io::path_t, std::vector<AudioPluginState> > registered;
     150           24 :     for (const auto& info : knownPluginsRegister()->pluginInfoList()) {
     151           17 :         registered[info.path].push_back(info.state);
     152            7 :     }
     153              : 
     154           14 :     for (const auto& scanner : scannerRegister()->scanners()) {
     155           22 :         for (const auto& path : scanner->scanPlugins(progress)) {
     156           15 :             auto it = registered.find(path);
     157           15 :             if (it == registered.end()) {
     158            3 :                 result.newPluginPaths.push_back(path);
     159            4 :                 continue;
     160              :             }
     161              : 
     162           12 :             const std::vector<AudioPluginState>& states = it->second;
     163              : 
     164              :             // Discovered placeholder: a prior run was interrupted, re-validate
     165           12 :             const bool hasDiscovered = std::any_of(states.cbegin(), states.cend(),
     166           13 :                                                    [](AudioPluginState state) {
     167           13 :                 return state == AudioPluginState::Discovered;
     168              :             });
     169           12 :             if (hasDiscovered) {
     170            1 :                 result.newPluginPaths.push_back(path);
     171            1 :                 registered.erase(it);
     172            1 :                 continue;
     173              :             }
     174              : 
     175              :             // a Missing path is back: re-validate rather than trust the cache,
     176              :             // the binary may have changed
     177           11 :             const bool hasMissing = std::any_of(states.cbegin(), states.cend(),
     178           11 :                                                 [](AudioPluginState state) {
     179           11 :                 return state == AudioPluginState::Missing;
     180              :             });
     181           11 :             if (hasMissing) {
     182            2 :                 result.newPluginPaths.push_back(path);
     183              :             }
     184           11 :             registered.erase(it);
     185            7 :         }
     186              :     }
     187              : 
     188              :     // paths no scanner reports anymore are missing
     189           10 :     for (const auto& [path, states] : registered) {
     190              :         // skip paths already fully Missing, nothing to transition
     191            3 :         const bool hasTransition = std::any_of(states.cbegin(), states.cend(),
     192            3 :                                                [](AudioPluginState state) {
     193            3 :             return state != AudioPluginState::Missing;
     194              :         });
     195            3 :         if (hasTransition) {
     196            3 :             result.missingPluginPaths.push_back(path);
     197              :         }
     198              :     }
     199              : 
     200            7 :     return result;
     201            7 : }
     202              : 
     203            0 : Ret RegisterAudioPluginsScenario::rescanAllPlugins()
     204              : {
     205            0 :     TRACEFUNC;
     206              : 
     207            0 :     Ret ret = knownPluginsRegister()->clear();
     208            0 :     if (!ret) {
     209            0 :         LOGE() << "Failed to clear plugins registry: " << ret.toString();
     210            0 :         return ret;
     211              :     }
     212              : 
     213            0 :     return updatePluginsRegistry();
     214            0 : }
     215              : 
     216            5 : Ret RegisterAudioPluginsScenario::updatePluginsRegistry()
     217              : {
     218            6 :     TRACEFUNC;
     219              : 
     220            5 :     PluginScanResult result = scanPlugins();
     221              : 
     222            5 :     Ret ret = knownPluginsRegister()->setPluginsState(result.missingPluginPaths, AudioPluginState::Missing);
     223            5 :     if (!ret) {
     224            0 :         LOGE() << "Failed to mark missing plugins: " << ret.toString();
     225            0 :         return ret;
     226              :     }
     227              : 
     228            5 :     ret = registerNewPlugins(result.newPluginPaths, /*validate*/ true);
     229            5 :     if (!ret) {
     230            0 :         LOGE() << "Failed to register new plugins: " << ret.toString();
     231            0 :         return ret;
     232              :     }
     233              : 
     234            5 :     return knownPluginsRegister()->load();
     235            5 : }
     236              : 
     237           11 : Ret RegisterAudioPluginsScenario::registerNewPlugins(const io::paths_t& pluginPaths, bool validate)
     238              : {
     239           12 :     TRACEFUNC;
     240              : 
     241           11 :     if (pluginPaths.empty()) {
     242            3 :         return make_ok();
     243              :     }
     244              : 
     245            8 :     Ret ret = persistDiscoveredPlaceholders(pluginPaths);
     246            8 :     if (!ret) {
     247            0 :         return ret;
     248              :     }
     249              : 
     250            8 :     if (validate) {
     251              :         SCAN_TRACE() << "Starting audio plugin validation for " << pluginPaths.size() << " plugin paths";
     252            6 :         processPluginsRegistration(pluginPaths);
     253              :     }
     254              : 
     255            8 :     return knownPluginsRegister()->load();
     256           11 : }
     257              : 
     258            8 : Ret RegisterAudioPluginsScenario::persistDiscoveredPlaceholders(const io::paths_t& pluginPaths)
     259              : {
     260              :     // Placeholders make scanPlugins() re-validate these paths next launch.
     261              :     // Clear any prior entry at the path first to avoid the same-id-same-path assert.
     262            8 :     AudioPluginInfoList placeholders;
     263            8 :     placeholders.reserve(pluginPaths.size());
     264           21 :     for (const io::path_t& path : pluginPaths) {
     265           13 :         Ret ret = knownPluginsRegister()->removePluginsAtPath(path);
     266           13 :         if (!ret) {
     267            0 :             return ret;
     268              :         }
     269              : 
     270           13 :         AudioPluginInfo info;
     271           13 :         info.meta.id = placeholderIdFromPath(path);
     272           13 :         info.meta.type = metaType(path);
     273           13 :         info.path = path;
     274           13 :         info.state = AudioPluginState::Discovered;
     275           13 :         placeholders.emplace_back(std::move(info));
     276           26 :     }
     277            8 :     return knownPluginsRegister()->registerPlugins(placeholders);
     278            8 : }
     279              : 
     280            0 : Ret RegisterAudioPluginsScenario::unregisterRemovedPlugins(const PluginResourceIdList& pluginIds)
     281              : {
     282            0 :     TRACEFUNC;
     283              : 
     284            0 :     if (pluginIds.empty()) {
     285            0 :         return make_ok();
     286              :     }
     287              : 
     288            0 :     Ret ret = knownPluginsRegister()->unregisterPlugins(pluginIds);
     289            0 :     if (!ret) {
     290            0 :         LOGE() << "Failed to unregister removed plugins: " << ret.toString();
     291              :     }
     292              : 
     293            0 :     return ret;
     294            0 : }
     295              : 
     296            9 : AudioPluginInfoList RegisterAudioPluginsScenario::scanResult(const io::path_t& pluginPath, const io::path_t& resultFile, int code) const
     297              : {
     298            9 :     if (code == 0) {
     299            6 :         RetVal<AudioPluginInfoList> res = knownPluginsRegister()->readPluginsFrom(resultFile);
     300            6 :         if (res.ret) {
     301            6 :             fileSystem()->remove(resultFile);
     302            6 :             return res.val;
     303              :         } else {
     304            0 :             LOGE() << "Could not read scan result for " << pluginPath.toStdString() << ": " << res.ret.toString();
     305              :         }
     306            6 :     } else {
     307            3 :         LOGE() << "Could not register plugin: " << pluginPath.toStdString() << "\n error code: " << code;
     308              :     }
     309              : 
     310            3 :     fileSystem()->remove(resultFile);
     311            9 :     return { makeFailedPluginInfo(pluginPath, code) };
     312            3 : }
     313              : 
     314            9 : static void appendPluginInfos(AudioPluginInfoList& destination, const AudioPluginInfoList& source)
     315              : {
     316            9 :     destination.insert(destination.end(), source.cbegin(), source.cend());
     317            9 : }
     318              : 
     319            6 : void RegisterAudioPluginsScenario::processPluginsRegistration(const io::paths_t& pluginPaths)
     320              : {
     321            6 :     interactive()->showProgress(muse::trc("audio", "Validating audio plugins"), m_progress);
     322              : 
     323            6 :     m_aborted = false;
     324            6 :     m_progress.start();
     325            6 :     processProgressEvents();
     326              : 
     327            6 :     const std::string appPath = globalConfiguration()->appBinPath().toStdString();
     328            6 :     const int64_t pluginCount = static_cast<int64_t>(pluginPaths.size());
     329            6 :     if (pluginCount == 0) {
     330            0 :         m_progress.finish(muse::make_ok());
     331            0 :         return;
     332              :     }
     333              : 
     334            6 :     const int64_t concurrency = pluginScanConcurrency();
     335            6 :     const size_t registryFlushSize = 64;
     336              : 
     337              :     SCAN_TRACE() << "Audio plugin scan process started: pluginCount=" << pluginCount
     338              :                  << ", concurrency=" << concurrency
     339              :                  << ", timeoutMs=" << AUDIO_PLUGIN_REGISTRATION_TIMEOUT_MS
     340              :                  << ", appPath=" << appPath;
     341              : 
     342              :     struct CompletedScan {
     343              :         int64_t index = 0;
     344              :         io::path_t resultFile;
     345              :         int code = 0;
     346              :     };
     347              : 
     348            6 :     std::mutex completedMutex;
     349            6 :     std::condition_variable completedChanged;
     350            6 :     std::vector<CompletedScan> completedScans;
     351            6 :     std::atomic<int64_t> nextIndex { 0 };
     352            6 :     std::atomic<int64_t> activeWorkers { 0 };
     353            6 :     std::vector<std::thread> workers;
     354            6 :     PluginResourceIdList completedPlaceholderIds;
     355            6 :     AudioPluginInfoList completedPluginInfo;
     356            6 :     int64_t doneCount = 0;
     357              : 
     358            6 :     auto flushCompletedScanResults = [&]() {
     359            6 :         if (completedPlaceholderIds.empty() && completedPluginInfo.empty()) {
     360            1 :             return;
     361              :         }
     362              : 
     363              :         SCAN_TRACE() << "Flushing audio plugin scan results: placeholders=" << completedPlaceholderIds.size()
     364              :                      << ", pluginInfos=" << completedPluginInfo.size();
     365              : 
     366            5 :         Ret ret = knownPluginsRegister()->unregisterPlugins(completedPlaceholderIds);
     367            5 :         if (!ret) {
     368            0 :             LOGE() << "Failed to remove completed plugin placeholders: " << ret.toString();
     369              :         }
     370              : 
     371            5 :         ret = knownPluginsRegister()->registerPlugins(completedPluginInfo);
     372            5 :         if (!ret) {
     373            0 :             LOGE() << "Failed to register scanned plugins: " << ret.toString();
     374              :         }
     375              : 
     376            5 :         completedPlaceholderIds.clear();
     377            5 :         completedPluginInfo.clear();
     378            5 :     };
     379              : 
     380           10 :     auto worker = [&](int64_t workerId) {
     381              :         SCAN_TRACE() << "Audio plugin scan worker started: workerId=" << workerId;
     382           20 :         while (!m_aborted.load() && !m_progress.isCanceled()) {
     383           19 :             const int64_t index = nextIndex.fetch_add(1);
     384           19 :             if (index >= pluginCount) {
     385            9 :                 break;
     386              :             }
     387              : 
     388           10 :             const io::path_t resultFile = scanResultFilePath(index);
     389           10 :             const std::string pluginPathStr = pluginPaths[index].toStdString();
     390              : 
     391              :             SCAN_TRACE() << "Audio plugin scan worker " << workerId
     392              :                          << " validating index=" << index << "/" << pluginCount
     393              :                          << ", resultFile=" << resultFile.toStdString()
     394              :                          << ", pluginPath=" << pluginPathStr;
     395              : 
     396              :             // clear leftovers from a previous run
     397           10 :             fileSystem()->remove(resultFile);
     398              : 
     399              :             std::vector<std::string> args { "--register-audio-plugin", pluginPathStr, "--register-audio-plugin-out",
     400           70 :                                             resultFile.toStdString() };
     401              : 
     402           10 :             const std::string serverUrl = MUSE_MODULE_AUDIOPLUGINS_CRASHREPORT_URL;
     403           10 :             if (!serverUrl.empty()) {
     404            0 :                 const std::string dumpsDir = (globalConfiguration()->userAppDataPath() + "/logs/dumps-plugin-validation").toStdString();
     405            0 :                 args.insert(args.end(), { "--crash-dumps-dir", dumpsDir });
     406            0 :                 args.insert(args.end(), { "--crash-server-url", serverUrl });
     407            0 :             }
     408              : 
     409           20 :             const int code = process()->execute(appPath, args,
     410              :                                                 AUDIO_PLUGIN_REGISTRATION_TIMEOUT_MS,
     411           11 :                                                 [this]() { return m_aborted.load() || m_progress.isCanceled(); });
     412              : 
     413              :             SCAN_TRACE() << "Audio plugin scan worker " << workerId
     414              :                          << " validation finished: index=" << index
     415              :                          << ", code=" << code
     416              :                          << ", aborted=" << m_aborted.load()
     417              :                          << ", pluginPath=" << pluginPathStr;
     418              : 
     419              :             {
     420           10 :                 std::lock_guard lock(completedMutex);
     421           10 :                 completedScans.push_back({ index, resultFile, code });
     422           10 :             }
     423           10 :             completedChanged.notify_one();
     424           10 :         }
     425              : 
     426           10 :         const int64_t remainingWorkers = --activeWorkers;
     427              :         SCAN_TRACE() << "Audio plugin scan worker stopped: workerId=" << workerId
     428              :                      << ", remainingWorkers=" << remainingWorkers
     429              :                      << ", aborted=" << m_aborted.load();
     430           10 :         completedChanged.notify_one();
     431           40 :     };
     432              : 
     433            6 :     const int64_t workerCount = std::min(concurrency, pluginCount);
     434            6 :     activeWorkers = workerCount;
     435            6 :     workers.reserve(static_cast<size_t>(workerCount));
     436           16 :     for (int64_t i = 0; i < workerCount; ++i) {
     437           10 :         workers.emplace_back(worker, i);
     438              :     }
     439              : 
     440           16 :     while (doneCount < pluginCount) {
     441           10 :         CompletedScan scan;
     442              :         {
     443           10 :             std::unique_lock lock(completedMutex);
     444           20 :             while (completedScans.empty() && activeWorkers.load() > 0) {
     445            5 :                 completedChanged.wait_for(lock, std::chrono::milliseconds(50));
     446            5 :                 lock.unlock();
     447            5 :                 processProgressEvents();
     448            5 :                 lock.lock();
     449              :             }
     450              : 
     451           10 :             if (completedScans.empty()) {
     452              :                 SCAN_TRACE() << "Audio plugin scan completion loop ended with no completed scans: doneCount=" << doneCount
     453              :                              << ", pluginCount=" << pluginCount
     454              :                              << ", activeWorkers=" << activeWorkers.load()
     455              :                              << ", aborted=" << m_aborted.load();
     456            0 :                 break;
     457              :             }
     458              : 
     459           10 :             scan = completedScans.back();
     460           10 :             completedScans.pop_back();
     461           10 :         }
     462              : 
     463           10 :         ++doneCount;
     464              :         SCAN_TRACE() << "Audio plugin scan result received: doneCount=" << doneCount << "/" << pluginCount
     465              :                      << ", index=" << scan.index
     466              :                      << ", code=" << scan.code
     467              :                      << ", resultFile=" << scan.resultFile.toStdString()
     468              :                      << ", pluginPath=" << pluginPaths[scan.index].toStdString();
     469              : 
     470           10 :         if (scan.code == IProcess::ExecuteCanceledCode) {
     471              :             SCAN_TRACE() << "Audio plugin scan result ignored after cancellation: index=" << scan.index
     472              :                          << ", pluginPath=" << pluginPaths[scan.index].toStdString();
     473            1 :             continue;
     474              :         }
     475              : 
     476            9 :         completedPlaceholderIds.push_back(placeholderIdFromPath(pluginPaths[scan.index]));
     477            9 :         appendPluginInfos(completedPluginInfo, scanResult(pluginPaths[scan.index], scan.resultFile, scan.code));
     478              : 
     479            9 :         m_progress.progress(doneCount, pluginCount, io::filename(pluginPaths[scan.index]).toStdString());
     480            9 :         processProgressEvents();
     481              : 
     482            9 :         if (completedPlaceholderIds.size() >= registryFlushSize) {
     483            0 :             flushCompletedScanResults();
     484              :         }
     485              : 
     486            9 :         if (m_aborted.load() && activeWorkers.load() == 0) {
     487            0 :             break;
     488              :         }
     489           10 :     }
     490              : 
     491           16 :     for (std::thread& workerThread : workers) {
     492           10 :         if (workerThread.joinable()) {
     493              :             SCAN_TRACE() << "Joining audio plugin scan worker thread";
     494           10 :             workerThread.join();
     495              :         }
     496              :     }
     497              : 
     498            6 :     flushCompletedScanResults();
     499              : 
     500              :     SCAN_TRACE() << "Audio plugin scan process finished: doneCount=" << doneCount
     501              :                  << ", pluginCount=" << pluginCount
     502              :                  << ", activeWorkers=" << activeWorkers.load()
     503              :                  << ", aborted=" << m_aborted.load();
     504              : 
     505            6 :     if (!m_aborted.load()) {
     506            6 :         m_progress.finish(muse::make_ok());
     507              :     }
     508            6 :     processProgressEvents();
     509            6 : }
     510              : 
     511            2 : RetVal<AudioPluginInfoList> RegisterAudioPluginsScenario::validatePluginInfo(const io::path_t& pluginPath) const
     512              : {
     513              :     SCAN_TRACE() << "Validating audio plugin metadata: pluginPath=" << pluginPath.toStdString();
     514              : 
     515            2 :     const IAudioPluginMetaReaderPtr reader = metaReader(pluginPath);
     516            2 :     if (!reader) {
     517              :         SCAN_TRACE() << "No audio plugin meta reader found: pluginPath=" << pluginPath.toStdString();
     518            0 :         return RetVal<AudioPluginInfoList>(make_ret(Err::UnknownPluginType));
     519              :     }
     520              : 
     521            2 :     const RetVal<PluginMetaList> metaList = reader->readMeta(pluginPath);
     522            2 :     if (!metaList.ret) {
     523              :         SCAN_TRACE() << "Failed reading audio plugin metadata: pluginPath=" << pluginPath.toStdString()
     524              :                      << ", ret=" << metaList.ret.toString();
     525            0 :         return RetVal<AudioPluginInfoList>(metaList.ret);
     526              :     }
     527              : 
     528              :     SCAN_TRACE() << "Audio plugin metadata read: pluginPath=" << pluginPath.toStdString()
     529              :                  << ", metaCount=" << metaList.val.size();
     530              : 
     531            2 :     AudioPluginInfoList infoList;
     532            2 :     infoList.reserve(metaList.val.size());
     533              : 
     534            5 :     for (const PluginMeta& meta : metaList.val) {
     535            3 :         AudioPluginInfo info;
     536            3 :         info.meta = meta;
     537            3 :         info.path = pluginPath;
     538            3 :         info.state = AudioPluginState::Validated;
     539            3 :         infoList.emplace_back(std::move(info));
     540            3 :     }
     541              : 
     542            2 :     return RetVal<AudioPluginInfoList>::make_ok(infoList);
     543            2 : }
     544              : 
     545            1 : Ret RegisterAudioPluginsScenario::registerPlugin(const io::path_t& pluginPath)
     546              : {
     547            2 :     TRACEFUNC;
     548              : 
     549            1 :     IF_ASSERT_FAILED(!pluginPath.empty()) {
     550            0 :         return false;
     551              :     }
     552              : 
     553            1 :     Ret ret = knownPluginsRegister()->removePluginsAtPath(pluginPath);
     554            1 :     if (!ret) {
     555            0 :         LOGE() << "Failed to clear existing entry at " << pluginPath.toStdString()
     556            0 :                << ": " << ret.toString();
     557            0 :         return ret;
     558              :     }
     559              : 
     560            1 :     RetVal<AudioPluginInfoList> infoList = validatePluginInfo(pluginPath);
     561            1 :     if (!infoList.ret) {
     562            0 :         LOGE() << infoList.ret.toString();
     563            0 :         return infoList.ret;
     564              :     }
     565              : 
     566            1 :     return knownPluginsRegister()->registerPlugins(infoList.val);
     567            1 : }
     568              : 
     569            1 : Ret RegisterAudioPluginsScenario::validatePlugin(const io::path_t& pluginPath, const io::path_t& outputFile)
     570              : {
     571            2 :     TRACEFUNC;
     572              : 
     573            1 :     IF_ASSERT_FAILED(!pluginPath.empty()) {
     574            0 :         return false;
     575              :     }
     576              : 
     577              :     SCAN_TRACE() << "Audio plugin validation subprocess started: pluginPath=" << pluginPath.toStdString()
     578              :                  << ", outputFile=" << outputFile.toStdString();
     579              : 
     580            1 :     RetVal<AudioPluginInfoList> infoList = validatePluginInfo(pluginPath);
     581            1 :     if (!infoList.ret) {
     582            0 :         LOGE() << infoList.ret.toString();
     583            0 :         return infoList.ret;
     584              :     }
     585              : 
     586            1 :     Ret ret = knownPluginsRegister()->writePluginsTo(outputFile, infoList.val);
     587              :     SCAN_TRACE() << "Audio plugin validation subprocess finished: pluginPath=" << pluginPath.toStdString()
     588              :                  << ", outputFile=" << outputFile.toStdString()
     589              :                  << ", pluginInfoCount=" << infoList.val.size()
     590              :                  << ", ret=" << ret.toString();
     591            1 :     return ret;
     592            1 : }
     593              : 
     594            3 : AudioPluginInfo RegisterAudioPluginsScenario::makeFailedPluginInfo(const io::path_t& pluginPath, int failCode) const
     595              : {
     596            3 :     AudioPluginInfo info;
     597            3 :     info.meta.id = placeholderIdFromPath(pluginPath);
     598            3 :     info.meta.type = metaType(pluginPath);
     599            3 :     info.path = pluginPath;
     600            3 :     info.state = AudioPluginState::Error;
     601            3 :     info.errorCode = failCode != 0 ? failCode : -1;
     602            3 :     return info;
     603            0 : }
     604              : 
     605           10 : io::path_t RegisterAudioPluginsScenario::scanResultFilePath(int64_t index) const
     606              : {
     607           10 :     return fileSystem()->temporaryDirectoryPath() + "/muse_audioplugin_scan_" + std::to_string(index) + ".json";
     608              : }
     609              : 
     610           18 : IAudioPluginMetaReaderPtr RegisterAudioPluginsScenario::metaReader(const io::path_t& pluginPath) const
     611              : {
     612           18 :     for (const IAudioPluginMetaReaderPtr& reader : metaReaderRegister()->readers()) {
     613           18 :         if (reader->canReadMeta(pluginPath)) {
     614           18 :             return reader;
     615              :         }
     616              :     }
     617              : 
     618            0 :     return nullptr;
     619              : }
     620              : 
     621           16 : audioplugins::PluginType RegisterAudioPluginsScenario::metaType(const io::path_t& pluginPath) const
     622              : {
     623           16 :     const IAudioPluginMetaReaderPtr reader = metaReader(pluginPath);
     624           32 :     return reader ? reader->metaType() : audioplugins::PluginType();
     625           16 : }
        

Generated by: LCOV version 2.0-1