implemented the register of known plugins
This commit is contained in:
parent
b0097353f6
commit
497435c2cb
14 changed files with 675 additions and 3 deletions
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
52
src/framework/audio/iknownaudiopluginsregister.h
Normal file
52
src/framework/audio/iknownaudiopluginsregister.h
Normal 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
|
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
197
src/framework/audio/internal/knownaudiopluginsregister.cpp
Normal file
197
src/framework/audio/internal/knownaudiopluginsregister.cpp
Normal 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";
|
||||
}
|
58
src/framework/audio/internal/knownaudiopluginsregister.h
Normal file
58
src/framework/audio/internal/knownaudiopluginsregister.h
Normal 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
|
31
src/framework/audio/tests/CMakeLists.txt
Normal file
31
src/framework/audio/tests/CMakeLists.txt
Normal 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)
|
||||
|
227
src/framework/audio/tests/knownaudiopluginsregistertest.cpp
Normal file
227
src/framework/audio/tests/knownaudiopluginsregistertest.cpp
Normal 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));
|
||||
}
|
69
src/framework/audio/tests/mocks/audioconfigurationmock.h
Normal file
69
src/framework/audio/tests/mocks/audioconfigurationmock.h
Normal 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
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue