implemented the register of known plugins

This commit is contained in:
Roman Pudashkin 2023-03-08 16:32:20 +02:00
parent b0097353f6
commit 497435c2cb
14 changed files with 675 additions and 3 deletions

View file

@ -146,10 +146,10 @@ endif()
if(BUILD_CONFIGURE MATCHES "UTEST")
set(MUE_BUILD_UNIT_TESTS ON)
set(MUE_ENABLE_LOGGER_DEBUGLEVEL ON)
set(MUE_BUILD_AUDIO_MODULE ON)
# set(MUE_BUILD_ASAN ON)
message(STATUS "If you added tests to a module that didn't have them yet, make sure that this module is enabled, see SetupConfigure.cmake")
set(MUE_BUILD_AUDIO_MODULE OFF)
set(MUE_BUILD_MIDI_MODULE OFF)
set(MUE_BUILD_MUSESAMPLER_MODULE OFF)
set(MUE_BUILD_NETWORK_MODULE OFF)

View file

@ -85,6 +85,7 @@ set(MODULE_SRC
${CMAKE_CURRENT_LIST_DIR}/iplayback.h
${CMAKE_CURRENT_LIST_DIR}/isoundfontrepository.h
${CMAKE_CURRENT_LIST_DIR}/isynthesizer.h
${CMAKE_CURRENT_LIST_DIR}/iknownaudiopluginsregister.h
# Common internal
${CMAKE_CURRENT_LIST_DIR}/internal/audioconfiguration.cpp
@ -104,6 +105,8 @@ set(MODULE_SRC
${CMAKE_CURRENT_LIST_DIR}/internal/abstractsynthesizer.cpp
${CMAKE_CURRENT_LIST_DIR}/internal/abstractsynthesizer.h
${CMAKE_CURRENT_LIST_DIR}/internal/abstracteventsequencer.h
${CMAKE_CURRENT_LIST_DIR}/internal/knownaudiopluginsregister.cpp
${CMAKE_CURRENT_LIST_DIR}/internal/knownaudiopluginsregister.h
# Driver
${DRIVER_SRC}
@ -229,7 +232,8 @@ endif()
if (OS_IS_MAC)
find_library(AudioToolbox NAMES AudioToolbox)
set(MODULE_LINK ${MODULE_LINK} ${AudioToolbox})
find_library(CoreAudio NAMES CoreAudio)
set(MODULE_LINK ${MODULE_LINK} ${AudioToolbox} ${CoreAudio})
elseif (OS_IS_WIN)
set(MODULE_LINK ${MODULE_LINK} winmm mmdevapi mfplat)
elseif (OS_IS_LIN)
@ -238,6 +242,10 @@ elseif (OS_IS_LIN)
set(MODULE_LINK ${MODULE_LINK} ${ALSA_LIBRARIES} pthread )
endif()
if (MUE_BUILD_UNIT_TESTS)
add_subdirectory(tests)
endif()
set(MODULE_QRC audio.qrc)
set(MODULE_QML_IMPORT ${CMAKE_CURRENT_LIST_DIR}/qml)

View file

@ -33,6 +33,7 @@
#include "internal/audiobuffer.h"
#include "internal/audiothreadsecurer.h"
#include "internal/audiooutputdevicecontroller.h"
#include "internal/knownaudiopluginsregister.h"
#include "internal/worker/audioengine.h"
#include "internal/worker/playback.h"
@ -66,6 +67,8 @@ static std::shared_ptr<Playback> s_playbackFacade = std::make_shared<Playback>()
static std::shared_ptr<SoundFontRepository> s_soundFontRepository = std::make_shared<SoundFontRepository>();
static std::shared_ptr<KnownAudioPluginsRegister> s_knownAudioPluginsRegister = std::make_shared<KnownAudioPluginsRegister>();
#ifdef Q_OS_LINUX
#include "internal/platform/lin/linuxaudiodriver.h"
static std::shared_ptr<IAudioDriver> s_audioDriver = std::shared_ptr<IAudioDriver>(new LinuxAudioDriver());
@ -116,6 +119,8 @@ void AudioModule::registerExports()
ioc()->registerExport<IFxResolver>(moduleName(), s_fxResolver);
ioc()->registerExport<ISoundFontRepository>(moduleName(), s_soundFontRepository);
ioc()->registerExport<IKnownAudioPluginsRegister>(moduleName(), s_knownAudioPluginsRegister);
}
void AudioModule::registerResources()
@ -163,6 +168,7 @@ void AudioModule::onInit(const framework::IApplication::RunMode& mode)
// Init configuration
s_audioConfiguration->init();
s_soundFontRepository->init();
s_knownAudioPluginsRegister->init();
s_audioBuffer->init(s_audioConfiguration->audioChannelsCount(),
s_audioConfiguration->renderStep());

View file

@ -33,6 +33,7 @@
#include "mpe/events.h"
#include "io/iodevice.h"
#include "async/channel.h"
#include "io/path.h"
namespace mu::audio {
using msecs_t = int64_t;
@ -152,6 +153,20 @@ struct AudioResourceMeta {
using AudioResourceMetaList = std::vector<AudioResourceMeta>;
using AudioResourceMetaSet = std::set<AudioResourceMeta>;
enum class AudioPluginType {
Undefined = -1,
Instrument,
Fx,
};
struct AudioPluginInfo {
AudioPluginType type = AudioPluginType::Undefined;
AudioResourceMeta meta;
io::path_t path;
bool enabled = false;
int errorCode = 0;
};
enum class AudioFxType {
Undefined = -1,
VstFx

View file

@ -69,6 +69,8 @@ public:
virtual Ret saveSynthesizerState(const synth::SynthesizerState& state) = 0;
virtual async::Notification synthesizerStateChanged() const = 0;
virtual async::Notification synthesizerStateGroupChanged(const std::string& groupName) const = 0;
virtual io::path_t knownAudioPluginsDir() const = 0;
};
}

View file

@ -0,0 +1,52 @@
/*
* SPDX-License-Identifier: GPL-3.0-only
* MuseScore-CLA-applies
*
* MuseScore
* Music Composition & Notation
*
* Copyright (C) 2021 MuseScore BVBA and others
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef MU_AUDIO_IKNOWNAUDIOPLUGINSREGISTER_H
#define MU_AUDIO_IKNOWNAUDIOPLUGINSREGISTER_H
#include "modularity/imoduleexport.h"
#include "types/ret.h"
#include "io/path.h"
#include "audiotypes.h"
namespace mu::audio {
class IKnownAudioPluginsRegister : MODULE_EXPORT_INTERFACE
{
INTERFACE_ID(IKnownAudioPluginsRegister)
public:
virtual ~IKnownAudioPluginsRegister() = default;
using PluginInfoAccepted = std::function<bool(const AudioPluginInfo& info)>;
virtual std::vector<AudioPluginInfo> pluginInfoList(PluginInfoAccepted accepted = PluginInfoAccepted()) const = 0;
virtual const io::path_t& pluginPath(const AudioResourceId& resourceId) const = 0;
virtual bool exists(const io::path_t& pluginPath) const = 0;
virtual bool exists(const AudioResourceId& resourceId) const = 0;
virtual Ret registerPlugin(const AudioPluginInfo& info) = 0;
virtual Ret unregisterPlugin(const AudioResourceId& resourceId) = 0;
};
}
#endif // MU_AUDIO_IKNOWNAUDIOPLUGINSREGISTER_H

View file

@ -260,6 +260,11 @@ async::Notification AudioConfiguration::synthesizerStateGroupChanged(const std::
return m_synthesizerStateGroupChanged[gname];
}
io::path_t AudioConfiguration::knownAudioPluginsDir() const
{
return globalConfiguration()->userAppDataPath() + "/audio plugins";
}
io::path_t AudioConfiguration::stateFilePath() const
{
return globalConfiguration()->userAppDataPath() + "/synthesizer.xml";

View file

@ -70,6 +70,8 @@ public:
async::Notification synthesizerStateChanged() const override;
async::Notification synthesizerStateGroupChanged(const std::string& groupName) const override;
io::path_t knownAudioPluginsDir() const override;
private:
async::Channel<io::paths_t> m_soundFontDirsChanged;

View file

@ -0,0 +1,197 @@
/*
* SPDX-License-Identifier: GPL-3.0-only
* MuseScore-CLA-applies
*
* MuseScore
* Music Composition & Notation
*
* Copyright (C) 2021 MuseScore BVBA and others
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "knownaudiopluginsregister.h"
#include <QJsonDocument>
#include <QJsonObject>
#include "log.h"
using namespace mu::audio;
static const std::map<AudioPluginType, QString> PLUGIN_TYPE_TO_STRING_MAP = {
{ AudioPluginType::Undefined, "Undefined" },
{ AudioPluginType::Instrument, "Instrument" },
{ AudioPluginType::Fx, "Fx" },
};
static const std::map<AudioResourceType, QString> RESOURCE_TYPE_TO_STRING_MAP {
{ AudioResourceType::VstPlugin, "VstPlugin" },
};
static QJsonObject metaToJson(const AudioResourceMeta& meta)
{
QJsonObject result;
result.insert(QStringLiteral("id"), QString::fromStdString(meta.id));
result.insert(QStringLiteral("type"), mu::value(RESOURCE_TYPE_TO_STRING_MAP, meta.type, "Undefined"));
result.insert(QStringLiteral("vendor"), QString::fromStdString(meta.vendor));
result.insert(QStringLiteral("hasNativeEditorSupport"), meta.hasNativeEditorSupport);
return result;
}
static AudioResourceMeta metaFromJson(const QJsonObject& object)
{
AudioResourceMeta result;
result.id = object.value(QStringLiteral("id")).toString().toStdString();
result.type = mu::key(RESOURCE_TYPE_TO_STRING_MAP, object.value(QString("type")).toString());
result.vendor = object.value(QStringLiteral("vendor")).toString().toStdString();
result.hasNativeEditorSupport = object.value(QStringLiteral("hasNativeEditorSupport")).toBool();
return result;
}
void KnownAudioPluginsRegister::init()
{
TRACEFUNC;
io::path_t knownAudioPluginsDir = configuration()->knownAudioPluginsDir();
if (!fileSystem()->exists(knownAudioPluginsDir)) {
fileSystem()->makePath(knownAudioPluginsDir);
}
RetVal<io::paths_t> paths = fileSystem()->scanFiles(knownAudioPluginsDir,
{ "*.json" },
io::ScanMode::FilesInCurrentDir);
if (!paths.ret) {
LOGE() << paths.ret.toString();
}
for (const io::path_t& infoPath : paths.val) {
RetVal<ByteArray> file = fileSystem()->readFile(infoPath);
if (!file.ret) {
LOGE() << file.ret.toString();
continue;
}
QJsonDocument json = QJsonDocument::fromJson(file.val.toQByteArrayNoCopy());
QJsonObject object = json.object();
AudioPluginInfo info;
info.type = mu::key(PLUGIN_TYPE_TO_STRING_MAP, object.value(QStringLiteral("type")).toString());
info.meta = metaFromJson(object.value(QStringLiteral("meta")).toObject());
info.path = object.value(QStringLiteral("path")).toString().toStdString();
info.enabled = object.value(QStringLiteral("enabled")).toBool();
info.errorCode = object.value(QStringLiteral("errorCode")).toInt(0);
if (info.errorCode == 0) {
info.meta.attributes = { { u"playbackSetupData", mpe::GENERIC_SETUP_DATA_STRING } };
}
m_pluginInfoMap[info.meta.id] = info;
m_pluginPaths.insert(info.path);
}
}
std::vector<AudioPluginInfo> KnownAudioPluginsRegister::pluginInfoList(PluginInfoAccepted accepted) const
{
if (!accepted) {
return mu::values(m_pluginInfoMap);
}
std::vector<AudioPluginInfo> result;
for (auto it = m_pluginInfoMap.cbegin(); it != m_pluginInfoMap.cend(); ++it) {
if (accepted(it->second)) {
result.push_back(it->second);
}
}
return result;
}
const mu::io::path_t& KnownAudioPluginsRegister::pluginPath(const AudioResourceId& resourceId) const
{
auto it = m_pluginInfoMap.find(resourceId);
if (it == m_pluginInfoMap.end()) {
static const io::path_t dummy;
return dummy;
}
return it->second.path;
}
bool KnownAudioPluginsRegister::exists(const io::path_t& pluginPath) const
{
return mu::contains(m_pluginPaths, pluginPath);
}
bool KnownAudioPluginsRegister::exists(const AudioResourceId& resourceId) const
{
return mu::contains(m_pluginInfoMap, resourceId);
}
mu::Ret KnownAudioPluginsRegister::registerPlugin(const AudioPluginInfo& info)
{
TRACEFUNC;
QJsonObject obj;
obj.insert(QStringLiteral("type"), mu::value(PLUGIN_TYPE_TO_STRING_MAP, info.type));
obj.insert(QStringLiteral("meta"), metaToJson(info.meta));
obj.insert(QStringLiteral("path"), info.path.toQString());
obj.insert(QStringLiteral("enabled"), info.enabled);
if (info.errorCode != 0) {
obj.insert(QStringLiteral("errorCode"), info.errorCode);
}
io::path_t path = pluginInfoPath(info.meta.id);
Ret ret = fileSystem()->writeFile(path, ByteArray::fromQByteArrayNoCopy(QJsonDocument(obj).toJson()));
if (!ret) {
return ret;
}
m_pluginInfoMap[info.meta.id] = info;
m_pluginPaths.insert(info.path);
return make_ok();
}
mu::Ret KnownAudioPluginsRegister::unregisterPlugin(const AudioResourceId& resourceId)
{
TRACEFUNC;
if (!exists(resourceId)) {
return make_ok();
}
io::path_t path = pluginInfoPath(resourceId);
Ret ret = fileSystem()->remove(path);
if (!ret) {
return ret;
}
AudioPluginInfo info = mu::take(m_pluginInfoMap, resourceId);
mu::remove(m_pluginPaths, info.path);
return make_ok();
}
mu::io::path_t KnownAudioPluginsRegister::pluginInfoPath(const AudioResourceId& resourceId) const
{
return configuration()->knownAudioPluginsDir() + "/" + resourceId + ".json";
}

View file

@ -0,0 +1,58 @@
/*
* SPDX-License-Identifier: GPL-3.0-only
* MuseScore-CLA-applies
*
* MuseScore
* Music Composition & Notation
*
* Copyright (C) 2021 MuseScore BVBA and others
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef MU_AUDIO_KNOWNAUDIOPLUGINSREGISTER_H
#define MU_AUDIO_KNOWNAUDIOPLUGINSREGISTER_H
#include "audio/iknownaudiopluginsregister.h"
#include "modularity/ioc.h"
#include "audio/iaudioconfiguration.h"
#include "io/ifilesystem.h"
namespace mu::audio {
class KnownAudioPluginsRegister : public IKnownAudioPluginsRegister
{
INJECT(audio, IAudioConfiguration, configuration)
INJECT(audio, io::IFileSystem, fileSystem)
public:
void init();
std::vector<AudioPluginInfo> pluginInfoList(PluginInfoAccepted accepted = PluginInfoAccepted()) const override;
const io::path_t& pluginPath(const AudioResourceId& resourceId) const override;
bool exists(const io::path_t& pluginPath) const override;
bool exists(const AudioResourceId& resourceId) const override;
Ret registerPlugin(const AudioPluginInfo& info) override;
Ret unregisterPlugin(const AudioResourceId& resourceId) override;
private:
mu::io::path_t pluginInfoPath(const AudioResourceId& resourceId) const;
std::unordered_map<AudioResourceId, AudioPluginInfo> m_pluginInfoMap;
std::set<io::path_t> m_pluginPaths;
};
}
#endif // MU_AUDIO_KNOWNAUDIOPLUGINSREGISTER_H

View file

@ -0,0 +1,31 @@
# SPDX-License-Identifier: GPL-3.0-only
# MuseScore-CLA-applies
#
# MuseScore
# Music Composition & Notation
#
# Copyright (C) 2021 MuseScore BVBA and others
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
set(MODULE_TEST audio_test)
set(MODULE_TEST_SRC
${CMAKE_CURRENT_LIST_DIR}/mocks/audioconfigurationmock.h
${CMAKE_CURRENT_LIST_DIR}/knownaudiopluginsregistertest.cpp
)
set(MODULE_TEST_LINK audio)
include(${PROJECT_SOURCE_DIR}/src/framework/testing/gtest.cmake)

View file

@ -0,0 +1,227 @@
/*
* SPDX-License-Identifier: GPL-3.0-only
* MuseScore-CLA-applies
*
* MuseScore
* Music Composition & Notation
*
* Copyright (C) 2021 MuseScore BVBA and others
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <gtest/gtest.h>
#include "audio/internal/knownaudiopluginsregister.h"
#include "global/tests/mocks/filesystemmock.h"
#include "audio/tests/mocks/audioconfigurationmock.h"
#include <QJsonDocument>
#include <QJsonObject>
using ::testing::_;
using ::testing::Return;
using namespace mu::audio;
using namespace mu::io;
namespace mu::audio {
class Audio_KnownAudioPluginsRegisterTest : public ::testing::Test
{
protected:
void SetUp() override
{
m_knownPlugins = std::make_shared<KnownAudioPluginsRegister>();
m_fileSystem = std::make_shared<FileSystemMock>();
m_configuration = std::make_shared<AudioConfigurationMock>();
m_knownPlugins->setfileSystem(m_fileSystem);
m_knownPlugins->setconfiguration(m_configuration);
m_knownAudioPluginsDir = "/test/some dir/audio plugins";
ON_CALL(*m_configuration, knownAudioPluginsDir())
.WillByDefault(Return(m_knownAudioPluginsDir));
}
ByteArray pluginInfoToJson(const AudioPluginInfo& info) const
{
const std::map<AudioPluginType, QString> PLUGIN_TYPE_TO_STR {
{ AudioPluginType::Undefined, "Undefined" },
{ AudioPluginType::Instrument, "Instrument" },
{ AudioPluginType::Fx, "Fx" },
};
const std::map<AudioResourceType, QString> RESOURCE_TYPE_TO_STR {
{ AudioResourceType::VstPlugin, "VstPlugin" },
};
QJsonObject metaObj;
metaObj.insert(QStringLiteral("id"), QString::fromStdString(info.meta.id));
metaObj.insert(QStringLiteral("type"), mu::value(RESOURCE_TYPE_TO_STR, info.meta.type, "Undefined"));
metaObj.insert(QStringLiteral("vendor"), QString::fromStdString(info.meta.vendor));
metaObj.insert(QStringLiteral("hasNativeEditorSupport"), info.meta.hasNativeEditorSupport);
QJsonObject mainObj;
mainObj.insert(QStringLiteral("type"), mu::value(PLUGIN_TYPE_TO_STR, info.type));
mainObj.insert(QStringLiteral("meta"), metaObj);
mainObj.insert(QStringLiteral("path"), info.path.toQString());
mainObj.insert(QStringLiteral("enabled"), info.enabled);
if (info.errorCode != 0) {
mainObj.insert(QStringLiteral("errorCode"), info.errorCode);
}
return ByteArray::fromQByteArray(QJsonDocument(mainObj).toJson());
}
std::vector<AudioPluginInfo> setupTestData()
{
std::vector<AudioPluginInfo> plugins;
AudioPluginInfo pluginInfo1;
pluginInfo1.type = AudioPluginType::Fx;
pluginInfo1.path = "/some/path/to/vst/plugin/AAA.vst3";
pluginInfo1.meta.id = "AAA";
pluginInfo1.meta.type = AudioResourceType::VstPlugin;
pluginInfo1.meta.vendor = "Some vendor";
pluginInfo1.meta.attributes = { { u"playbackSetupData", mpe::GENERIC_SETUP_DATA_STRING } };
pluginInfo1.enabled = true;
plugins.push_back(pluginInfo1);
AudioPluginInfo pluginInfo2;
pluginInfo2.type = AudioPluginType::Instrument;
pluginInfo2.path = "/some/path/to/vst/plugin/BBB.vst3";
pluginInfo2.meta.id = "BBB";
pluginInfo2.meta.type = AudioResourceType::VstPlugin;
pluginInfo2.meta.vendor = "Another vendor";
pluginInfo2.meta.attributes = { { u"playbackSetupData", mpe::GENERIC_SETUP_DATA_STRING } };
pluginInfo2.enabled = true;
plugins.push_back(pluginInfo2);
AudioPluginInfo disabledPluginInfo;
disabledPluginInfo.type = AudioPluginType::Instrument;
disabledPluginInfo.path = "/some/path/to/vst/plugin/CCC.vst3";;
disabledPluginInfo.meta.id = "CCC";
disabledPluginInfo.meta.type = AudioResourceType::VstPlugin;
disabledPluginInfo.meta.vendor = "Some vendor";
disabledPluginInfo.enabled = false;
disabledPluginInfo.errorCode = -1;
plugins.push_back(disabledPluginInfo);
paths_t infoPaths {
m_knownAudioPluginsDir + "/AAA.json",
m_knownAudioPluginsDir + "/BBB.json",
m_knownAudioPluginsDir + "/CCC.json",
};
ON_CALL(*m_fileSystem, scanFiles(m_knownAudioPluginsDir, std::vector<std::string>{ "*.json" }, ScanMode::FilesInCurrentDir))
.WillByDefault(Return(mu::RetVal<paths_t>::make_ok({ infoPaths })));
for (size_t i = 0; i < infoPaths.size(); ++i) {
mu::ByteArray data = pluginInfoToJson(plugins[i]);
ON_CALL(*m_fileSystem, readFile(infoPaths[i]))
.WillByDefault(Return(mu::RetVal<mu::ByteArray>::make_ok(data)));
}
return plugins;
}
std::shared_ptr<KnownAudioPluginsRegister> m_knownPlugins;
std::shared_ptr<FileSystemMock> m_fileSystem;
std::shared_ptr<AudioConfigurationMock> m_configuration;
path_t m_knownAudioPluginsDir;
};
inline bool operator==(const AudioPluginInfo& info1, const AudioPluginInfo& info2)
{
bool equal = info1.type == info2.type;
equal &= (info1.path == info2.path);
equal &= (info1.meta == info2.meta);
equal &= (info1.enabled == info2.enabled);
equal &= (info1.errorCode == info2.errorCode);
return equal;
}
}
TEST_F(Audio_KnownAudioPluginsRegisterTest, PluginInfoList)
{
// [GIVEN] All known plugins
std::vector<AudioPluginInfo> expectedPluginInfoList = setupTestData();
// [WHEN] Init the register
m_knownPlugins->init();
// [WHEN] Request the info
std::vector<AudioPluginInfo> actualPluginInfoList = m_knownPlugins->pluginInfoList();
// [THEN] Successfully received the info
EXPECT_EQ(actualPluginInfoList.size(), expectedPluginInfoList.size());
for (const AudioPluginInfo& actualInfo : actualPluginInfoList) {
EXPECT_TRUE(mu::contains(expectedPluginInfoList, actualInfo));
EXPECT_TRUE(m_knownPlugins->exists(actualInfo.path));
EXPECT_TRUE(m_knownPlugins->exists(actualInfo.meta.id));
EXPECT_EQ(actualInfo.path, m_knownPlugins->pluginPath(actualInfo.meta.id));
}
// [THEN] Make sure that exists() does not always return true
EXPECT_FALSE(m_knownPlugins->exists(path_t("/path/to/nonexistent/plugin.vst3")));
EXPECT_FALSE(m_knownPlugins->exists(AudioResourceId("nonexistent_plugin")));
// [GIVEN] New plugin for registration
AudioPluginInfo newPluginInfo;
newPluginInfo.type = AudioPluginType::Instrument;
newPluginInfo.meta.id = "new_plugin";
newPluginInfo.meta.type = AudioResourceType::VstPlugin;
newPluginInfo.path = "/path/to/new/plugin/plugin.vst";
newPluginInfo.enabled = true;
expectedPluginInfoList.push_back(newPluginInfo);
// [THEN] The plugin will be written to the corresponding file
mu::ByteArray expectedNewPluginData = pluginInfoToJson(newPluginInfo);
EXPECT_CALL(*m_fileSystem, writeFile(m_knownAudioPluginsDir + "/" + newPluginInfo.meta.id + ".json", expectedNewPluginData))
.WillOnce(Return(mu::make_ok()));
// [WHEN] Register it
mu::Ret ret = m_knownPlugins->registerPlugin(newPluginInfo);
// [THEN] The plugin successfully registered
EXPECT_TRUE(ret);
actualPluginInfoList = m_knownPlugins->pluginInfoList();
EXPECT_EQ(expectedPluginInfoList.size(), actualPluginInfoList.size());
for (const AudioPluginInfo& actualInfo : actualPluginInfoList) {
EXPECT_TRUE(mu::contains(expectedPluginInfoList, actualInfo));
EXPECT_TRUE(m_knownPlugins->exists(actualInfo.path));
EXPECT_TRUE(m_knownPlugins->exists(actualInfo.meta.id));
EXPECT_EQ(actualInfo.path, m_knownPlugins->pluginPath(actualInfo.meta.id));
}
// [WHEN] Unregister the first plugin in the list
AudioPluginInfo unregisteredPlugin = mu::takeFirst(expectedPluginInfoList);
EXPECT_CALL(*m_fileSystem, remove(m_knownAudioPluginsDir + "/" + unregisteredPlugin.meta.id + ".json", false))
.WillOnce(Return(mu::make_ok()));
ret = m_knownPlugins->unregisterPlugin(unregisteredPlugin.meta.id);
// [THEN] The plugin successfully unregistered
EXPECT_TRUE(ret);
EXPECT_FALSE(m_knownPlugins->exists(unregisteredPlugin.path));
EXPECT_FALSE(m_knownPlugins->exists(unregisteredPlugin.meta.id));
EXPECT_EQ(m_knownPlugins->pluginPath(unregisteredPlugin.meta.id), "");
actualPluginInfoList = m_knownPlugins->pluginInfoList();
EXPECT_FALSE(mu::contains(actualPluginInfoList, unregisteredPlugin));
}

View file

@ -0,0 +1,69 @@
/*
* SPDX-License-Identifier: GPL-3.0-only
* MuseScore-CLA-applies
*
* MuseScore
* Music Composition & Notation
*
* Copyright (C) 2021 MuseScore BVBA and others
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef MU_FRAMEWORK_AUDIOCONFIGURATIONMOCK_H
#define MU_FRAMEWORK_AUDIOCONFIGURATIONMOCK_H
#include <gmock/gmock.h>
#include "framework/audio/iaudioconfiguration.h"
namespace mu::audio {
class AudioConfigurationMock : public IAudioConfiguration
{
public:
MOCK_METHOD(std::vector<std::string>, availableAudioApiList, (), (const, override));
MOCK_METHOD(std::string, currentAudioApi, (), (const, override));
MOCK_METHOD(void, setCurrentAudioApi, (const std::string&), (override));
MOCK_METHOD(std::string, audioOutputDeviceId, (), (const, override));
MOCK_METHOD(void, setAudioOutputDeviceId, (const std::string&), (override));
MOCK_METHOD(async::Notification, audioOutputDeviceIdChanged, (), (const, override));
MOCK_METHOD(audioch_t, audioChannelsCount, (), (const, override));
MOCK_METHOD(unsigned int, driverBufferSize, (), (const, override));
MOCK_METHOD(void, setDriverBufferSize, (unsigned int), (override));
MOCK_METHOD(async::Notification, driverBufferSizeChanged, (), (const, override));
MOCK_METHOD(samples_t, renderStep, (), (const, override));
MOCK_METHOD(unsigned int, sampleRate, (), (const, override));
MOCK_METHOD(void, setSampleRate, (unsigned int), (override));
MOCK_METHOD(async::Notification, sampleRateChanged, (), (const, override));
MOCK_METHOD(AudioInputParams, defaultAudioInputParams, (), (const, override));
MOCK_METHOD(io::paths_t, soundFontDirectories, (), (const, override));
MOCK_METHOD(io::paths_t, userSoundFontDirectories, (), (const, override));
MOCK_METHOD(void, setUserSoundFontDirectories, (const io::paths_t&), (override));
MOCK_METHOD(async::Channel<io::paths_t>, soundFontDirectoriesChanged, (), (const, override));
MOCK_METHOD(const synth::SynthesizerState&, synthesizerState, (), (const, override));
MOCK_METHOD(Ret, saveSynthesizerState, (const synth::SynthesizerState&), (override));
MOCK_METHOD(async::Notification, synthesizerStateChanged, (), (const, override));
MOCK_METHOD(async::Notification, synthesizerStateGroupChanged, (const std::string&), (const, override));
MOCK_METHOD(io::path_t, knownAudioPluginsDir, (), (const, override));
};
}
#endif // MU_FRAMEWORK_AUDIOCONFIGURATIONMOCK_H

View file

@ -300,7 +300,7 @@ inline bool remove(Map& c, const T& v)
template<typename Map, typename K>
inline auto take(Map& m, const K& k) -> typename Map::mapped_type
{
auto it = m.find(static_cast<int>(k));
auto it = m.find(k);
if (it != m.end()) {
auto v = it->second;
m.erase(it);