Merge remote-tracking branch 'upstream/master' into dynamic-effect-dialog
Merge master from upstream to get past the problems related to the submodule for resid once and for all. See #6916.
This commit is contained in:
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -40,9 +40,9 @@
|
||||
[submodule "plugins/CarlaBase/carla"]
|
||||
path = plugins/CarlaBase/carla
|
||||
url = https://github.com/falktx/carla
|
||||
[submodule "plugins/Sid/resid"]
|
||||
path = plugins/Sid/resid
|
||||
url = https://github.com/simonowen/resid
|
||||
[submodule "plugins/Sid/resid/resid"]
|
||||
path = plugins/Sid/resid/resid
|
||||
url = https://github.com/libsidplayfp/resid
|
||||
[submodule "src/3rdparty/jack2"]
|
||||
path = src/3rdparty/jack2
|
||||
url = https://github.com/jackaudio/jack2
|
||||
|
||||
@@ -35,7 +35,7 @@ INCLUDE(GenerateExportHeader)
|
||||
|
||||
STRING(TOUPPER "${CMAKE_PROJECT_NAME}" PROJECT_NAME_UCASE)
|
||||
|
||||
SET(PROJECT_YEAR 2020)
|
||||
SET(PROJECT_YEAR 2023)
|
||||
|
||||
SET(PROJECT_AUTHOR "LMMS Developers")
|
||||
SET(PROJECT_URL "https://lmms.io")
|
||||
@@ -77,6 +77,7 @@ OPTION(WANT_SOUNDIO "Include libsoundio support" ON)
|
||||
OPTION(WANT_SDL "Include SDL (Simple DirectMedia Layer) support" ON)
|
||||
OPTION(WANT_SF2 "Include SoundFont2 player plugin" ON)
|
||||
OPTION(WANT_GIG "Include GIG player plugin" ON)
|
||||
option(WANT_SID "Include Sid instrument" ON)
|
||||
OPTION(WANT_STK "Include Stk (Synthesis Toolkit) support" ON)
|
||||
OPTION(WANT_SWH "Include Steve Harris's LADSPA plugins" ON)
|
||||
OPTION(WANT_TAP "Include Tom's Audio Processing LADSPA plugins" ON)
|
||||
@@ -211,6 +212,13 @@ CHECK_CXX_SOURCE_COMPILES(
|
||||
LMMS_HAVE_SF_COMPLEVEL
|
||||
)
|
||||
|
||||
# check for perl
|
||||
if(LMMS_BUILD_APPLE)
|
||||
# Prefer system perl over Homebrew, MacPorts, etc
|
||||
set(Perl_ROOT "/usr/bin")
|
||||
endif()
|
||||
find_package(Perl)
|
||||
|
||||
IF(WANT_LV2)
|
||||
IF(PKG_CONFIG_FOUND)
|
||||
PKG_CHECK_MODULES(LV2 lv2)
|
||||
@@ -273,11 +281,6 @@ ELSE(WANT_CMT)
|
||||
ENDIF(WANT_CMT)
|
||||
|
||||
IF(WANT_SWH)
|
||||
IF(LMMS_BUILD_APPLE)
|
||||
# Prefer system perl over Homebrew, MacPorts, etc
|
||||
SET(Perl_ROOT "/usr/bin")
|
||||
ENDIF()
|
||||
FIND_PACKAGE(Perl)
|
||||
IF(PERL_FOUND)
|
||||
SET(LMMS_HAVE_SWH TRUE)
|
||||
SET(STATUS_SWH "OK")
|
||||
@@ -349,6 +352,16 @@ IF(WANT_SDL AND NOT LMMS_HAVE_SDL2)
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
# check for Sid
|
||||
if(WANT_SID)
|
||||
if(PERL_FOUND)
|
||||
set(LMMS_HAVE_SID TRUE)
|
||||
set(STATUS_SID "OK")
|
||||
else()
|
||||
set(STATUS_SID "not found, please install perl if you require the Sid instrument")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# check for Stk
|
||||
IF(WANT_STK)
|
||||
FIND_PACKAGE(STK)
|
||||
@@ -816,6 +829,7 @@ MESSAGE(
|
||||
"* ZynAddSubFX instrument : ${STATUS_ZYN}\n"
|
||||
"* Carla Patchbay & Rack : ${STATUS_CARLA}\n"
|
||||
"* SoundFont2 player : ${STATUS_FLUIDSYNTH}\n"
|
||||
"* Sid instrument : ${STATUS_SID}\n"
|
||||
"* Stk Mallets : ${STATUS_STK}\n"
|
||||
"* VST-instrument hoster : ${STATUS_VST}\n"
|
||||
"* VST-effect hoster : ${STATUS_VST}\n"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
********************/
|
||||
|
||||
/* most foreground text items */
|
||||
QLabel, QTreeWidget, QListWidget, QGroupBox, QMenuBar {
|
||||
QLabel, QTreeWidget, QListWidget, QGroupBox, QMenuBar, QCheckBox {
|
||||
color: #d1d8e4;
|
||||
}
|
||||
|
||||
@@ -464,6 +464,10 @@ lmms--gui--EffectSelectDialog QScrollArea {
|
||||
background: #262b30;
|
||||
}
|
||||
|
||||
lmms--gui--SetupDialog QScrollArea {
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
/* the inner boxes in LADSPA effect windows */
|
||||
|
||||
lmms--gui--EffectControlDialog QGroupBox {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* AudioDeviceSetupWidget.h - Base class for audio device setup widgets
|
||||
*
|
||||
* Copyright (c) 2004-2015 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2023- Michael Gregorius
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
@@ -25,12 +26,12 @@
|
||||
#ifndef LMMS_GUI_AUDIO_DEVICE_SETUP_WIDGET_H
|
||||
#define LMMS_GUI_AUDIO_DEVICE_SETUP_WIDGET_H
|
||||
|
||||
#include "TabWidget.h"
|
||||
#include <QGroupBox>
|
||||
|
||||
namespace lmms::gui
|
||||
{
|
||||
|
||||
class AudioDeviceSetupWidget : public TabWidget
|
||||
class AudioDeviceSetupWidget : public QGroupBox
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
@@ -197,6 +197,7 @@ public:
|
||||
|
||||
// audio-device-stuff
|
||||
|
||||
bool renderOnly() const { return m_renderOnly; }
|
||||
// Returns the current audio device's name. This is not necessarily
|
||||
// the user's preferred audio device, in case you were thinking that.
|
||||
inline const QString & audioDevName() const
|
||||
|
||||
@@ -48,8 +48,8 @@ public:
|
||||
m_value{value}
|
||||
{}
|
||||
|
||||
constexpr auto testAll(Flags flags) const -> bool { return *this & flags == flags; }
|
||||
constexpr auto testAny(Flags flags) const -> bool { return *this & flags != Flags{}; }
|
||||
constexpr auto testAll(Flags flags) const -> bool { return (*this & flags) == flags; }
|
||||
constexpr auto testAny(Flags flags) const -> bool { return (*this & flags) != Flags{}; }
|
||||
constexpr auto testFlag(EnumType flag) const -> bool { return static_cast<bool>(*this & flag); }
|
||||
|
||||
constexpr auto operator~() const -> Flags { return Flags{~m_value}; }
|
||||
|
||||
93
include/LmmsSemaphore.h
Normal file
93
include/LmmsSemaphore.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Semaphore.h - Semaphore declaration
|
||||
*
|
||||
* Copyright (c) 2022-2022 Johannes Lorenz <jlsf2013$users.sourceforge.net, $=@>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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 (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code has been copied and adapted from https://github.com/drobilla/jalv
|
||||
* File src/zix/sem.h
|
||||
*/
|
||||
|
||||
#ifndef LMMS_SEMAPHORE_H
|
||||
#define LMMS_SEMAPHORE_H
|
||||
|
||||
#include "lmmsconfig.h"
|
||||
|
||||
#ifdef LMMS_BUILD_APPLE
|
||||
# include <mach/mach.h>
|
||||
#elif defined(LMMS_BUILD_WIN32)
|
||||
# include <windows.h>
|
||||
#else
|
||||
# include <semaphore.h>
|
||||
#endif
|
||||
|
||||
#include <system_error>
|
||||
|
||||
namespace lmms {
|
||||
|
||||
/**
|
||||
A counting semaphore.
|
||||
|
||||
This is an integer that is always positive, and has two main operations:
|
||||
increment (post) and decrement (wait). If a decrement can not be performed
|
||||
(i.e. the value is 0) the caller will be blocked until another thread posts
|
||||
and the operation can succeed.
|
||||
|
||||
Semaphores can be created with any starting value, but typically this will
|
||||
be 0 so the semaphore can be used as a simple signal where each post
|
||||
corresponds to one wait.
|
||||
|
||||
Semaphores are very efficient (much moreso than a mutex/cond pair). In
|
||||
particular, at least on Linux, post is async-signal-safe, which means it
|
||||
does not block and will not be interrupted. If you need to signal from
|
||||
a realtime thread, this is the most appropriate primitive to use.
|
||||
|
||||
@note Likely outdated with C++20's std::counting_semaphore
|
||||
(though we have to check that this will be RT conforming on all platforms)
|
||||
*/
|
||||
class Semaphore
|
||||
{
|
||||
public:
|
||||
Semaphore(unsigned initial);
|
||||
Semaphore(const Semaphore&) = delete;
|
||||
Semaphore& operator=(const Semaphore&) = delete;
|
||||
Semaphore(Semaphore&&) = delete;
|
||||
Semaphore& operator=(Semaphore&&) = delete;
|
||||
~Semaphore();
|
||||
|
||||
void post();
|
||||
void wait();
|
||||
bool tryWait();
|
||||
|
||||
private:
|
||||
#ifdef LMMS_BUILD_APPLE
|
||||
semaphore_t m_sem;
|
||||
#elif defined(LMMS_BUILD_WIN32)
|
||||
HANDLE m_sem;
|
||||
#else
|
||||
sem_t m_sem;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif // LMMS_SEMAPHORE_H
|
||||
@@ -51,13 +51,14 @@ public:
|
||||
std::size_t capacity() const {return m_buffer.maximum_eventual_write_space();}
|
||||
std::size_t free() const {return m_buffer.write_space();}
|
||||
void wakeAll() {m_notifier.wakeAll();}
|
||||
std::size_t write(const sampleFrame *src, std::size_t cnt, bool notify = false)
|
||||
std::size_t write(const T *src, std::size_t cnt, bool notify = false)
|
||||
{
|
||||
std::size_t written = LocklessRingBuffer<T>::m_buffer.write(src, cnt);
|
||||
// Let all waiting readers know new data are available.
|
||||
if (notify) {LocklessRingBuffer<T>::m_notifier.wakeAll();}
|
||||
return written;
|
||||
}
|
||||
void mlock() { m_buffer.mlock(); }
|
||||
|
||||
protected:
|
||||
ringbuffer_t<T> m_buffer;
|
||||
|
||||
@@ -47,8 +47,14 @@ struct LilvNodesDeleter
|
||||
void operator()(LilvNodes* n) { lilv_nodes_free(n); }
|
||||
};
|
||||
|
||||
struct LilvScalePointsDeleter
|
||||
{
|
||||
void operator()(LilvScalePoints* s) { lilv_scale_points_free(s); }
|
||||
};
|
||||
|
||||
using AutoLilvNode = std::unique_ptr<LilvNode, LilvNodeDeleter>;
|
||||
using AutoLilvNodes = std::unique_ptr<LilvNodes, LilvNodesDeleter>;
|
||||
using AutoLilvScalePoints = std::unique_ptr<LilvScalePoints, LilvScalePointsDeleter>;
|
||||
|
||||
/**
|
||||
Return QString from a plugin's node, everything will be freed automatically
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#ifdef LMMS_HAVE_LV2
|
||||
|
||||
#include <map>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include "Lv2Manager.h"
|
||||
|
||||
@@ -78,7 +79,7 @@ private:
|
||||
//! pointers to m_features, required for lilv_plugin_instantiate
|
||||
std::vector<const LV2_Feature*> m_featurePointers;
|
||||
//! features + data, ordered by URI
|
||||
std::map<const char*, void*, Lv2Manager::CmpStr> m_featureByUri;
|
||||
std::map<std::string_view, void*> m_featureByUri;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string_view>
|
||||
#include <lilv/lilv.h>
|
||||
|
||||
#include "Lv2Basics.h"
|
||||
@@ -120,15 +121,9 @@ public:
|
||||
Iterator begin() { return m_lv2InfoMap.begin(); }
|
||||
Iterator end() { return m_lv2InfoMap.end(); }
|
||||
|
||||
//! strcmp based key comparator for std::set and std::map
|
||||
struct CmpStr
|
||||
{
|
||||
bool operator()(char const *a, char const *b) const;
|
||||
};
|
||||
|
||||
UridMap& uridMap() { return m_uridMap; }
|
||||
const Lv2UridCache& uridCache() const { return m_uridCache; }
|
||||
const std::set<const char*, CmpStr>& supportedFeatureURIs() const
|
||||
const std::set<std::string_view>& supportedFeatureURIs() const
|
||||
{
|
||||
return m_supportedFeatureURIs;
|
||||
}
|
||||
@@ -136,17 +131,21 @@ public:
|
||||
AutoLilvNodes findNodes(const LilvNode *subject,
|
||||
const LilvNode *predicate, const LilvNode *object);
|
||||
|
||||
static const std::set<const char*, Lv2Manager::CmpStr>& getPluginBlacklist()
|
||||
static const std::set<std::string_view>& getPluginBlacklist()
|
||||
{
|
||||
return pluginBlacklist;
|
||||
}
|
||||
static const std::set<std::string_view>& getPluginBlacklistBuffersizeLessThan32()
|
||||
{
|
||||
return pluginBlacklistBuffersizeLessThan32;
|
||||
}
|
||||
|
||||
private:
|
||||
// general data
|
||||
bool m_debug; //!< if set, debug output will be printed
|
||||
LilvWorld* m_world;
|
||||
Lv2InfoMap m_lv2InfoMap;
|
||||
std::set<const char*, CmpStr> m_supportedFeatureURIs;
|
||||
std::set<std::string_view> m_supportedFeatureURIs;
|
||||
|
||||
// feature data that are common for all Lv2Proc
|
||||
UridMap m_uridMap;
|
||||
@@ -155,7 +154,8 @@ private:
|
||||
Lv2UridCache m_uridCache;
|
||||
|
||||
// static
|
||||
static const std::set<const char*, Lv2Manager::CmpStr> pluginBlacklist;
|
||||
static const std::set<std::string_view>
|
||||
pluginBlacklist, pluginBlacklistBuffersizeLessThan32;
|
||||
|
||||
// functions
|
||||
bool isSubclassOf(const LilvPluginClass *clvss, const char *uriStr);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Lv2Proc.h - Lv2 processor class
|
||||
*
|
||||
* Copyright (c) 2019-2020 Johannes Lorenz <jlsf2013$users.sourceforge.net, $=@>
|
||||
* Copyright (c) 2019-2022 Johannes Lorenz <jlsf2013$users.sourceforge.net, $=@>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
@@ -31,11 +31,14 @@
|
||||
|
||||
#include <lilv/lilv.h>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "LinkedModelGroups.h"
|
||||
#include "LmmsSemaphore.h"
|
||||
#include "Lv2Basics.h"
|
||||
#include "Lv2Features.h"
|
||||
#include "Lv2Options.h"
|
||||
#include "LinkedModelGroups.h"
|
||||
#include "Lv2Worker.h"
|
||||
#include "Plugin.h"
|
||||
#include "../src/3rdparty/ringbuffer/include/ringbuffer/ringbuffer.h"
|
||||
#include "TimePos.h"
|
||||
@@ -174,8 +177,14 @@ private:
|
||||
const LilvPlugin* m_plugin;
|
||||
LilvInstance* m_instance;
|
||||
Lv2Features m_features;
|
||||
|
||||
// options
|
||||
Lv2Options m_options;
|
||||
|
||||
// worker
|
||||
std::optional<Lv2Worker> m_worker;
|
||||
Semaphore m_workLock; // this must be shared by different workers
|
||||
|
||||
// full list of ports
|
||||
std::vector<std::unique_ptr<Lv2Ports::PortBase>> m_ports;
|
||||
// quick reference to specific, unique ports
|
||||
|
||||
@@ -55,8 +55,6 @@ class UridMap
|
||||
LV2_URID_Map m_mapFeature;
|
||||
LV2_URID_Unmap m_unmapFeature;
|
||||
|
||||
LV2_URID m_lastUrid = 0;
|
||||
|
||||
public:
|
||||
//! constructor; will set up the features
|
||||
UridMap();
|
||||
|
||||
@@ -56,7 +56,7 @@ class Lv2ViewProc : public LinkedModelGroupView
|
||||
{
|
||||
public:
|
||||
//! @param colNum numbers of columns for the controls
|
||||
Lv2ViewProc(QWidget *parent, Lv2Proc *ctrlBase, int colNum);
|
||||
Lv2ViewProc(QWidget *parent, Lv2Proc *proc, int colNum);
|
||||
~Lv2ViewProc() override = default;
|
||||
|
||||
private:
|
||||
|
||||
93
include/Lv2Worker.h
Normal file
93
include/Lv2Worker.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Lv2Worker.h - Lv2Worker class
|
||||
*
|
||||
* Copyright (c) 2022-2022 Johannes Lorenz <jlsf2013$users.sourceforge.net, $=@>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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 (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV2WORKER_H
|
||||
#define LV2WORKER_H
|
||||
|
||||
#include "lmmsconfig.h"
|
||||
|
||||
#ifdef LMMS_HAVE_LV2
|
||||
|
||||
#include <lilv/lilv.h>
|
||||
#include <lv2/lv2plug.in/ns/ext/worker/worker.h>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "LocklessRingBuffer.h"
|
||||
#include "LmmsSemaphore.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
/**
|
||||
Worker container
|
||||
*/
|
||||
class Lv2Worker
|
||||
{
|
||||
public:
|
||||
// CTOR/DTOR/feature access
|
||||
Lv2Worker(const LV2_Worker_Interface* iface, Semaphore* common_work_lock, bool threaded);
|
||||
~Lv2Worker();
|
||||
void setHandle(LV2_Handle handle) { m_handle = handle; }
|
||||
LV2_Worker_Schedule* feature() { return &m_scheduleFeature; }
|
||||
|
||||
// public API
|
||||
void emitResponses();
|
||||
void notifyPluginThatRunFinished()
|
||||
{
|
||||
if(m_iface->end_run) { m_iface->end_run(m_scheduleFeature.handle); }
|
||||
}
|
||||
|
||||
// to be called only by static functions
|
||||
LV2_Worker_Status scheduleWork(uint32_t size, const void* data);
|
||||
LV2_Worker_Status respond(uint32_t size, const void* data);
|
||||
|
||||
private:
|
||||
// functions
|
||||
void workerFunc();
|
||||
std::size_t bufferSize() const; //!< size of internal buffers
|
||||
|
||||
// parameters
|
||||
const LV2_Worker_Interface* m_iface;
|
||||
bool m_threaded;
|
||||
LV2_Handle m_handle;
|
||||
LV2_Worker_Schedule m_scheduleFeature;
|
||||
|
||||
// threading/synchronization
|
||||
std::thread m_thread;
|
||||
std::vector<char> m_response; //!< buffer where single requests from m_requests are unpacked
|
||||
LocklessRingBuffer<char> m_requests, m_responses; //!< ringbuffer to queue multiple requests
|
||||
LocklessRingBufferReader<char> m_requestsReader, m_responsesReader;
|
||||
std::atomic<bool> m_exit = false; //!< Whether the worker function should keep looping
|
||||
Semaphore m_sem;
|
||||
Semaphore* m_workLock;
|
||||
};
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif // LMMS_HAVE_LV2
|
||||
|
||||
#endif // LV2WORKER_H
|
||||
|
||||
@@ -212,7 +212,7 @@ private:
|
||||
int32_t m_sysExDataLen; // len of m_sysExData
|
||||
} m_data;
|
||||
|
||||
const char* m_sysExData;
|
||||
[[maybe_unused]] const char* m_sysExData;
|
||||
const void* m_sourcePort;
|
||||
|
||||
// Stores the source of the MidiEvent: Internal or External (hardware controllers).
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#ifndef LMMS_GUI_MIDI_SETUP_WIDGET_H
|
||||
#define LMMS_GUI_MIDI_SETUP_WIDGET_H
|
||||
|
||||
#include "TabWidget.h"
|
||||
#include <QGroupBox>
|
||||
|
||||
class QLineEdit;
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace lmms::gui
|
||||
{
|
||||
|
||||
|
||||
class MidiSetupWidget : public TabWidget
|
||||
class MidiSetupWidget : public QGroupBox
|
||||
{
|
||||
Q_OBJECT
|
||||
MidiSetupWidget( const QString & caption, const QString & configSection,
|
||||
|
||||
@@ -30,12 +30,12 @@
|
||||
|
||||
#include "AudioDevice.h"
|
||||
#include "AudioDeviceSetupWidget.h"
|
||||
#include "LedCheckBox.h"
|
||||
#include "lmmsconfig.h"
|
||||
#include "MidiClient.h"
|
||||
#include "MidiSetupWidget.h"
|
||||
|
||||
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class QLabel;
|
||||
class QLineEdit;
|
||||
@@ -102,6 +102,7 @@ private slots:
|
||||
// Audio settings widget.
|
||||
void audioInterfaceChanged(const QString & driver);
|
||||
void toggleHQAudioDev(bool enabled);
|
||||
void updateBufferSizeWarning(int value);
|
||||
void setBufferSize(int value);
|
||||
void resetBufferSize();
|
||||
|
||||
@@ -155,14 +156,14 @@ private:
|
||||
bool m_enableRunningAutoSave;
|
||||
QSlider * m_saveIntervalSlider;
|
||||
QLabel * m_saveIntervalLbl;
|
||||
LedCheckBox * m_autoSave;
|
||||
LedCheckBox * m_runningAutoSave;
|
||||
QCheckBox * m_autoSave;
|
||||
QCheckBox * m_runningAutoSave;
|
||||
bool m_smoothScroll;
|
||||
bool m_animateAFP;
|
||||
QLabel * m_vstEmbedLbl;
|
||||
QComboBox* m_vstEmbedComboBox;
|
||||
QString m_vstEmbedMethod;
|
||||
LedCheckBox * m_vstAlwaysOnTopCheckBox;
|
||||
QCheckBox * m_vstAlwaysOnTopCheckBox;
|
||||
bool m_vstAlwaysOnTop;
|
||||
bool m_disableAutoQuit;
|
||||
|
||||
@@ -179,6 +180,7 @@ private:
|
||||
int m_bufferSize;
|
||||
QSlider * m_bufferSizeSlider;
|
||||
QLabel * m_bufferSizeLbl;
|
||||
QLabel * m_bufferSizeWarnLbl;
|
||||
|
||||
// MIDI settings widgets.
|
||||
QComboBox * m_midiInterfaces;
|
||||
|
||||
@@ -49,7 +49,12 @@ public:
|
||||
|
||||
TabButton * addTab( QWidget * _w, const QString & _text,
|
||||
int _id, bool _add_stretch = false,
|
||||
bool _text_is_tooltip = false );
|
||||
bool _text_is_tooltip = false,
|
||||
// TODO Remove fixWidgetToParentSize once it is used
|
||||
// with false everywhere.
|
||||
// At the time of writing it is only used in
|
||||
// LadspaBrowser with default parameters.
|
||||
bool fixWidgetToParentSize = true );
|
||||
void removeTab( int _id );
|
||||
|
||||
inline void setExclusive( bool _on )
|
||||
|
||||
@@ -325,6 +325,32 @@ static inline T absMin( T a, T b )
|
||||
return std::abs(a) < std::abs(b) ? a : b;
|
||||
}
|
||||
|
||||
// @brief Calculate number of digits which LcdSpinBox would show for a given number
|
||||
// @note Once we upgrade to C++20, we could probably use std::formatted_size
|
||||
static inline int numDigitsAsInt(float f)
|
||||
{
|
||||
// use rounding:
|
||||
// LcdSpinBox sometimes uses roundf(), sometimes cast rounding
|
||||
// we use rounding to be on the "safe side"
|
||||
const float rounded = roundf(f);
|
||||
int asInt = static_cast<int>(rounded);
|
||||
int digits = 1; // always at least 1
|
||||
if(asInt < 0)
|
||||
{
|
||||
++digits;
|
||||
asInt = -asInt;
|
||||
}
|
||||
// "asInt" is positive from now
|
||||
int32_t power = 1;
|
||||
for(int32_t i = 1; i<10; ++i)
|
||||
{
|
||||
power *= 10;
|
||||
if(static_cast<int32_t>(asInt) >= power) { ++digits; } // 2 digits for >=10, 3 for >=100
|
||||
else { break; }
|
||||
}
|
||||
return digits;
|
||||
}
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
|
||||
@@ -177,7 +177,8 @@ void CompressorEffect::calcRange()
|
||||
|
||||
void CompressorEffect::resizeRMS()
|
||||
{
|
||||
m_rmsTimeConst = exp(-1.f / (m_compressorControls.m_rmsModel.value() * 0.001f * m_sampleRate));
|
||||
const float rmsValue = m_compressorControls.m_rmsModel.value();
|
||||
m_rmsTimeConst = (rmsValue > 0) ? exp(-1.f / (rmsValue * 0.001f * m_sampleRate)) : 0;
|
||||
}
|
||||
|
||||
void CompressorEffect::calcLookaheadLength()
|
||||
|
||||
@@ -497,10 +497,12 @@ void CompressorControlDialog::redrawKnee()
|
||||
float actualRatio = m_controls->m_limiterModel.value() ? 0 : m_controls->m_effect->m_ratioVal;
|
||||
|
||||
// Calculate endpoints for the two straight lines
|
||||
float kneePoint1 = m_controls->m_effect->m_thresholdVal - m_controls->m_effect->m_kneeVal;
|
||||
float kneePoint2X = m_controls->m_effect->m_thresholdVal + m_controls->m_effect->m_kneeVal;
|
||||
float kneePoint2Y = (m_controls->m_effect->m_thresholdVal + (-m_controls->m_effect->m_thresholdVal * (actualRatio * (m_controls->m_effect->m_kneeVal / -m_controls->m_effect->m_thresholdVal))));
|
||||
float ratioPoint = m_controls->m_effect->m_thresholdVal + (-m_controls->m_effect->m_thresholdVal * actualRatio);
|
||||
const float thresholdVal = m_controls->m_effect->m_thresholdVal;
|
||||
const float kneeVal = m_controls->m_effect->m_kneeVal;
|
||||
float kneePoint1 = thresholdVal - kneeVal;
|
||||
float kneePoint2X = thresholdVal + kneeVal;
|
||||
float kneePoint2Y = thresholdVal + kneeVal * actualRatio;
|
||||
float ratioPoint = thresholdVal + (-thresholdVal * actualRatio);
|
||||
|
||||
// Draw two straight lines
|
||||
m_p.drawLine(0, m_kneeWindowSizeY, dbfsToXPoint(kneePoint1), dbfsToYPoint(kneePoint1));
|
||||
@@ -510,7 +512,7 @@ void CompressorControlDialog::redrawKnee()
|
||||
}
|
||||
|
||||
// Draw knee section
|
||||
if (m_controls->m_effect->m_kneeVal)
|
||||
if (kneeVal)
|
||||
{
|
||||
m_p.setPen(QPen(m_kneeColor2, 3));
|
||||
|
||||
@@ -522,8 +524,8 @@ void CompressorControlDialog::redrawKnee()
|
||||
{
|
||||
newPoint[0] = linearInterpolate(kneePoint1, kneePoint2X, (i + 1) / (float)COMP_KNEE_LINES);
|
||||
|
||||
const float temp = newPoint[0] - m_controls->m_effect->m_thresholdVal + m_controls->m_effect->m_kneeVal;
|
||||
newPoint[1] = (newPoint[0] + (actualRatio - 1) * temp * temp / (4 * m_controls->m_effect->m_kneeVal));
|
||||
const float temp = newPoint[0] - thresholdVal + kneeVal;
|
||||
newPoint[1] = (newPoint[0] + (actualRatio - 1) * temp * temp / (4 * kneeVal));
|
||||
|
||||
m_p.drawLine(dbfsToXPoint(prevPoint[0]), dbfsToYPoint(prevPoint[1]), dbfsToXPoint(newPoint[0]), dbfsToYPoint(newPoint[1]));
|
||||
|
||||
@@ -768,4 +770,4 @@ void CompressorControlDialog::resetCompressorView()
|
||||
}
|
||||
|
||||
|
||||
} // namespace lmms::gui
|
||||
} // namespace lmms::gui
|
||||
|
||||
@@ -4,22 +4,22 @@ BUILD_PLUGIN(ladspaeffect LadspaEffect.cpp LadspaControls.cpp LadspaControlDialo
|
||||
|
||||
SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/ladspa")
|
||||
|
||||
IF(WANT_CAPS)
|
||||
IF(LMMS_HAVE_CAPS)
|
||||
ADD_SUBDIRECTORY(caps)
|
||||
ENDIF(WANT_CAPS)
|
||||
ENDIF()
|
||||
|
||||
IF(WANT_TAP)
|
||||
IF(LMMS_HAVE_TAP)
|
||||
ADD_SUBDIRECTORY(tap)
|
||||
ENDIF(WANT_TAP)
|
||||
ENDIF()
|
||||
|
||||
IF(WANT_SWH)
|
||||
IF(LMMS_HAVE_SWH)
|
||||
ADD_SUBDIRECTORY(swh)
|
||||
ENDIF(WANT_SWH)
|
||||
ENDIF()
|
||||
|
||||
IF(WANT_CMT)
|
||||
IF(LMMS_HAVE_CMT)
|
||||
ADD_SUBDIRECTORY(cmt)
|
||||
ENDIF(WANT_CMT)
|
||||
ENDIF()
|
||||
|
||||
IF(WANT_CALF)
|
||||
IF(LMMS_HAVE_CALF)
|
||||
ADD_SUBDIRECTORY(calf)
|
||||
ENDIF(WANT_CALF)
|
||||
ENDIF()
|
||||
|
||||
@@ -1,51 +1,14 @@
|
||||
INCLUDE(BuildPlugin)
|
||||
|
||||
INCLUDE_DIRECTORIES(resid)
|
||||
if(NOT LMMS_HAVE_SID)
|
||||
return()
|
||||
endif()
|
||||
|
||||
BUILD_PLUGIN(sid
|
||||
SidInstrument.cpp
|
||||
SidInstrument.h
|
||||
resid/envelope.h
|
||||
resid/extfilt.h
|
||||
resid/filter.h
|
||||
resid/pot.h
|
||||
resid/siddefs.h
|
||||
resid/sid.h
|
||||
resid/spline.h
|
||||
resid/voice.h
|
||||
resid/wave.h
|
||||
resid/envelope.cc
|
||||
resid/extfilt.cc
|
||||
resid/filter.cc
|
||||
resid/pot.cc
|
||||
resid/sid.cc
|
||||
resid/version.cc
|
||||
resid/voice.cc
|
||||
resid/wave6581_PS_.cc
|
||||
resid/wave6581_PST.cc
|
||||
resid/wave6581_P_T.cc
|
||||
resid/wave6581__ST.cc
|
||||
resid/wave8580_PS_.cc
|
||||
resid/wave8580_PST.cc
|
||||
resid/wave8580_P_T.cc
|
||||
resid/wave8580__ST.cc
|
||||
resid/wave.cc
|
||||
MOCFILES SidInstrument.h
|
||||
EMBEDDED_RESOURCES *.png)
|
||||
|
||||
# Parse VERSION
|
||||
FILE(READ "resid/CMakeLists.txt" lines)
|
||||
STRING(REGEX MATCH "set\\(MAJOR_VER [A-Za-z0-9_]*\\)" MAJOR_RAW ${lines})
|
||||
STRING(REGEX MATCH "set\\(MINOR_VER [A-Za-z0-9_]*\\)" MINOR_RAW ${lines})
|
||||
STRING(REGEX MATCH "set\\(PATCH_VER [A-Za-z0-9_]*\\)" PATCH_RAW ${lines})
|
||||
SEPARATE_ARGUMENTS(MAJOR_RAW)
|
||||
SEPARATE_ARGUMENTS(MINOR_RAW)
|
||||
SEPARATE_ARGUMENTS(PATCH_RAW)
|
||||
LIST(GET MAJOR_RAW 1 MAJOR_RAW)
|
||||
LIST(GET MINOR_RAW 1 MINOR_RAW)
|
||||
LIST(GET PATCH_RAW 1 PATCH_RAW)
|
||||
STRING(REPLACE ")" "" MAJOR_VER "${MAJOR_RAW}")
|
||||
STRING(REPLACE ")" "" MINOR_VER "${MINOR_RAW}")
|
||||
STRING(REPLACE ")" "" PATCH_VER "${PATCH_RAW}")
|
||||
|
||||
TARGET_COMPILE_DEFINITIONS(sid PRIVATE VERSION="${MAJOR_VER}.${MINOR_VER}.${PATCH_VER}")
|
||||
add_subdirectory(resid)
|
||||
target_link_libraries(sid resid)
|
||||
|
||||
@@ -239,7 +239,7 @@ f_cnt_t SidInstrument::desiredReleaseFrames() const
|
||||
|
||||
|
||||
|
||||
static int sid_fillbuffer(unsigned char* sidreg, SID *sid, int tdelta, short *ptr, int samples)
|
||||
static int sid_fillbuffer(unsigned char* sidreg, reSID::SID *sid, int tdelta, short *ptr, int samples)
|
||||
{
|
||||
int tdelta2;
|
||||
int result;
|
||||
@@ -302,9 +302,9 @@ void SidInstrument::playNote( NotePlayHandle * _n,
|
||||
|
||||
if (!_n->m_pluginData)
|
||||
{
|
||||
SID *sid = new SID();
|
||||
sid->set_sampling_parameters( clockrate, SAMPLE_FAST, samplerate );
|
||||
sid->set_chip_model( MOS8580 );
|
||||
auto sid = new reSID::SID();
|
||||
sid->set_sampling_parameters(clockrate, reSID::SAMPLE_FAST, samplerate);
|
||||
sid->set_chip_model(reSID::MOS8580);
|
||||
sid->enable_filter( true );
|
||||
sid->reset();
|
||||
_n->m_pluginData = sid;
|
||||
@@ -312,7 +312,7 @@ void SidInstrument::playNote( NotePlayHandle * _n,
|
||||
const fpp_t frames = _n->framesLeftForCurrentPeriod();
|
||||
const f_cnt_t offset = _n->noteOffset();
|
||||
|
||||
SID *sid = static_cast<SID *>( _n->m_pluginData );
|
||||
auto sid = static_cast<reSID::SID*>(_n->m_pluginData);
|
||||
int delta_t = clockrate * frames / samplerate + 4;
|
||||
// avoid variable length array for msvc compat
|
||||
auto buf = reinterpret_cast<short*>(_working_buffer + offset);
|
||||
@@ -325,20 +325,20 @@ void SidInstrument::playNote( NotePlayHandle * _n,
|
||||
|
||||
if( (ChipModel)m_chipModel.value() == ChipModel::MOS6581 )
|
||||
{
|
||||
sid->set_chip_model( MOS6581 );
|
||||
sid->set_chip_model(reSID::MOS6581);
|
||||
}
|
||||
else
|
||||
{
|
||||
sid->set_chip_model( MOS8580 );
|
||||
sid->set_chip_model(reSID::MOS8580);
|
||||
}
|
||||
|
||||
// voices
|
||||
reg8 data8 = 0;
|
||||
reg8 data16 = 0;
|
||||
reg8 base = 0;
|
||||
reSID::reg8 data8 = 0;
|
||||
reSID::reg16 data16 = 0;
|
||||
size_t base = 0;
|
||||
float freq = 0.0;
|
||||
float note = 0.0;
|
||||
for( reg8 i = 0 ; i < 3 ; ++i )
|
||||
for (size_t i = 0; i < 3; ++i)
|
||||
{
|
||||
base = i*7;
|
||||
// freq ( Fn = Fout / Fclk * 16777216 ) + coarse detuning
|
||||
@@ -436,7 +436,7 @@ void SidInstrument::playNote( NotePlayHandle * _n,
|
||||
|
||||
void SidInstrument::deleteNotePluginData( NotePlayHandle * _n )
|
||||
{
|
||||
delete static_cast<SID *>( _n->m_pluginData );
|
||||
delete static_cast<reSID::SID*>(_n->m_pluginData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Submodule plugins/Sid/resid deleted from 02afcc5cef
68
plugins/Sid/resid/CMakeLists.txt
Normal file
68
plugins/Sid/resid/CMakeLists.txt
Normal file
@@ -0,0 +1,68 @@
|
||||
# These are the defaults
|
||||
set(RESID_INLINING 1)
|
||||
set(RESID_INLINE inline)
|
||||
set(RESID_BRANCH_HINTS 1)
|
||||
set(NEW_8580_FILTER 0)
|
||||
|
||||
set(HAVE_BOOL 1)
|
||||
set(HAVE_LOG1P 1)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "GCC|Clang")
|
||||
set(HAVE_BUILTIN_EXPECT 1)
|
||||
else()
|
||||
set(HAVE_BUILTIN_EXPECT 0)
|
||||
endif()
|
||||
|
||||
configure_file(resid/siddefs.h.in resid/siddefs.h @ONLY)
|
||||
|
||||
add_library(resid_objects OBJECT
|
||||
resid/sid.cc
|
||||
resid/voice.cc
|
||||
resid/wave.cc
|
||||
resid/envelope.cc
|
||||
resid/filter.cc
|
||||
resid/dac.cc
|
||||
resid/extfilt.cc
|
||||
resid/pot.cc
|
||||
resid/version.cc
|
||||
)
|
||||
|
||||
target_include_directories(resid_objects PUBLIC
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/resid"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/resid"
|
||||
)
|
||||
target_compile_definitions(resid_objects PUBLIC VERSION="1.0")
|
||||
|
||||
set(RESID_WAVES
|
||||
wave6581_PST
|
||||
wave6581_PS_
|
||||
wave6581_P_T
|
||||
wave6581__ST
|
||||
wave8580_PST
|
||||
wave8580_PS_
|
||||
wave8580_P_T
|
||||
wave8580__ST
|
||||
)
|
||||
|
||||
set(RESID_SAMP2SRC_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/resid/samp2src.pl)
|
||||
foreach(WAVE_DATA IN LISTS RESID_WAVES)
|
||||
set(WAVE_DATA_IN ${CMAKE_CURRENT_SOURCE_DIR}/resid/${WAVE_DATA}.dat)
|
||||
set(WAVE_SRC_OUT ${CMAKE_CURRENT_BINARY_DIR}/resid/${WAVE_DATA}.h)
|
||||
set(WAVE_COMMAND
|
||||
"${PERL_EXECUTABLE}"
|
||||
"${RESID_SAMP2SRC_SCRIPT}"
|
||||
"${WAVE_DATA}"
|
||||
"${WAVE_DATA_IN}"
|
||||
"${WAVE_SRC_OUT}"
|
||||
)
|
||||
add_custom_command(OUTPUT ${WAVE_SRC_OUT} COMMAND ${WAVE_COMMAND} VERBATIM)
|
||||
target_sources(resid_objects PUBLIC ${WAVE_SRC_OUT})
|
||||
endforeach()
|
||||
|
||||
# TODO CMake 3.12: Use target_link_libraries() to propagate usage requirements directly to sid plugin
|
||||
add_library(resid INTERFACE)
|
||||
|
||||
target_sources(resid INTERFACE $<TARGET_OBJECTS:resid_objects>)
|
||||
|
||||
get_target_property(resid_includes resid_objects INCLUDE_DIRECTORIES)
|
||||
target_include_directories(resid INTERFACE ${resid_includes})
|
||||
1
plugins/Sid/resid/resid
Submodule
1
plugins/Sid/resid/resid
Submodule
Submodule plugins/Sid/resid/resid added at ef72462f5f
@@ -87,6 +87,7 @@ MalletsInstrument::MalletsInstrument( InstrumentTrack * _instrument_track ):
|
||||
m_strikeModel( true, this, tr( "Bowed" ) ),
|
||||
m_presetsModel(this),
|
||||
m_spreadModel(0, 0, 255, 1, this, tr( "Spread" )),
|
||||
m_randomModel(0.0f, 0.0f, 1.0f, 0.01f, this, tr("Randomness")),
|
||||
m_versionModel( MALLETS_PRESET_VERSION, 0, MALLETS_PRESET_VERSION, this, "" ),
|
||||
m_isOldVersionModel( false, this, "" ),
|
||||
m_filesMissing( !QDir( ConfigManager::inst()->stkDir() ).exists() ||
|
||||
@@ -155,6 +156,7 @@ void MalletsInstrument::saveSettings( QDomDocument & _doc, QDomElement & _this )
|
||||
|
||||
m_presetsModel.saveSettings( _doc, _this, "preset" );
|
||||
m_spreadModel.saveSettings( _doc, _this, "spread" );
|
||||
m_randomModel.saveSettings(_doc, _this, "randomness");
|
||||
m_versionModel.saveSettings( _doc, _this, "version" );
|
||||
m_isOldVersionModel.saveSettings( _doc, _this, "oldversion" );
|
||||
}
|
||||
@@ -189,6 +191,7 @@ void MalletsInstrument::loadSettings( const QDomElement & _this )
|
||||
|
||||
m_presetsModel.loadSettings( _this, "preset" );
|
||||
m_spreadModel.loadSettings( _this, "spread" );
|
||||
m_randomModel.loadSettings(_this, "randomness");
|
||||
m_isOldVersionModel.loadSettings( _this, "oldversion" );
|
||||
|
||||
// To maintain backward compatibility
|
||||
@@ -284,7 +287,7 @@ void MalletsInstrument::playNote( NotePlayHandle * _n,
|
||||
}
|
||||
|
||||
int p = m_presetsModel.value();
|
||||
|
||||
|
||||
const float freq = _n->frequency();
|
||||
if (!_n->m_pluginData)
|
||||
{
|
||||
@@ -293,6 +296,29 @@ void MalletsInstrument::playNote( NotePlayHandle * _n,
|
||||
m_isOldVersionModel.value() ? 100.0 : 200.0;
|
||||
const float vel = _n->getVolume() / velocityAdjust;
|
||||
|
||||
const float random = m_randomModel.value();
|
||||
float hardness = m_hardnessModel.value();
|
||||
float position = m_positionModel.value();
|
||||
float modulator = m_modulatorModel.value();
|
||||
float crossfade = m_crossfadeModel.value();
|
||||
|
||||
if (p < 9)
|
||||
{
|
||||
hardness += random * static_cast<float>(fast_rand() % 128) - 64.0;
|
||||
hardness = std::clamp(hardness, 0.0f, 128.0f);
|
||||
|
||||
position += random * static_cast<float>(fast_rand() % 64) - 32.0;
|
||||
position = std::clamp(position, 0.0f, 64.0f);
|
||||
}
|
||||
else if (p == 9)
|
||||
{
|
||||
modulator += random * static_cast<float>(fast_rand() % 128) - 64.0;
|
||||
modulator = std::clamp(modulator, 0.0f, 128.0f);
|
||||
|
||||
crossfade += random * static_cast<float>(fast_rand() % 128) - 64.0;
|
||||
crossfade = std::clamp(crossfade, 0.0f, 128.0f);
|
||||
}
|
||||
|
||||
// critical section as STK is not thread-safe
|
||||
static QMutex m;
|
||||
m.lock();
|
||||
@@ -301,8 +327,8 @@ void MalletsInstrument::playNote( NotePlayHandle * _n,
|
||||
_n->m_pluginData = new MalletsSynth( freq,
|
||||
vel,
|
||||
m_stickModel.value(),
|
||||
m_hardnessModel.value(),
|
||||
m_positionModel.value(),
|
||||
hardness,
|
||||
position,
|
||||
m_vibratoGainModel.value(),
|
||||
m_vibratoFreqModel.value(),
|
||||
p,
|
||||
@@ -315,8 +341,8 @@ void MalletsInstrument::playNote( NotePlayHandle * _n,
|
||||
vel,
|
||||
p,
|
||||
m_lfoDepthModel.value(),
|
||||
m_modulatorModel.value(),
|
||||
m_crossfadeModel.value(),
|
||||
modulator,
|
||||
crossfade,
|
||||
m_lfoSpeedModel.value(),
|
||||
m_adsrModel.value(),
|
||||
(uint8_t) m_spreadModel.value(),
|
||||
@@ -412,6 +438,11 @@ MalletsInstrumentView::MalletsInstrumentView( MalletsInstrument * _instrument,
|
||||
m_spreadKnob->move( 190, 140 );
|
||||
m_spreadKnob->setHintText( tr( "Spread:" ), "" );
|
||||
|
||||
m_randomKnob = new Knob(KnobType::Vintage32, this);
|
||||
m_randomKnob->setLabel(tr("Random"));
|
||||
m_randomKnob->move(190, 190);
|
||||
m_randomKnob->setHintText(tr("Random:"), "");
|
||||
|
||||
// try to inform user about missing Stk-installation
|
||||
if( _instrument->m_filesMissing && getGUI() != nullptr )
|
||||
{
|
||||
@@ -467,7 +498,7 @@ QWidget * MalletsInstrumentView::setupModalBarControls( QWidget * _parent )
|
||||
m_stickKnob->setLabel( tr( "Stick mix" ) );
|
||||
m_stickKnob->move( 190, 90 );
|
||||
m_stickKnob->setHintText( tr( "Stick mix:" ), "" );
|
||||
|
||||
|
||||
return( widget );
|
||||
}
|
||||
|
||||
@@ -565,6 +596,7 @@ void MalletsInstrumentView::modelChanged()
|
||||
// m_strikeLED->setModel( &inst->m_strikeModel );
|
||||
m_presetsCombo->setModel( &inst->m_presetsModel );
|
||||
m_spreadKnob->setModel( &inst->m_spreadModel );
|
||||
m_randomKnob->setModel(&inst->m_randomModel);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -197,6 +197,7 @@ private:
|
||||
|
||||
ComboBoxModel m_presetsModel;
|
||||
FloatModel m_spreadModel;
|
||||
FloatModel m_randomModel;
|
||||
IntModel m_versionModel;
|
||||
BoolModel m_isOldVersionModel;
|
||||
|
||||
@@ -255,6 +256,7 @@ private:
|
||||
|
||||
ComboBox * m_presetsCombo;
|
||||
Knob * m_spreadKnob;
|
||||
Knob * m_randomKnob;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@ set(LMMS_SRCS
|
||||
core/SamplePlayHandle.cpp
|
||||
core/SampleRecordHandle.cpp
|
||||
core/Scale.cpp
|
||||
core/LmmsSemaphore.cpp
|
||||
core/SerializingObject.cpp
|
||||
core/Song.cpp
|
||||
core/TempoSyncKnobModel.cpp
|
||||
@@ -112,6 +113,7 @@ set(LMMS_SRCS
|
||||
core/lv2/Lv2SubPluginFeatures.cpp
|
||||
core/lv2/Lv2UridCache.cpp
|
||||
core/lv2/Lv2UridMap.cpp
|
||||
core/lv2/Lv2Worker.cpp
|
||||
|
||||
core/midi/MidiAlsaRaw.cpp
|
||||
core/midi/MidiAlsaSeq.cpp
|
||||
|
||||
@@ -302,7 +302,7 @@ void DataFile::write( QTextStream & _strm )
|
||||
bool DataFile::writeFile(const QString& filename, bool withResources)
|
||||
{
|
||||
// Small lambda function for displaying errors
|
||||
auto showError = [this](QString title, QString body){
|
||||
auto showError = [](QString title, QString body){
|
||||
if (gui::getGUI() != nullptr)
|
||||
{
|
||||
QMessageBox mb;
|
||||
|
||||
@@ -273,7 +273,9 @@ int DrumSynth::GetDSFileSamples(QString dsfile, int16_t *&wave, int channels, sa
|
||||
//generation
|
||||
long Length, tpos=0, tplus, totmp, t, i, j;
|
||||
float x[3] = {0.f, 0.f, 0.f};
|
||||
float MasterTune, randmax, randmax2;
|
||||
float MasterTune;
|
||||
constexpr float randmax = 1.f / static_cast<float>(RAND_MAX);
|
||||
constexpr float randmax2 = 2.f / static_cast<float>(RAND_MAX);
|
||||
int MainFilter, HighPass;
|
||||
|
||||
long NON, NT, TON, DiON, TDroop=0, DStep;
|
||||
@@ -454,7 +456,6 @@ int DrumSynth::GetDSFileSamples(QString dsfile, int16_t *&wave, int channels, sa
|
||||
}
|
||||
|
||||
//prepare envelopes
|
||||
randmax = 1.f / RAND_MAX; randmax2 = 2.f * randmax;
|
||||
for (i=1;i<8;i++) { envData[i][NEXTT]=0; envData[i][PNT]=0; }
|
||||
Length = LongestEnv();
|
||||
|
||||
@@ -745,4 +746,4 @@ int DrumSynth::GetDSFileSamples(QString dsfile, int16_t *&wave, int channels, sa
|
||||
}
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
} // namespace lmms
|
||||
|
||||
@@ -409,7 +409,7 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
|
||||
// Skip notes randomly
|
||||
if( m_arpSkipModel.value() )
|
||||
{
|
||||
if( 100 * ( (float) rand() / (float)( RAND_MAX + 1.0f ) ) < m_arpSkipModel.value() )
|
||||
if (100 * static_cast<float>(rand()) / (static_cast<float>(RAND_MAX) + 1.0f) < m_arpSkipModel.value())
|
||||
{
|
||||
// update counters
|
||||
frames_processed += arp_frames;
|
||||
@@ -425,7 +425,7 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
|
||||
|
||||
if( m_arpMissModel.value() )
|
||||
{
|
||||
if( 100 * ( (float) rand() / (float)( RAND_MAX + 1.0f ) ) < m_arpMissModel.value() )
|
||||
if (100 * static_cast<float>(rand()) / (static_cast<float>(RAND_MAX) + 1.0f) < m_arpMissModel.value())
|
||||
{
|
||||
dir = ArpDirection::Random;
|
||||
}
|
||||
|
||||
143
src/core/LmmsSemaphore.cpp
Normal file
143
src/core/LmmsSemaphore.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Semaphore.cpp - Semaphore implementation
|
||||
*
|
||||
* Copyright (c) 2022-2022 Johannes Lorenz <jlsf2013$users.sourceforge.net, $=@>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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 (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code has been copied and adapted from https://github.com/drobilla/jalv
|
||||
* File src/zix/sem.h
|
||||
*/
|
||||
|
||||
#include "LmmsSemaphore.h"
|
||||
|
||||
#if defined(LMMS_BUILD_WIN32)
|
||||
# include <limits.h>
|
||||
#else
|
||||
# include <errno.h>
|
||||
#endif
|
||||
|
||||
#include <system_error>
|
||||
|
||||
namespace lmms {
|
||||
|
||||
#ifdef LMMS_BUILD_APPLE
|
||||
Semaphore::Semaphore(unsigned val)
|
||||
{
|
||||
kern_return_t rval = semaphore_create(mach_task_self(), &m_sem, SYNC_POLICY_FIFO, val);
|
||||
if(rval != 0) {
|
||||
throw std::system_error(rval, std::system_category(), "Could not create semaphore");
|
||||
}
|
||||
}
|
||||
|
||||
Semaphore::~Semaphore()
|
||||
{
|
||||
semaphore_destroy(mach_task_self(), m_sem);
|
||||
}
|
||||
|
||||
void Semaphore::post()
|
||||
{
|
||||
semaphore_signal(m_sem);
|
||||
}
|
||||
|
||||
void Semaphore::wait()
|
||||
{
|
||||
kern_return_t rval = semaphore_wait(m_sem);
|
||||
if (rval != KERN_SUCCESS) {
|
||||
throw std::system_error(rval, std::system_category(), "Waiting for semaphore failed");
|
||||
}
|
||||
}
|
||||
|
||||
bool Semaphore::tryWait()
|
||||
{
|
||||
const mach_timespec_t zero = { 0, 0 };
|
||||
return semaphore_timedwait(m_sem, zero) == KERN_SUCCESS;
|
||||
}
|
||||
|
||||
#elif defined(LMMS_BUILD_WIN32)
|
||||
|
||||
Semaphore::Semaphore(unsigned initial)
|
||||
{
|
||||
if(CreateSemaphore(nullptr, initial, LONG_MAX, nullptr) == nullptr) {
|
||||
throw std::system_error(GetLastError(), std::system_category(), "Could not create semaphore");
|
||||
}
|
||||
}
|
||||
|
||||
Semaphore::~Semaphore()
|
||||
{
|
||||
CloseHandle(m_sem);
|
||||
}
|
||||
|
||||
void Semaphore::post()
|
||||
{
|
||||
ReleaseSemaphore(m_sem, 1, nullptr);
|
||||
}
|
||||
|
||||
void Semaphore::wait()
|
||||
{
|
||||
if (WaitForSingleObject(m_sem, INFINITE) != WAIT_OBJECT_0) {
|
||||
throw std::system_error(GetLastError(), std::system_category(), "Waiting for semaphore failed");
|
||||
}
|
||||
}
|
||||
|
||||
bool Semaphore::tryWait()
|
||||
{
|
||||
return WaitForSingleObject(m_sem, 0) == WAIT_OBJECT_0;
|
||||
}
|
||||
|
||||
#else /* !defined(LMMS_BUILD_APPLE) && !defined(LMMS_BUILD_WIN32) */
|
||||
|
||||
Semaphore::Semaphore(unsigned initial)
|
||||
{
|
||||
if(sem_init(&m_sem, 0, initial) != 0) {
|
||||
throw std::system_error(errno, std::generic_category(), "Could not create semaphore");
|
||||
}
|
||||
}
|
||||
|
||||
Semaphore::~Semaphore()
|
||||
{
|
||||
sem_destroy(&m_sem);
|
||||
}
|
||||
|
||||
void Semaphore::post()
|
||||
{
|
||||
sem_post(&m_sem);
|
||||
}
|
||||
|
||||
void Semaphore::wait()
|
||||
{
|
||||
while (sem_wait(&m_sem) != 0) {
|
||||
if (errno != EINTR) {
|
||||
throw std::system_error(errno, std::generic_category(), "Waiting for semaphore failed");
|
||||
}
|
||||
/* Otherwise, interrupted, so try again. */
|
||||
}
|
||||
}
|
||||
|
||||
bool Semaphore::tryWait()
|
||||
{
|
||||
return (sem_trywait(&m_sem) == 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
@@ -1389,7 +1389,7 @@ SampleBuffer * SampleBuffer::resample(const sample_rate_t srcSR, const sample_ra
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Error: src_new() failed in sample_buffer.cpp!\n");
|
||||
printf("Error: src_new() failed in SampleBuffer.cpp!\n");
|
||||
}
|
||||
dstSB->update();
|
||||
return dstSB;
|
||||
@@ -1612,7 +1612,7 @@ SampleBuffer::handleState::handleState(bool varyingPitch, int interpolationMode)
|
||||
|
||||
if ((m_resamplingData = src_new(interpolationMode, DEFAULT_CHANNELS, &error)) == nullptr)
|
||||
{
|
||||
qDebug("Error: src_new() failed in sample_buffer.cpp!\n");
|
||||
qDebug("Error: src_new() failed in SampleBuffer.cpp!\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
|
||||
#ifdef LMMS_HAVE_JACK
|
||||
|
||||
#include <QFormLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "Engine.h"
|
||||
@@ -454,17 +454,16 @@ void AudioJack::shutdownCallback( void * _udata )
|
||||
AudioJack::setupWidget::setupWidget( QWidget * _parent ) :
|
||||
AudioDeviceSetupWidget( AudioJack::name(), _parent )
|
||||
{
|
||||
QFormLayout * form = new QFormLayout(this);
|
||||
|
||||
QString cn = ConfigManager::inst()->value( "audiojack", "clientname" );
|
||||
if( cn.isEmpty() )
|
||||
{
|
||||
cn = "lmms";
|
||||
}
|
||||
m_clientName = new QLineEdit( cn, this );
|
||||
m_clientName->setGeometry( 10, 20, 160, 20 );
|
||||
|
||||
auto cn_lbl = new QLabel(tr("Client name"), this);
|
||||
cn_lbl->setFont( pointSize<7>( cn_lbl->font() ) );
|
||||
cn_lbl->setGeometry( 10, 40, 160, 10 );
|
||||
form->addRow(tr("Client name"), m_clientName);
|
||||
|
||||
auto m = new gui::LcdSpinBoxModel(/* this */);
|
||||
m->setRange( DEFAULT_CHANNELS, SURROUND_CHANNELS );
|
||||
@@ -474,8 +473,8 @@ AudioJack::setupWidget::setupWidget( QWidget * _parent ) :
|
||||
|
||||
m_channels = new gui::LcdSpinBox( 1, this );
|
||||
m_channels->setModel( m );
|
||||
m_channels->setLabel( tr( "Channels" ) );
|
||||
m_channels->move( 180, 20 );
|
||||
|
||||
form->addRow(tr("Channels"), m_channels);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#ifdef LMMS_HAVE_OSS
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QLabel>
|
||||
#include <QFormLayout>
|
||||
#include <QLineEdit>
|
||||
|
||||
#include "endian_handling.h"
|
||||
@@ -320,12 +320,11 @@ void AudioOss::run()
|
||||
AudioOss::setupWidget::setupWidget( QWidget * _parent ) :
|
||||
AudioDeviceSetupWidget( AudioOss::name(), _parent )
|
||||
{
|
||||
m_device = new QLineEdit( probeDevice(), this );
|
||||
m_device->setGeometry( 10, 20, 160, 20 );
|
||||
QFormLayout * form = new QFormLayout(this);
|
||||
|
||||
auto dev_lbl = new QLabel(tr("Device"), this);
|
||||
dev_lbl->setFont( pointSize<7>( dev_lbl->font() ) );
|
||||
dev_lbl->setGeometry( 10, 40, 160, 10 );
|
||||
m_device = new QLineEdit( probeDevice(), this );
|
||||
|
||||
form->addRow(tr("Device"), m_device);
|
||||
|
||||
auto m = new gui::LcdSpinBoxModel(/* this */);
|
||||
m->setRange( DEFAULT_CHANNELS, SURROUND_CHANNELS );
|
||||
@@ -335,9 +334,8 @@ AudioOss::setupWidget::setupWidget( QWidget * _parent ) :
|
||||
|
||||
m_channels = new gui::LcdSpinBox( 1, this );
|
||||
m_channels->setModel( m );
|
||||
m_channels->setLabel( tr( "Channels" ) );
|
||||
m_channels->move( 180, 20 );
|
||||
|
||||
form->addRow(tr("Channels"), m_channels);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ void AudioPortAudioSetupUtil::updateChannels()
|
||||
|
||||
#ifdef LMMS_HAVE_PORTAUDIO
|
||||
|
||||
#include <QLabel>
|
||||
#include <QFormLayout>
|
||||
|
||||
#include "Engine.h"
|
||||
#include "ConfigManager.h"
|
||||
@@ -419,19 +419,13 @@ AudioPortAudio::setupWidget::setupWidget( QWidget * _parent ) :
|
||||
{
|
||||
using gui::ComboBox;
|
||||
|
||||
m_backend = new ComboBox( this, "BACKEND" );
|
||||
m_backend->setGeometry( 64, 15, 260, ComboBox::DEFAULT_HEIGHT );
|
||||
QFormLayout * form = new QFormLayout(this);
|
||||
|
||||
auto backend_lbl = new QLabel(tr("Backend"), this);
|
||||
backend_lbl->setFont( pointSize<7>( backend_lbl->font() ) );
|
||||
backend_lbl->move( 8, 18 );
|
||||
m_backend = new ComboBox( this, "BACKEND" );
|
||||
form->addRow(tr("Backend"), m_backend);
|
||||
|
||||
m_device = new ComboBox( this, "DEVICE" );
|
||||
m_device->setGeometry( 64, 35, 260, ComboBox::DEFAULT_HEIGHT );
|
||||
|
||||
auto dev_lbl = new QLabel(tr("Device"), this);
|
||||
dev_lbl->setFont( pointSize<7>( dev_lbl->font() ) );
|
||||
dev_lbl->move( 8, 38 );
|
||||
form->addRow(tr("Device"), m_device);
|
||||
|
||||
/* LcdSpinBoxModel * m = new LcdSpinBoxModel( );
|
||||
m->setRange( DEFAULT_CHANNELS, SURROUND_CHANNELS );
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <QFormLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QLabel>
|
||||
|
||||
#include "AudioPulseAudio.h"
|
||||
|
||||
@@ -312,24 +312,21 @@ void AudioPulseAudio::signalConnected( bool connected )
|
||||
AudioPulseAudio::setupWidget::setupWidget( QWidget * _parent ) :
|
||||
AudioDeviceSetupWidget( AudioPulseAudio::name(), _parent )
|
||||
{
|
||||
QFormLayout * form = new QFormLayout(this);
|
||||
|
||||
m_device = new QLineEdit( AudioPulseAudio::probeDevice(), this );
|
||||
m_device->setGeometry( 10, 20, 160, 20 );
|
||||
form->addRow(tr("Device"), m_device);
|
||||
|
||||
auto dev_lbl = new QLabel(tr("Device"), this);
|
||||
dev_lbl->setFont( pointSize<7>( dev_lbl->font() ) );
|
||||
dev_lbl->setGeometry( 10, 40, 160, 10 );
|
||||
|
||||
auto m = new gui::LcdSpinBoxModel(/* this */);
|
||||
auto m = new gui::LcdSpinBoxModel();
|
||||
m->setRange( DEFAULT_CHANNELS, SURROUND_CHANNELS );
|
||||
m->setStep( 2 );
|
||||
m->setValue( ConfigManager::inst()->value( "audiopa",
|
||||
"channels" ).toInt() );
|
||||
"channels" ).toInt() );
|
||||
|
||||
m_channels = new gui::LcdSpinBox( 1, this );
|
||||
m_channels->setModel( m );
|
||||
m_channels->setLabel( tr( "Channels" ) );
|
||||
m_channels->move( 180, 20 );
|
||||
|
||||
form->addRow(tr("Channels"), m_channels);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
#ifdef LMMS_HAVE_SDL
|
||||
|
||||
#include <QLabel>
|
||||
#include <QFormLayout>
|
||||
#include <QLineEdit>
|
||||
#include <SDL.h>
|
||||
|
||||
@@ -327,14 +327,12 @@ void AudioSdl::sdlInputAudioCallback(Uint8 *_buf, int _len) {
|
||||
AudioSdl::setupWidget::setupWidget( QWidget * _parent ) :
|
||||
AudioDeviceSetupWidget( AudioSdl::name(), _parent )
|
||||
{
|
||||
QFormLayout * form = new QFormLayout(this);
|
||||
|
||||
QString dev = ConfigManager::inst()->value( "audiosdl", "device" );
|
||||
m_device = new QLineEdit( dev, this );
|
||||
m_device->setGeometry( 10, 20, 160, 20 );
|
||||
|
||||
auto dev_lbl = new QLabel(tr("Device"), this);
|
||||
dev_lbl->setFont( pointSize<7>( dev_lbl->font() ) );
|
||||
dev_lbl->setGeometry( 10, 40, 160, 10 );
|
||||
|
||||
form->addRow(tr("Device"), m_device);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#ifdef LMMS_HAVE_SNDIO
|
||||
|
||||
#include <cstdlib>
|
||||
#include <QLabel>
|
||||
#include <QFormLayout>
|
||||
#include <QLineEdit>
|
||||
|
||||
#include "endian_handling.h"
|
||||
@@ -183,12 +183,10 @@ void AudioSndio::run()
|
||||
AudioSndio::setupWidget::setupWidget( QWidget * _parent ) :
|
||||
AudioDeviceSetupWidget( AudioSndio::name(), _parent )
|
||||
{
|
||||
m_device = new QLineEdit( "", this );
|
||||
m_device->setGeometry( 10, 20, 160, 20 );
|
||||
QFormLayout * form = new QFormLayout(this);
|
||||
|
||||
QLabel * dev_lbl = new QLabel( tr( "Device" ), this );
|
||||
dev_lbl->setFont( pointSize<6>( dev_lbl->font() ) );
|
||||
dev_lbl->setGeometry( 10, 40, 160, 10 );
|
||||
m_device = new QLineEdit( "", this );
|
||||
form->addRow(tr("Device"), m_device);
|
||||
|
||||
gui::LcdSpinBoxModel * m = new gui::LcdSpinBoxModel( /* this */ );
|
||||
m->setRange( DEFAULT_CHANNELS, SURROUND_CHANNELS );
|
||||
@@ -198,9 +196,8 @@ AudioSndio::setupWidget::setupWidget( QWidget * _parent ) :
|
||||
|
||||
m_channels = new gui::LcdSpinBox( 1, this );
|
||||
m_channels->setModel( m );
|
||||
m_channels->setLabel( tr( "Channels" ) );
|
||||
m_channels->move( 180, 20 );
|
||||
|
||||
form->addRow(tr("Channels"), m_channels);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
#ifdef LMMS_HAVE_SOUNDIO
|
||||
|
||||
#include <QLabel>
|
||||
#include <QFormLayout>
|
||||
#include <QLineEdit>
|
||||
|
||||
#include "Engine.h"
|
||||
@@ -451,19 +451,13 @@ AudioSoundIo::setupWidget::setupWidget( QWidget * _parent ) :
|
||||
{
|
||||
m_setupUtil.m_setupWidget = this;
|
||||
|
||||
m_backend = new gui::ComboBox( this, "BACKEND" );
|
||||
m_backend->setGeometry( 64, 15, 260, 20 );
|
||||
QFormLayout * form = new QFormLayout(this);
|
||||
|
||||
QLabel * backend_lbl = new QLabel( tr( "Backend" ), this );
|
||||
backend_lbl->setFont( pointSize<7>( backend_lbl->font() ) );
|
||||
backend_lbl->move( 8, 18 );
|
||||
m_backend = new gui::ComboBox( this, "BACKEND" );
|
||||
form->addRow(tr("Backend"), m_backend);
|
||||
|
||||
m_device = new gui::ComboBox( this, "DEVICE" );
|
||||
m_device->setGeometry( 64, 35, 260, 20 );
|
||||
|
||||
QLabel * dev_lbl = new QLabel( tr( "Device" ), this );
|
||||
dev_lbl->setFont( pointSize<7>( dev_lbl->font() ) );
|
||||
dev_lbl->move( 8, 38 );
|
||||
form->addRow(tr("Device"), m_device);
|
||||
|
||||
// Setup models
|
||||
m_soundio = soundio_create();
|
||||
|
||||
@@ -48,7 +48,7 @@ Lv2Features::Lv2Features()
|
||||
{
|
||||
const Lv2Manager* man = Engine::getLv2Manager();
|
||||
// create (yet empty) map feature URI -> feature
|
||||
for(const char* uri : man->supportedFeatureURIs())
|
||||
for(auto uri : man->supportedFeatureURIs())
|
||||
{
|
||||
m_featureByUri.emplace(uri, nullptr);
|
||||
}
|
||||
@@ -71,7 +71,7 @@ void Lv2Features::initCommon()
|
||||
void Lv2Features::createFeatureVectors()
|
||||
{
|
||||
// create vector of features
|
||||
for(std::pair<const char* const, void*>& pr : m_featureByUri)
|
||||
for(const auto& [uri, feature] : m_featureByUri)
|
||||
{
|
||||
/*
|
||||
If pr.second is nullptr here, this means that the LV2_feature
|
||||
@@ -82,7 +82,7 @@ void Lv2Features::createFeatureVectors()
|
||||
vector creation (This can be done in
|
||||
Lv2Proc::initPluginSpecificFeatures or in Lv2Features::initCommon)
|
||||
*/
|
||||
m_features.push_back(LV2_Feature { pr.first, pr.second });
|
||||
m_features.push_back(LV2_Feature{(const char*)uri.data(), (void*)feature});
|
||||
}
|
||||
|
||||
// create pointer vector (for lilv_plugin_instantiate)
|
||||
|
||||
@@ -28,13 +28,14 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <lilv/lilv.h>
|
||||
#include <lv2/lv2plug.in/ns/ext/buf-size/buf-size.h>
|
||||
#include <lv2/lv2plug.in/ns/ext/options/options.h>
|
||||
#include <lv2/lv2plug.in/ns/ext/worker/worker.h>
|
||||
#include <QDebug>
|
||||
#include <QElapsedTimer>
|
||||
|
||||
#include "AudioEngine.h"
|
||||
#include "Engine.h"
|
||||
#include "Plugin.h"
|
||||
#include "Lv2ControlBase.h"
|
||||
@@ -46,7 +47,7 @@ namespace lmms
|
||||
{
|
||||
|
||||
|
||||
const std::set<const char*, Lv2Manager::CmpStr> Lv2Manager::pluginBlacklist =
|
||||
const std::set<std::string_view> Lv2Manager::pluginBlacklist =
|
||||
{
|
||||
// github.com/calf-studio-gear/calf, #278
|
||||
"http://calf.sourceforge.net/plugins/Analyzer",
|
||||
@@ -137,6 +138,26 @@ const std::set<const char*, Lv2Manager::CmpStr> Lv2Manager::pluginBlacklist =
|
||||
"urn:juced:DrumSynth"
|
||||
};
|
||||
|
||||
const std::set<std::string_view> Lv2Manager::pluginBlacklistBuffersizeLessThan32 =
|
||||
{
|
||||
"http://moddevices.com/plugins/mod-devel/2Voices",
|
||||
"http://moddevices.com/plugins/mod-devel/Capo",
|
||||
"http://moddevices.com/plugins/mod-devel/Drop",
|
||||
"http://moddevices.com/plugins/mod-devel/Harmonizer",
|
||||
"http://moddevices.com/plugins/mod-devel/Harmonizer2",
|
||||
"http://moddevices.com/plugins/mod-devel/HarmonizerCS",
|
||||
"http://moddevices.com/plugins/mod-devel/SuperCapo",
|
||||
"http://moddevices.com/plugins/mod-devel/SuperWhammy",
|
||||
"http://moddevices.com/plugins/mod-devel/Gx2Voices",
|
||||
"http://moddevices.com/plugins/mod-devel/GxCapo",
|
||||
"http://moddevices.com/plugins/mod-devel/GxDrop",
|
||||
"http://moddevices.com/plugins/mod-devel/GxHarmonizer",
|
||||
"http://moddevices.com/plugins/mod-devel/GxHarmonizer2",
|
||||
"http://moddevices.com/plugins/mod-devel/GxHarmonizerCS",
|
||||
"http://moddevices.com/plugins/mod-devel/GxSuperCapo",
|
||||
"http://moddevices.com/plugins/mod-devel/GxSuperWhammy"
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -152,10 +173,15 @@ Lv2Manager::Lv2Manager() :
|
||||
m_supportedFeatureURIs.insert(LV2_URID__map);
|
||||
m_supportedFeatureURIs.insert(LV2_URID__unmap);
|
||||
m_supportedFeatureURIs.insert(LV2_OPTIONS__options);
|
||||
m_supportedFeatureURIs.insert(LV2_WORKER__schedule);
|
||||
// min/max is always passed in the options
|
||||
m_supportedFeatureURIs.insert(LV2_BUF_SIZE__boundedBlockLength);
|
||||
// block length is only changed initially in AudioEngine CTOR
|
||||
m_supportedFeatureURIs.insert(LV2_BUF_SIZE__fixedBlockLength);
|
||||
if (const auto fpp = Engine::audioEngine()->framesPerPeriod(); (fpp & (fpp - 1)) == 0) // <=> ffp is power of 2 (for ffp > 0)
|
||||
{
|
||||
m_supportedFeatureURIs.insert(LV2_BUF_SIZE__powerOf2BlockLength);
|
||||
}
|
||||
|
||||
auto supportOpt = [this](Lv2UridCache::Id id)
|
||||
{
|
||||
@@ -288,14 +314,6 @@ void Lv2Manager::initPlugins()
|
||||
|
||||
|
||||
|
||||
bool Lv2Manager::CmpStr::operator()(const char *a, const char *b) const
|
||||
{
|
||||
return std::strcmp(a, b) < 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool Lv2Manager::isFeatureSupported(const char *featName) const
|
||||
{
|
||||
return m_supportedFeatureURIs.find(featName) != m_supportedFeatureURIs.end();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Lv2Proc.cpp - Lv2 processor class
|
||||
*
|
||||
* Copyright (c) 2019-2020 Johannes Lorenz <jlsf2013$users.sourceforge.net, $=@>
|
||||
* Copyright (c) 2019-2022 Johannes Lorenz <jlsf2013$users.sourceforge.net, $=@>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <lv2/lv2plug.in/ns/ext/midi/midi.h>
|
||||
#include <lv2/lv2plug.in/ns/ext/atom/atom.h>
|
||||
#include <lv2/lv2plug.in/ns/ext/resize-port/resize-port.h>
|
||||
#include <lv2/lv2plug.in/ns/ext/worker/worker.h>
|
||||
#include <QDebug>
|
||||
#include <QDomDocument>
|
||||
#include <QtGlobal>
|
||||
@@ -75,11 +76,19 @@ Plugin::Type Lv2Proc::check(const LilvPlugin *plugin,
|
||||
// TODO: manage a global blacklist outside of the code
|
||||
// for now, this will help
|
||||
// this is only a fix for the meantime
|
||||
const auto& pluginBlacklist = Lv2Manager::getPluginBlacklist();
|
||||
if (!Engine::ignorePluginBlacklist() &&
|
||||
pluginBlacklist.find(pluginUri) != pluginBlacklist.end())
|
||||
if (!Engine::ignorePluginBlacklist())
|
||||
{
|
||||
issues.emplace_back(PluginIssueType::Blacklisted);
|
||||
const auto& pluginBlacklist = Lv2Manager::getPluginBlacklist();
|
||||
const auto& pluginBlacklist32 = Lv2Manager::getPluginBlacklistBuffersizeLessThan32();
|
||||
if(pluginBlacklist.find(pluginUri) != pluginBlacklist.end())
|
||||
{
|
||||
issues.emplace_back(PluginIssueType::Blacklisted);
|
||||
}
|
||||
else if(Engine::audioEngine()->framesPerPeriod() <= 32 &&
|
||||
pluginBlacklist32.find(pluginUri) != pluginBlacklist32.end())
|
||||
{
|
||||
issues.emplace_back(PluginIssueType::Blacklisted); // currently no special blacklist category
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned portNum = 0; portNum < maxPorts; ++portNum)
|
||||
@@ -162,6 +171,7 @@ Plugin::Type Lv2Proc::check(const LilvPlugin *plugin,
|
||||
Lv2Proc::Lv2Proc(const LilvPlugin *plugin, Model* parent) :
|
||||
LinkedModelGroup(parent),
|
||||
m_plugin(plugin),
|
||||
m_workLock(1),
|
||||
m_midiInputBuf(m_maxMidiInputEvents),
|
||||
m_midiInputReader(m_midiInputBuf)
|
||||
{
|
||||
@@ -352,7 +362,19 @@ void Lv2Proc::copyBuffersToCore(sampleFrame* buf,
|
||||
|
||||
void Lv2Proc::run(fpp_t frames)
|
||||
{
|
||||
if (m_worker)
|
||||
{
|
||||
// Process any worker replies
|
||||
m_worker->emitResponses();
|
||||
}
|
||||
|
||||
lilv_instance_run(m_instance, static_cast<uint32_t>(frames));
|
||||
|
||||
if (m_worker)
|
||||
{
|
||||
// Notify the plugin the run() cycle is finished
|
||||
m_worker->notifyPluginThatRunFinished();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -420,6 +442,9 @@ void Lv2Proc::initPlugin()
|
||||
|
||||
if (m_instance)
|
||||
{
|
||||
if(m_worker) {
|
||||
m_worker->setHandle(lilv_instance_get_handle(m_instance));
|
||||
}
|
||||
for (std::size_t portNum = 0; portNum < m_ports.size(); ++portNum)
|
||||
connectPort(portNum);
|
||||
lilv_instance_activate(m_instance);
|
||||
@@ -496,8 +521,20 @@ void Lv2Proc::initMOptions()
|
||||
|
||||
void Lv2Proc::initPluginSpecificFeatures()
|
||||
{
|
||||
// options
|
||||
initMOptions();
|
||||
m_features[LV2_OPTIONS__options] = const_cast<LV2_Options_Option*>(m_options.feature());
|
||||
|
||||
// worker (if plugin has worker extension)
|
||||
Lv2Manager* mgr = Engine::getLv2Manager();
|
||||
if (lilv_plugin_has_extension_data(m_plugin, mgr->uri(LV2_WORKER__interface).get())) {
|
||||
const auto iface = static_cast<const LV2_Worker_Interface*>(
|
||||
lilv_instance_get_extension_data(m_instance, LV2_WORKER__interface));
|
||||
bool threaded = !Engine::audioEngine()->renderOnly();
|
||||
m_worker.emplace(iface, &m_workLock, threaded);
|
||||
m_features[LV2_WORKER__schedule] = m_worker->feature();
|
||||
// Note: m_worker::setHandle will still need to be called later
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -566,21 +603,35 @@ void Lv2Proc::createPort(std::size_t portNum)
|
||||
break;
|
||||
case Lv2Ports::Vis::Enumeration:
|
||||
{
|
||||
auto comboModel = new ComboBoxModel(nullptr, dispName);
|
||||
LilvScalePoints* sps =
|
||||
lilv_port_get_scale_points(m_plugin, lilvPort);
|
||||
LILV_FOREACH(scale_points, i, sps)
|
||||
ComboBoxModel* comboModel = new ComboBoxModel(nullptr, dispName);
|
||||
|
||||
{
|
||||
const LilvScalePoint* sp = lilv_scale_points_get(sps, i);
|
||||
ctrl->m_scalePointMap.push_back(lilv_node_as_float(
|
||||
lilv_scale_point_get_value(sp)));
|
||||
comboModel->addItem(
|
||||
lilv_node_as_string(
|
||||
lilv_scale_point_get_label(sp)));
|
||||
AutoLilvScalePoints sps (static_cast<LilvScalePoints*>(lilv_port_get_scale_points(m_plugin, lilvPort)));
|
||||
// temporary map, since lilv may return scale points in random order
|
||||
std::map<float, const char*> scalePointMap;
|
||||
LILV_FOREACH(scale_points, i, sps.get())
|
||||
{
|
||||
const LilvScalePoint* sp = lilv_scale_points_get(sps.get(), i);
|
||||
const float f = lilv_node_as_float(lilv_scale_point_get_value(sp));
|
||||
const char* s = lilv_node_as_string(lilv_scale_point_get_label(sp));
|
||||
scalePointMap[f] = s;
|
||||
}
|
||||
for (const auto& [f,s] : scalePointMap)
|
||||
{
|
||||
ctrl->m_scalePointMap.push_back(f);
|
||||
comboModel->addItem(s);
|
||||
}
|
||||
}
|
||||
for(std::size_t i = 0; i < ctrl->m_scalePointMap.size(); ++i)
|
||||
{
|
||||
if(meta.def() == ctrl->m_scalePointMap[i])
|
||||
{
|
||||
comboModel->setValue(i);
|
||||
comboModel->setInitValue(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
lilv_scale_points_free(sps);
|
||||
ctrl->m_connectedModel.reset(comboModel);
|
||||
// TODO: use default value on comboModel, too?
|
||||
break;
|
||||
}
|
||||
case Lv2Ports::Vis::Toggled:
|
||||
|
||||
203
src/core/lv2/Lv2Worker.cpp
Normal file
203
src/core/lv2/Lv2Worker.cpp
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Lv2Worker.cpp - Lv2Worker implementation
|
||||
*
|
||||
* Copyright (c) 2022-2022 Johannes Lorenz <jlsf2013$users.sourceforge.net, $=@>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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 (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Lv2Worker.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <QDebug>
|
||||
|
||||
#ifdef LMMS_HAVE_LV2
|
||||
|
||||
#include "Engine.h"
|
||||
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
|
||||
// static wrappers
|
||||
|
||||
static LV2_Worker_Status
|
||||
staticWorkerRespond(LV2_Worker_Respond_Handle handle,
|
||||
uint32_t size, const void* data)
|
||||
{
|
||||
Lv2Worker* worker = static_cast<Lv2Worker*>(handle);
|
||||
return worker->respond(size, data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
std::size_t Lv2Worker::bufferSize() const
|
||||
{
|
||||
// ardour uses this fixed size for ALSA:
|
||||
return 8192 * 4;
|
||||
// for jack, they use 4 * jack_port_type_get_buffer_size (..., JACK_DEFAULT_MIDI_TYPE)
|
||||
// (possible extension for AudioDevice)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Lv2Worker::Lv2Worker(const LV2_Worker_Interface* iface,
|
||||
Semaphore* common_work_lock,
|
||||
bool threaded) :
|
||||
m_iface(iface),
|
||||
m_threaded(threaded),
|
||||
m_response(bufferSize()),
|
||||
m_requests(bufferSize()),
|
||||
m_responses(bufferSize()),
|
||||
m_requestsReader(m_requests),
|
||||
m_responsesReader(m_responses),
|
||||
m_sem(0),
|
||||
m_workLock(common_work_lock)
|
||||
{
|
||||
assert(iface);
|
||||
m_scheduleFeature.handle = static_cast<LV2_Worker_Schedule_Handle>(this);
|
||||
m_scheduleFeature.schedule_work = [](LV2_Worker_Schedule_Handle handle,
|
||||
uint32_t size, const void* data) -> LV2_Worker_Status
|
||||
{
|
||||
Lv2Worker* worker = static_cast<Lv2Worker*>(handle);
|
||||
return worker->scheduleWork(size, data);
|
||||
};
|
||||
|
||||
if (threaded) { m_thread = std::thread(&Lv2Worker::workerFunc, this); }
|
||||
|
||||
m_requests.mlock();
|
||||
m_responses.mlock();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Lv2Worker::~Lv2Worker()
|
||||
{
|
||||
m_exit = true;
|
||||
if(m_threaded) {
|
||||
m_sem.post();
|
||||
m_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Let the worker send responses to the audio thread
|
||||
LV2_Worker_Status Lv2Worker::respond(uint32_t size, const void* data)
|
||||
{
|
||||
if(m_threaded)
|
||||
{
|
||||
if(m_responses.free() < sizeof(size) + size)
|
||||
{
|
||||
return LV2_WORKER_ERR_NO_SPACE;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_responses.write((const char*)&size, sizeof(size));
|
||||
if(size && data) { m_responses.write((const char*)data, size); }
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_iface->work_response(m_handle, size, data);
|
||||
}
|
||||
return LV2_WORKER_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Let the worker receive work from the audio thread and "work" on it
|
||||
void Lv2Worker::workerFunc()
|
||||
{
|
||||
std::vector<char> buf;
|
||||
uint32_t size;
|
||||
while (true) {
|
||||
m_sem.wait();
|
||||
if (m_exit) { break; }
|
||||
const std::size_t readSpace = m_requestsReader.read_space();
|
||||
if (readSpace <= sizeof(size)) { continue; } // (should not happen)
|
||||
|
||||
m_requestsReader.read(sizeof(size)).copy((char*)&size, sizeof(size));
|
||||
assert(size <= readSpace - sizeof(size));
|
||||
if(size > buf.size()) { buf.resize(size); }
|
||||
if(size) { m_requestsReader.read(size).copy(buf.data(), size); }
|
||||
|
||||
m_workLock->wait();
|
||||
m_iface->work(m_handle, staticWorkerRespond, this, size, buf.data());
|
||||
m_workLock->post();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Let the audio thread schedule work for the worker
|
||||
LV2_Worker_Status Lv2Worker::scheduleWork(uint32_t size, const void *data)
|
||||
{
|
||||
if (m_threaded)
|
||||
{
|
||||
if(m_requests.free() < sizeof(size) + size)
|
||||
{
|
||||
return LV2_WORKER_ERR_NO_SPACE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Schedule a request to be executed by the worker thread
|
||||
m_requests.write((const char*)&size, sizeof(size));
|
||||
if(size && data) { m_requests.write((const char*)data, size); }
|
||||
m_sem.post();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Execute work immediately in this thread
|
||||
m_workLock->wait();
|
||||
m_iface->work(m_handle, staticWorkerRespond, this, size, data);
|
||||
m_workLock->post();
|
||||
}
|
||||
|
||||
return LV2_WORKER_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Let the audio thread read incoming worker responses, and process it
|
||||
void Lv2Worker::emitResponses()
|
||||
{
|
||||
std::size_t read_space = m_responsesReader.read_space();
|
||||
uint32_t size;
|
||||
while (read_space > sizeof(size)) {
|
||||
m_responsesReader.read(sizeof(size)).copy((char*)&size, sizeof(size));
|
||||
if(size) { m_responsesReader.read(size).copy(m_response.data(), size); }
|
||||
m_iface->work_response(m_handle, size, m_response.data());
|
||||
read_space -= sizeof(size) + size;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif // LMMS_HAVE_LV2
|
||||
@@ -23,7 +23,7 @@
|
||||
*/
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QLabel>
|
||||
#include <QFormLayout>
|
||||
|
||||
#include "AudioAlsaSetupWidget.h"
|
||||
|
||||
@@ -40,6 +40,8 @@ AudioAlsaSetupWidget::AudioAlsaSetupWidget( QWidget * _parent ) :
|
||||
AudioDeviceSetupWidget( AudioAlsa::name(), _parent ),
|
||||
m_selectedDevice(-1)
|
||||
{
|
||||
QFormLayout * form = new QFormLayout(this);
|
||||
|
||||
m_deviceInfos = AudioAlsa::getAvailableDevices();
|
||||
|
||||
QString deviceText = ConfigManager::inst()->value( "audioalsa", "device" );
|
||||
@@ -62,14 +64,11 @@ AudioAlsaSetupWidget::AudioAlsaSetupWidget( QWidget * _parent ) :
|
||||
|
||||
m_selectedDevice = m_deviceComboBox->currentIndex();
|
||||
|
||||
m_deviceComboBox->setGeometry( 10, 20, 160, 20 );
|
||||
connect(m_deviceComboBox,
|
||||
SIGNAL(currentIndexChanged(int)),
|
||||
SLOT(onCurrentIndexChanged(int)));
|
||||
|
||||
auto dev_lbl = new QLabel(tr("DEVICE"), this);
|
||||
dev_lbl->setFont( pointSize<7>( dev_lbl->font() ) );
|
||||
dev_lbl->setGeometry( 10, 40, 160, 10 );
|
||||
form->addRow(tr("Device"), m_deviceComboBox);
|
||||
|
||||
auto m = new LcdSpinBoxModel(/* this */);
|
||||
m->setRange( DEFAULT_CHANNELS, SURROUND_CHANNELS );
|
||||
@@ -79,9 +78,8 @@ AudioAlsaSetupWidget::AudioAlsaSetupWidget( QWidget * _parent ) :
|
||||
|
||||
m_channels = new LcdSpinBox( 1, this );
|
||||
m_channels->setModel( m );
|
||||
m_channels->setLabel( tr( "CHANNELS" ) );
|
||||
m_channels->move( 180, 20 );
|
||||
|
||||
form->addRow(tr("Channels"), m_channels);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace lmms::gui
|
||||
{
|
||||
|
||||
AudioDeviceSetupWidget::AudioDeviceSetupWidget(const QString & caption, QWidget * parent) :
|
||||
TabWidget(TabWidget::tr("Settings for %1").arg(tr(caption.toUtf8())), parent)
|
||||
QGroupBox(QGroupBox::tr("Settings for %1").arg(tr(caption.toUtf8())), parent)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -38,4 +38,4 @@ void AudioDeviceSetupWidget::show()
|
||||
QWidget::show();
|
||||
}
|
||||
|
||||
} // namespace lmms::gui
|
||||
} // namespace lmms::gui
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "GuiApplication.h"
|
||||
#include "embed.h"
|
||||
#include "gui_templates.h"
|
||||
#include "lmms_math.h"
|
||||
#include "Lv2ControlBase.h"
|
||||
#include "Lv2Manager.h"
|
||||
#include "Lv2Proc.h"
|
||||
@@ -51,13 +52,13 @@ namespace lmms::gui
|
||||
{
|
||||
|
||||
|
||||
Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* ctrlBase, int colNum) :
|
||||
LinkedModelGroupView (parent, ctrlBase, colNum)
|
||||
Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* proc, int colNum) :
|
||||
LinkedModelGroupView (parent, proc, colNum)
|
||||
{
|
||||
class SetupWidget : public Lv2Ports::ConstVisitor
|
||||
class SetupTheWidget : public Lv2Ports::ConstVisitor
|
||||
{
|
||||
public:
|
||||
QWidget* m_par; // input
|
||||
QWidget* m_parent; // input
|
||||
const LilvNode* m_commentUri; // input
|
||||
Control* m_control = nullptr; // output
|
||||
void visit(const Lv2Ports::Control& port) override
|
||||
@@ -69,20 +70,22 @@ Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* ctrlBase, int colNum) :
|
||||
switch (port.m_vis)
|
||||
{
|
||||
case PortVis::Generic:
|
||||
m_control = new KnobControl(m_par);
|
||||
m_control = new KnobControl(m_parent);
|
||||
break;
|
||||
case PortVis::Integer:
|
||||
{
|
||||
sample_rate_t sr = Engine::audioEngine()->processingSampleRate();
|
||||
m_control = new LcdControl((port.max(sr) <= 9.0f) ? 1 : 2,
|
||||
m_par);
|
||||
auto pMin = port.min(sr);
|
||||
auto pMax = port.max(sr);
|
||||
int numDigits = std::max(numDigitsAsInt(pMin), numDigitsAsInt(pMax));
|
||||
m_control = new LcdControl(numDigits, m_parent);
|
||||
break;
|
||||
}
|
||||
case PortVis::Enumeration:
|
||||
m_control = new ComboControl(m_par);
|
||||
m_control = new ComboControl(m_parent);
|
||||
break;
|
||||
case PortVis::Toggled:
|
||||
m_control = new CheckControl(m_par);
|
||||
m_control = new CheckControl(m_parent);
|
||||
break;
|
||||
}
|
||||
m_control->setText(port.name());
|
||||
@@ -100,14 +103,14 @@ Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* ctrlBase, int colNum) :
|
||||
};
|
||||
|
||||
AutoLilvNode commentUri = uri(LILV_NS_RDFS "comment");
|
||||
ctrlBase->foreach_port(
|
||||
proc->foreach_port(
|
||||
[this, &commentUri](const Lv2Ports::PortBase* port)
|
||||
{
|
||||
if(!lilv_port_has_property(port->m_plugin, port->m_port,
|
||||
uri(LV2_PORT_PROPS__notOnGUI).get()))
|
||||
{
|
||||
SetupWidget setup;
|
||||
setup.m_par = this;
|
||||
SetupTheWidget setup;
|
||||
setup.m_parent = this;
|
||||
setup.m_commentUri = commentUri.get();
|
||||
port->accept(setup);
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
#include "MidiSetupWidget.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QFormLayout>
|
||||
#include <QLineEdit>
|
||||
|
||||
#include "ConfigManager.h"
|
||||
@@ -37,7 +37,7 @@ namespace lmms::gui
|
||||
|
||||
MidiSetupWidget::MidiSetupWidget(const QString & caption, const QString & configSection,
|
||||
const QString & devName, QWidget * parent) :
|
||||
TabWidget(TabWidget::tr("Settings for %1").arg(tr(caption.toUtf8())), parent),
|
||||
QGroupBox(QGroupBox::tr("Settings for %1").arg(tr(caption.toUtf8())), parent),
|
||||
m_configSection(configSection),
|
||||
m_device(nullptr)
|
||||
{
|
||||
@@ -45,12 +45,11 @@ MidiSetupWidget::MidiSetupWidget(const QString & caption, const QString & config
|
||||
// to indicate that there is no editable device field
|
||||
if (!devName.isNull())
|
||||
{
|
||||
m_device = new QLineEdit(devName, this);
|
||||
m_device->setGeometry(10, 20, 160, 20);
|
||||
QFormLayout * form = new QFormLayout(this);
|
||||
|
||||
auto dev_lbl = new QLabel(tr("Device"), this);
|
||||
dev_lbl->setFont(pointSize<7>(dev_lbl->font()));
|
||||
dev_lbl->setGeometry(10, 40, 160, 10);
|
||||
m_device = new QLineEdit(devName, this);
|
||||
|
||||
form->addRow(tr("Device"), m_device);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -464,7 +464,7 @@ bool MixerView::confirmRemoval(int index)
|
||||
QString messageTitleRemoveTrack = tr("Confirm removal");
|
||||
QString askAgainText = tr("Don't ask again");
|
||||
auto askAgainCheckBox = new QCheckBox(askAgainText, nullptr);
|
||||
connect(askAgainCheckBox, &QCheckBox::stateChanged, [this](int state) {
|
||||
connect(askAgainCheckBox, &QCheckBox::stateChanged, [](int state) {
|
||||
// Invert button state, if it's checked we *shouldn't* ask again
|
||||
ConfigManager::inst()->setValue("ui", "mixerchanneldeletionwarning", state ? "0" : "1");
|
||||
});
|
||||
|
||||
@@ -23,14 +23,15 @@
|
||||
*/
|
||||
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QGroupBox>
|
||||
#include <QImageReader>
|
||||
#include <QLabel>
|
||||
#include <QLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QScrollArea>
|
||||
|
||||
#include "AudioDeviceSetupWidget.h"
|
||||
#include "AudioEngine.h"
|
||||
#include "debug.h"
|
||||
#include "embed.h"
|
||||
@@ -79,13 +80,12 @@ inline void labelWidget(QWidget * w, const QString & txt)
|
||||
auto title = new QLabel(txt, w);
|
||||
QFont f = title->font();
|
||||
f.setBold(true);
|
||||
title->setFont(pointSize<12>(f));
|
||||
title->setFont(f);
|
||||
|
||||
QBoxLayout * boxLayout = dynamic_cast<QBoxLayout *>(w->layout());
|
||||
assert(boxLayout);
|
||||
|
||||
assert(dynamic_cast<QBoxLayout *>(w->layout()) != nullptr);
|
||||
|
||||
dynamic_cast<QBoxLayout *>(w->layout())->addSpacing(5);
|
||||
dynamic_cast<QBoxLayout *>(w->layout())->addWidget(title);
|
||||
boxLayout->addWidget(title);
|
||||
}
|
||||
|
||||
|
||||
@@ -162,15 +162,10 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) :
|
||||
// TODO: Equivalent to the new setWindowFlag(Qt::WindowContextHelpButtonHint, false)
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setModal(true);
|
||||
setFixedSize(454, 400);
|
||||
|
||||
Engine::projectJournal()->setJournalling(false);
|
||||
|
||||
|
||||
// Constants for positioning LED check boxes.
|
||||
const int XDelta = 10;
|
||||
const int YDelta = 18;
|
||||
|
||||
// Main widget.
|
||||
auto main_w = new QWidget(this);
|
||||
|
||||
@@ -191,7 +186,8 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) :
|
||||
|
||||
// Settings widget.
|
||||
auto settings_w = new QWidget(main_w);
|
||||
settings_w->setFixedSize(360, 360);
|
||||
|
||||
QVBoxLayout * settingsLayout = new QVBoxLayout(settings_w);
|
||||
|
||||
// General widget.
|
||||
auto general_w = new QWidget(settings_w);
|
||||
@@ -211,77 +207,79 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) :
|
||||
// Path selectors layout.
|
||||
auto generalControlsLayout = new QVBoxLayout;
|
||||
generalControlsLayout->setSpacing(10);
|
||||
generalControlsLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
auto addLedCheckBox = [&XDelta, &YDelta, this](const QString& ledText, TabWidget* tw, int& counter,
|
||||
bool initialState, const char* toggledSlot, bool showRestartWarning) {
|
||||
auto checkBox = new LedCheckBox(ledText, tw);
|
||||
counter++;
|
||||
checkBox->move(XDelta, YDelta * counter);
|
||||
auto addCheckBox = [&](const QString& ledText, QWidget* parent, QBoxLayout * layout,
|
||||
bool initialState, const char* toggledSlot, bool showRestartWarning) -> QCheckBox * {
|
||||
auto checkBox = new QCheckBox(ledText, parent);
|
||||
checkBox->setChecked(initialState);
|
||||
connect(checkBox, SIGNAL(toggled(bool)), this, toggledSlot);
|
||||
|
||||
if (showRestartWarning)
|
||||
{
|
||||
connect(checkBox, SIGNAL(toggled(bool)), this, SLOT(showRestartWarning()));
|
||||
}
|
||||
|
||||
if (layout)
|
||||
{
|
||||
layout->addWidget(checkBox);
|
||||
}
|
||||
|
||||
return checkBox;
|
||||
};
|
||||
|
||||
int counter = 0;
|
||||
|
||||
// GUI tab.
|
||||
auto gui_tw = new TabWidget(tr("Graphical user interface (GUI)"), generalControls);
|
||||
QGroupBox * guiGroupBox = new QGroupBox(tr("Graphical user interface (GUI)"), generalControls);
|
||||
QVBoxLayout * guiGroupLayout = new QVBoxLayout(guiGroupBox);
|
||||
|
||||
addLedCheckBox(tr("Display volume as dBFS "), gui_tw, counter,
|
||||
addCheckBox(tr("Display volume as dBFS "), guiGroupBox, guiGroupLayout,
|
||||
m_displaydBFS, SLOT(toggleDisplaydBFS(bool)), true);
|
||||
addLedCheckBox(tr("Enable tooltips"), gui_tw, counter,
|
||||
addCheckBox(tr("Enable tooltips"), guiGroupBox, guiGroupLayout,
|
||||
m_tooltips, SLOT(toggleTooltips(bool)), true);
|
||||
addLedCheckBox(tr("Enable master oscilloscope by default"), gui_tw, counter,
|
||||
addCheckBox(tr("Enable master oscilloscope by default"), guiGroupBox, guiGroupLayout,
|
||||
m_displayWaveform, SLOT(toggleDisplayWaveform(bool)), true);
|
||||
addLedCheckBox(tr("Enable all note labels in piano roll"), gui_tw, counter,
|
||||
addCheckBox(tr("Enable all note labels in piano roll"), guiGroupBox, guiGroupLayout,
|
||||
m_printNoteLabels, SLOT(toggleNoteLabels(bool)), false);
|
||||
addLedCheckBox(tr("Enable compact track buttons"), gui_tw, counter,
|
||||
addCheckBox(tr("Enable compact track buttons"), guiGroupBox, guiGroupLayout,
|
||||
m_compactTrackButtons, SLOT(toggleCompactTrackButtons(bool)), true);
|
||||
addLedCheckBox(tr("Enable one instrument-track-window mode"), gui_tw, counter,
|
||||
addCheckBox(tr("Enable one instrument-track-window mode"), guiGroupBox, guiGroupLayout,
|
||||
m_oneInstrumentTrackWindow, SLOT(toggleOneInstrumentTrackWindow(bool)), true);
|
||||
addLedCheckBox(tr("Show sidebar on the right-hand side"), gui_tw, counter,
|
||||
addCheckBox(tr("Show sidebar on the right-hand side"), guiGroupBox, guiGroupLayout,
|
||||
m_sideBarOnRight, SLOT(toggleSideBarOnRight(bool)), true);
|
||||
addLedCheckBox(tr("Let sample previews continue when mouse is released"), gui_tw, counter,
|
||||
addCheckBox(tr("Let sample previews continue when mouse is released"), guiGroupBox, guiGroupLayout,
|
||||
m_letPreviewsFinish, SLOT(toggleLetPreviewsFinish(bool)), false);
|
||||
addLedCheckBox(tr("Mute automation tracks during solo"), gui_tw, counter,
|
||||
addCheckBox(tr("Mute automation tracks during solo"), guiGroupBox, guiGroupLayout,
|
||||
m_soloLegacyBehavior, SLOT(toggleSoloLegacyBehavior(bool)), false);
|
||||
addLedCheckBox(tr("Show warning when deleting tracks"), gui_tw, counter,
|
||||
addCheckBox(tr("Show warning when deleting tracks"), guiGroupBox, guiGroupLayout,
|
||||
m_trackDeletionWarning, SLOT(toggleTrackDeletionWarning(bool)), false);
|
||||
addLedCheckBox(tr("Show warning when deleting a mixer channel that is in use"), gui_tw, counter,
|
||||
addCheckBox(tr("Show warning when deleting a mixer channel that is in use"), guiGroupBox, guiGroupLayout,
|
||||
m_mixerChannelDeletionWarning, SLOT(toggleMixerChannelDeletionWarning(bool)), false);
|
||||
|
||||
gui_tw->setFixedHeight(YDelta + YDelta * counter);
|
||||
generalControlsLayout->addWidget(guiGroupBox);
|
||||
|
||||
generalControlsLayout->addWidget(gui_tw);
|
||||
generalControlsLayout->addSpacing(10);
|
||||
|
||||
|
||||
counter = 0;
|
||||
|
||||
// Projects tab.
|
||||
auto projects_tw = new TabWidget(tr("Projects"), generalControls);
|
||||
QGroupBox * projectsGroupBox = new QGroupBox(tr("Projects"), generalControls);
|
||||
QVBoxLayout * projectsGroupLayout = new QVBoxLayout(projectsGroupBox);
|
||||
|
||||
addLedCheckBox(tr("Compress project files by default"), projects_tw, counter,
|
||||
addCheckBox(tr("Compress project files by default"), projectsGroupBox, projectsGroupLayout,
|
||||
m_MMPZ, SLOT(toggleMMPZ(bool)), true);
|
||||
addLedCheckBox(tr("Create a backup file when saving a project"), projects_tw, counter,
|
||||
addCheckBox(tr("Create a backup file when saving a project"), projectsGroupBox, projectsGroupLayout,
|
||||
m_disableBackup, SLOT(toggleDisableBackup(bool)), false);
|
||||
addLedCheckBox(tr("Reopen last project on startup"), projects_tw, counter,
|
||||
addCheckBox(tr("Reopen last project on startup"), projectsGroupBox, projectsGroupLayout,
|
||||
m_openLastProject, SLOT(toggleOpenLastProject(bool)), false);
|
||||
|
||||
projects_tw->setFixedHeight(YDelta + YDelta * counter);
|
||||
generalControlsLayout->addWidget(projectsGroupBox);
|
||||
|
||||
generalControlsLayout->addWidget(projects_tw);
|
||||
generalControlsLayout->addSpacing(10);
|
||||
|
||||
|
||||
// Language tab.
|
||||
auto lang_tw = new TabWidget(tr("Language"), generalControls);
|
||||
lang_tw->setFixedHeight(48);
|
||||
auto changeLang = new QComboBox(lang_tw);
|
||||
changeLang->move(XDelta, 20);
|
||||
QGroupBox * languageGroupBox = new QGroupBox(tr("Language"), generalControls);
|
||||
QVBoxLayout * languageGroupLayout = new QVBoxLayout(languageGroupBox);
|
||||
|
||||
auto changeLang = new QComboBox(languageGroupBox);
|
||||
languageGroupLayout->addWidget(changeLang);
|
||||
|
||||
QDir dir(ConfigManager::inst()->localeDir());
|
||||
QStringList fileNames = dir.entryList(QStringList("*.qm"));
|
||||
@@ -333,7 +331,7 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) :
|
||||
connect(changeLang, SIGNAL(currentIndexChanged(int)),
|
||||
this, SLOT(showRestartWarning()));
|
||||
|
||||
generalControlsLayout->addWidget(lang_tw);
|
||||
generalControlsLayout->addWidget(languageGroupBox);
|
||||
generalControlsLayout->addSpacing(10);
|
||||
|
||||
// General layout ordering.
|
||||
@@ -341,9 +339,7 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) :
|
||||
generalControls->setLayout(generalControlsLayout);
|
||||
generalScroll->setWidget(generalControls);
|
||||
generalScroll->setWidgetResizable(true);
|
||||
general_layout->addWidget(generalScroll);
|
||||
general_layout->addStretch();
|
||||
|
||||
general_layout->addWidget(generalScroll, 1);
|
||||
|
||||
|
||||
|
||||
@@ -357,71 +353,63 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) :
|
||||
|
||||
|
||||
// Autosave tab.
|
||||
auto auto_save_tw = new TabWidget(tr("Autosave"), performance_w);
|
||||
auto_save_tw->setFixedHeight(106);
|
||||
QGroupBox * autoSaveBox = new QGroupBox(tr("Autosave"), performance_w);
|
||||
QVBoxLayout * autoSaveLayout = new QVBoxLayout(autoSaveBox);
|
||||
QHBoxLayout * autoSaveSubLayout = new QHBoxLayout();
|
||||
|
||||
m_saveIntervalSlider = new QSlider(Qt::Horizontal, auto_save_tw);
|
||||
m_saveIntervalSlider = new QSlider(Qt::Horizontal, autoSaveBox);
|
||||
m_saveIntervalSlider->setValue(m_saveInterval);
|
||||
m_saveIntervalSlider->setRange(1, 20);
|
||||
m_saveIntervalSlider->setTickInterval(1);
|
||||
m_saveIntervalSlider->setPageStep(1);
|
||||
m_saveIntervalSlider->setGeometry(10, 18, 340, 18);
|
||||
m_saveIntervalSlider->setTickPosition(QSlider::TicksBelow);
|
||||
|
||||
connect(m_saveIntervalSlider, SIGNAL(valueChanged(int)),
|
||||
this, SLOT(setAutoSaveInterval(int)));
|
||||
|
||||
m_saveIntervalLbl = new QLabel(auto_save_tw);
|
||||
m_saveIntervalLbl->setGeometry(10, 40, 200, 24);
|
||||
setAutoSaveInterval(m_saveIntervalSlider->value());
|
||||
|
||||
m_autoSave = new LedCheckBox(
|
||||
tr("Enable autosave"), auto_save_tw);
|
||||
m_autoSave->move(10, 70);
|
||||
m_autoSave->setChecked(m_enableAutoSave);
|
||||
connect(m_autoSave, SIGNAL(toggled(bool)),
|
||||
this, SLOT(toggleAutoSave(bool)));
|
||||
|
||||
m_runningAutoSave = new LedCheckBox(
|
||||
tr("Allow autosave while playing"), auto_save_tw);
|
||||
m_runningAutoSave->move(20, 88);
|
||||
m_runningAutoSave->setChecked(m_enableRunningAutoSave);
|
||||
connect(m_runningAutoSave, SIGNAL(toggled(bool)),
|
||||
this, SLOT(toggleRunningAutoSave(bool)));
|
||||
|
||||
auto autoSaveResetBtn = new QPushButton(embed::getIconPixmap("reload"), "", auto_save_tw);
|
||||
autoSaveResetBtn->setGeometry(320, 70, 28, 28);
|
||||
auto autoSaveResetBtn = new QPushButton(embed::getIconPixmap("reload"), "", autoSaveBox);
|
||||
autoSaveResetBtn->setFixedSize(32, 32);
|
||||
connect(autoSaveResetBtn, SIGNAL(clicked()),
|
||||
this, SLOT(resetAutoSave()));
|
||||
this, SLOT(resetAutoSave()));
|
||||
|
||||
autoSaveSubLayout->addWidget(m_saveIntervalSlider);
|
||||
autoSaveSubLayout->addWidget(autoSaveResetBtn);
|
||||
|
||||
autoSaveLayout->addLayout(autoSaveSubLayout);
|
||||
|
||||
m_saveIntervalLbl = new QLabel(autoSaveBox);
|
||||
setAutoSaveInterval(m_saveIntervalSlider->value());
|
||||
autoSaveLayout->addWidget(m_saveIntervalLbl);
|
||||
|
||||
m_autoSave = addCheckBox(tr("Enable autosave"), autoSaveBox, autoSaveLayout,
|
||||
m_enableAutoSave, SLOT(toggleAutoSave(bool)), false);
|
||||
|
||||
m_runningAutoSave = addCheckBox(tr("Allow autosave while playing"), autoSaveBox, autoSaveLayout,
|
||||
m_enableRunningAutoSave, SLOT(toggleRunningAutoSave(bool)), false);
|
||||
|
||||
m_saveIntervalSlider->setEnabled(m_enableAutoSave);
|
||||
m_runningAutoSave->setVisible(m_enableAutoSave);
|
||||
|
||||
|
||||
counter = 0;
|
||||
|
||||
// UI effect vs. performance tab.
|
||||
auto ui_fx_tw = new TabWidget(tr("User interface (UI) effects vs. performance"), performance_w);
|
||||
QGroupBox * uiFxBox = new QGroupBox(tr("User interface (UI) effects vs. performance"), performance_w);
|
||||
QVBoxLayout * uiFxLayout = new QVBoxLayout(uiFxBox);
|
||||
|
||||
addLedCheckBox(tr("Smooth scroll in song editor"), ui_fx_tw, counter,
|
||||
addCheckBox(tr("Smooth scroll in song editor"), uiFxBox, uiFxLayout,
|
||||
m_smoothScroll, SLOT(toggleSmoothScroll(bool)), false);
|
||||
addLedCheckBox(tr("Display playback cursor in AudioFileProcessor"), ui_fx_tw, counter,
|
||||
addCheckBox(tr("Display playback cursor in AudioFileProcessor"), uiFxBox, uiFxLayout,
|
||||
m_animateAFP, SLOT(toggleAnimateAFP(bool)), false);
|
||||
|
||||
ui_fx_tw->setFixedHeight(YDelta + YDelta * counter);
|
||||
|
||||
// Plugins group
|
||||
QGroupBox * pluginsBox = new QGroupBox(tr("Plugins"), performance_w);
|
||||
QVBoxLayout * pluginsLayout = new QVBoxLayout(pluginsBox);
|
||||
|
||||
counter = 0;
|
||||
|
||||
// Plugins tab.
|
||||
auto plugins_tw = new TabWidget(tr("Plugins"), performance_w);
|
||||
|
||||
m_vstEmbedLbl = new QLabel(plugins_tw);
|
||||
m_vstEmbedLbl->move(XDelta, YDelta * ++counter);
|
||||
m_vstEmbedLbl = new QLabel(pluginsBox);
|
||||
m_vstEmbedLbl->setText(tr("VST plugins embedding:"));
|
||||
pluginsLayout->addWidget(m_vstEmbedLbl);
|
||||
|
||||
m_vstEmbedComboBox = new QComboBox(plugins_tw);
|
||||
m_vstEmbedComboBox->move(XDelta, YDelta * ++counter);
|
||||
m_vstEmbedComboBox = new QComboBox(pluginsBox);
|
||||
|
||||
QStringList embedMethods = ConfigManager::availableVstEmbedMethods();
|
||||
m_vstEmbedComboBox->addItem(tr("No embedding"), "none");
|
||||
@@ -440,27 +428,19 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) :
|
||||
m_vstEmbedComboBox->setCurrentIndex(m_vstEmbedComboBox->findData(m_vstEmbedMethod));
|
||||
connect(m_vstEmbedComboBox, SIGNAL(currentIndexChanged(int)),
|
||||
this, SLOT(vstEmbedMethodChanged()));
|
||||
pluginsLayout->addWidget(m_vstEmbedComboBox);
|
||||
|
||||
counter += 2;
|
||||
m_vstAlwaysOnTopCheckBox = addCheckBox(tr("Keep plugin windows on top when not embedded"), pluginsBox, pluginsLayout,
|
||||
m_vstAlwaysOnTop, SLOT(toggleVSTAlwaysOnTop(bool)), false);
|
||||
|
||||
m_vstAlwaysOnTopCheckBox = new LedCheckBox(
|
||||
tr("Keep plugin windows on top when not embedded"), plugins_tw);
|
||||
m_vstAlwaysOnTopCheckBox->move(20, 66);
|
||||
m_vstAlwaysOnTopCheckBox->setChecked(m_vstAlwaysOnTop);
|
||||
m_vstAlwaysOnTopCheckBox->setVisible(m_vstEmbedMethod == "none");
|
||||
connect(m_vstAlwaysOnTopCheckBox, SIGNAL(toggled(bool)),
|
||||
this, SLOT(toggleVSTAlwaysOnTop(bool)));
|
||||
|
||||
addLedCheckBox(tr("Keep effects running even without input"), plugins_tw, counter,
|
||||
addCheckBox(tr("Keep effects running even without input"), pluginsBox, pluginsLayout,
|
||||
m_disableAutoQuit, SLOT(toggleDisableAutoQuit(bool)), false);
|
||||
|
||||
plugins_tw->setFixedHeight(YDelta + YDelta * counter);
|
||||
|
||||
|
||||
// Performance layout ordering.
|
||||
performance_layout->addWidget(auto_save_tw);
|
||||
performance_layout->addWidget(ui_fx_tw);
|
||||
performance_layout->addWidget(plugins_tw);
|
||||
performance_layout->addWidget(autoSaveBox);
|
||||
performance_layout->addWidget(uiFxBox);
|
||||
performance_layout->addWidget(pluginsBox);
|
||||
performance_layout->addStretch();
|
||||
|
||||
|
||||
@@ -473,17 +453,15 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) :
|
||||
labelWidget(audio_w,
|
||||
tr("Audio"));
|
||||
|
||||
// Audio interface tab.
|
||||
auto audioiface_tw = new TabWidget(tr("Audio interface"), audio_w);
|
||||
audioiface_tw->setFixedHeight(56);
|
||||
|
||||
m_audioInterfaces = new QComboBox(audioiface_tw);
|
||||
m_audioInterfaces->setGeometry(10, 20, 240, 28);
|
||||
// Audio interface group
|
||||
QGroupBox * audioInterfaceBox = new QGroupBox(tr("Audio interface"), audio_w);
|
||||
QVBoxLayout * audioInterfaceLayout = new QVBoxLayout(audioInterfaceBox);
|
||||
|
||||
m_audioInterfaces = new QComboBox(audioInterfaceBox);
|
||||
audioInterfaceLayout->addWidget(m_audioInterfaces);
|
||||
|
||||
// Ifaces-settings-widget.
|
||||
auto as_w = new QWidget(audio_w);
|
||||
as_w->setFixedHeight(60);
|
||||
|
||||
auto as_w_layout = new QHBoxLayout(as_w);
|
||||
as_w_layout->setSpacing(0);
|
||||
@@ -563,54 +541,58 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) :
|
||||
this, SLOT(audioInterfaceChanged(const QString&)));
|
||||
|
||||
// Advanced setting, hidden for now
|
||||
if(false)
|
||||
{
|
||||
auto useNaNHandler = new LedCheckBox(tr("Use built-in NaN handler"), audio_w);
|
||||
useNaNHandler->setChecked(m_NaNHandler);
|
||||
}
|
||||
// // TODO Handle or remove.
|
||||
// auto useNaNHandler = new LedCheckBox(tr("Use built-in NaN handler"), audio_w);
|
||||
// audio_layout->addWidget(useNaNHandler);
|
||||
// useNaNHandler->setChecked(m_NaNHandler);
|
||||
|
||||
// HQ mode LED.
|
||||
auto hqaudio = new LedCheckBox(tr("HQ mode for output audio device"), audio_w);
|
||||
hqaudio->move(10, 0);
|
||||
hqaudio->setChecked(m_hqAudioDev);
|
||||
connect(hqaudio, SIGNAL(toggled(bool)),
|
||||
this, SLOT(toggleHQAudioDev(bool)));
|
||||
// HQ mode checkbox
|
||||
auto hqaudio = addCheckBox(tr("HQ mode for output audio device"), audioInterfaceBox, nullptr,
|
||||
m_hqAudioDev, SLOT(toggleHQAudioDev(bool)), false);
|
||||
|
||||
// Buffer size group
|
||||
QGroupBox * bufferSizeBox = new QGroupBox(tr("Buffer size"), audio_w);
|
||||
QVBoxLayout * bufferSizeLayout = new QVBoxLayout(bufferSizeBox);
|
||||
QHBoxLayout * bufferSizeSubLayout = new QHBoxLayout();
|
||||
|
||||
// Buffer size tab.
|
||||
auto bufferSize_tw = new TabWidget(tr("Buffer size"), audio_w);
|
||||
bufferSize_tw->setFixedHeight(76);
|
||||
|
||||
m_bufferSizeSlider = new QSlider(Qt::Horizontal, bufferSize_tw);
|
||||
m_bufferSizeSlider = new QSlider(Qt::Horizontal, bufferSizeBox);
|
||||
m_bufferSizeSlider->setRange(1, 128);
|
||||
m_bufferSizeSlider->setTickInterval(8);
|
||||
m_bufferSizeSlider->setPageStep(8);
|
||||
m_bufferSizeSlider->setValue(m_bufferSize / BUFFERSIZE_RESOLUTION);
|
||||
m_bufferSizeSlider->setGeometry(10, 18, 340, 18);
|
||||
m_bufferSizeSlider->setTickPosition(QSlider::TicksBelow);
|
||||
|
||||
connect(m_bufferSizeSlider, SIGNAL(valueChanged(int)),
|
||||
this, SLOT(setBufferSize(int)));
|
||||
connect(m_bufferSizeSlider, SIGNAL(valueChanged(int)),
|
||||
this, SLOT(showRestartWarning()));
|
||||
bufferSizeSubLayout->addWidget(m_bufferSizeSlider, 1);
|
||||
|
||||
m_bufferSizeLbl = new QLabel(bufferSize_tw);
|
||||
m_bufferSizeLbl->setGeometry(10, 40, 200, 24);
|
||||
setBufferSize(m_bufferSizeSlider->value());
|
||||
|
||||
auto bufferSize_reset_btn = new QPushButton(embed::getIconPixmap("reload"), "", bufferSize_tw);
|
||||
bufferSize_reset_btn->setGeometry(320, 40, 28, 28);
|
||||
auto bufferSize_reset_btn = new QPushButton(embed::getIconPixmap("reload"), "", bufferSizeBox);
|
||||
bufferSize_reset_btn->setFixedSize(32, 32);
|
||||
connect(bufferSize_reset_btn, SIGNAL(clicked()),
|
||||
this, SLOT(resetBufferSize()));
|
||||
this, SLOT(resetBufferSize()));
|
||||
bufferSize_reset_btn->setToolTip(
|
||||
tr("Reset to default value"));
|
||||
tr("Reset to default value"));
|
||||
|
||||
bufferSizeSubLayout->addWidget(bufferSize_reset_btn);
|
||||
bufferSizeLayout->addLayout(bufferSizeSubLayout);
|
||||
|
||||
m_bufferSizeLbl = new QLabel(bufferSizeBox);
|
||||
bufferSizeLayout->addWidget(m_bufferSizeLbl);
|
||||
|
||||
m_bufferSizeWarnLbl = new QLabel(bufferSizeBox);
|
||||
m_bufferSizeWarnLbl->setWordWrap(true);
|
||||
bufferSizeLayout->addWidget(m_bufferSizeWarnLbl);
|
||||
|
||||
setBufferSize(m_bufferSizeSlider->value());
|
||||
|
||||
|
||||
// Audio layout ordering.
|
||||
audio_layout->addWidget(audioiface_tw);
|
||||
audio_layout->addWidget(audioInterfaceBox);
|
||||
audio_layout->addWidget(as_w);
|
||||
audio_layout->addWidget(hqaudio);
|
||||
audio_layout->addWidget(bufferSize_tw);
|
||||
audio_layout->addWidget(bufferSizeBox);
|
||||
audio_layout->addStretch();
|
||||
|
||||
|
||||
@@ -620,19 +602,17 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) :
|
||||
auto midi_layout = new QVBoxLayout(midi_w);
|
||||
midi_layout->setSpacing(10);
|
||||
midi_layout->setContentsMargins(0, 0, 0, 0);
|
||||
labelWidget(midi_w,
|
||||
tr("MIDI"));
|
||||
labelWidget(midi_w, tr("MIDI"));
|
||||
|
||||
// MIDI interface tab.
|
||||
auto midiiface_tw = new TabWidget(tr("MIDI interface"), midi_w);
|
||||
midiiface_tw->setFixedHeight(56);
|
||||
// MIDI interface group
|
||||
QGroupBox * midiInterfaceBox = new QGroupBox(tr("MIDI interface"), midi_w);
|
||||
QVBoxLayout * midiInterfaceLayout = new QVBoxLayout(midiInterfaceBox);
|
||||
|
||||
m_midiInterfaces = new QComboBox(midiiface_tw);
|
||||
m_midiInterfaces->setGeometry(10, 20, 240, 28);
|
||||
m_midiInterfaces = new QComboBox(midiInterfaceBox);
|
||||
midiInterfaceLayout->addWidget(m_midiInterfaces);
|
||||
|
||||
// Ifaces-settings-widget.
|
||||
auto ms_w = new QWidget(midi_w);
|
||||
ms_w->setFixedHeight(60);
|
||||
|
||||
auto ms_w_layout = new QHBoxLayout(ms_w);
|
||||
ms_w_layout->setSpacing(0);
|
||||
@@ -702,12 +682,12 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) :
|
||||
this, SLOT(midiInterfaceChanged(const QString&)));
|
||||
|
||||
|
||||
// MIDI autoassign tab.
|
||||
auto midiAutoAssign_tw = new TabWidget(tr("Automatically assign MIDI controller to selected track"), midi_w);
|
||||
midiAutoAssign_tw->setFixedHeight(56);
|
||||
// MIDI autoassign group
|
||||
QGroupBox * midiAutoAssignBox = new QGroupBox(tr("Automatically assign MIDI controller to selected track"), midi_w);
|
||||
QVBoxLayout * midiAutoAssignLayout = new QVBoxLayout(midiAutoAssignBox);
|
||||
|
||||
m_assignableMidiDevices = new QComboBox(midiAutoAssign_tw);
|
||||
m_assignableMidiDevices->setGeometry(10, 20, 240, 28);
|
||||
m_assignableMidiDevices = new QComboBox(midiAutoAssignBox);
|
||||
midiAutoAssignLayout->addWidget(m_assignableMidiDevices);
|
||||
m_assignableMidiDevices->addItem("none");
|
||||
if ( !Engine::audioEngine()->midiClient()->isRaw() )
|
||||
{
|
||||
@@ -724,9 +704,9 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) :
|
||||
}
|
||||
|
||||
// MIDI layout ordering.
|
||||
midi_layout->addWidget(midiiface_tw);
|
||||
midi_layout->addWidget(midiInterfaceBox);
|
||||
midi_layout->addWidget(ms_w);
|
||||
midi_layout->addWidget(midiAutoAssign_tw);
|
||||
midi_layout->addWidget(midiAutoAssignBox);
|
||||
midi_layout->addStretch();
|
||||
|
||||
|
||||
@@ -749,29 +729,29 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) :
|
||||
// Path selectors widget.
|
||||
auto pathSelectors = new QWidget(paths_w);
|
||||
|
||||
const int txtLength = 284;
|
||||
const int btnStart = 300;
|
||||
|
||||
// Path selectors layout.
|
||||
auto pathSelectorsLayout = new QVBoxLayout;
|
||||
pathSelectorsLayout->setSpacing(10);
|
||||
pathSelectorsLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
auto addPathEntry = [&](const QString& caption, const QString& content, const char* setSlot, const char* openSlot,
|
||||
QLineEdit*& lineEdit, const char* pixmap = "project_open") {
|
||||
auto newTw = new TabWidget(caption, pathSelectors);
|
||||
newTw->setFixedHeight(48);
|
||||
auto pathEntryGroupBox = new QGroupBox(caption, pathSelectors);
|
||||
QHBoxLayout * pathEntryLayout = new QHBoxLayout(pathEntryGroupBox);
|
||||
|
||||
lineEdit = new QLineEdit(content, newTw);
|
||||
lineEdit->setGeometry(10, 20, txtLength, 16);
|
||||
lineEdit = new QLineEdit(content, pathEntryGroupBox);
|
||||
connect(lineEdit, SIGNAL(textChanged(const QString&)),
|
||||
this, setSlot);
|
||||
|
||||
auto selectBtn = new QPushButton(embed::getIconPixmap(pixmap, 16, 16), "", newTw);
|
||||
pathEntryLayout->addWidget(lineEdit, 1);
|
||||
|
||||
auto selectBtn = new QPushButton(embed::getIconPixmap(pixmap, 16, 16), "", pathEntryGroupBox);
|
||||
selectBtn->setFixedSize(24, 24);
|
||||
selectBtn->move(btnStart, 16);
|
||||
connect(selectBtn, SIGNAL(clicked()), this, openSlot);
|
||||
|
||||
pathSelectorsLayout->addWidget(newTw);
|
||||
pathEntryLayout->addWidget(selectBtn, 0);
|
||||
|
||||
pathSelectorsLayout->addWidget(pathEntryGroupBox);
|
||||
pathSelectorsLayout->addSpacing(10);
|
||||
};
|
||||
|
||||
@@ -817,24 +797,32 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) :
|
||||
pathsScroll->setWidget(pathSelectors);
|
||||
pathsScroll->setWidgetResizable(true);
|
||||
|
||||
paths_layout->addWidget(pathsScroll);
|
||||
paths_layout->addWidget(pathsScroll, 1);
|
||||
paths_layout->addStretch();
|
||||
|
||||
// Add all main widgets to the layout of the settings widget
|
||||
// This is needed so that we automatically get the correct sizes.
|
||||
settingsLayout->addWidget(general_w);
|
||||
settingsLayout->addWidget(performance_w);
|
||||
settingsLayout->addWidget(audio_w);
|
||||
settingsLayout->addWidget(midi_w);
|
||||
settingsLayout->addWidget(paths_w);
|
||||
|
||||
// Major tabs ordering.
|
||||
m_tabBar->addTab(general_w,
|
||||
tr("General"), 0, false, true)->setIcon(
|
||||
tr("General"), 0, false, true, false)->setIcon(
|
||||
embed::getIconPixmap("setup_general"));
|
||||
m_tabBar->addTab(performance_w,
|
||||
tr("Performance"), 1, false, true)->setIcon(
|
||||
tr("Performance"), 1, false, true, false)->setIcon(
|
||||
embed::getIconPixmap("setup_performance"));
|
||||
m_tabBar->addTab(audio_w,
|
||||
tr("Audio"), 2, false, true)->setIcon(
|
||||
tr("Audio"), 2, false, true, false)->setIcon(
|
||||
embed::getIconPixmap("setup_audio"));
|
||||
m_tabBar->addTab(midi_w,
|
||||
tr("MIDI"), 3, false, true)->setIcon(
|
||||
tr("MIDI"), 3, false, true, false)->setIcon(
|
||||
embed::getIconPixmap("setup_midi"));
|
||||
m_tabBar->addTab(paths_w,
|
||||
tr("Paths"), 4, true, true)->setIcon(
|
||||
tr("Paths"), 4, true, true, false)->setIcon(
|
||||
embed::getIconPixmap("setup_directories"));
|
||||
|
||||
m_tabBar->setActiveTab(static_cast<int>(tab_to_open));
|
||||
@@ -877,11 +865,14 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) :
|
||||
extras_layout->addSpacing(10);
|
||||
|
||||
// Vertical layout ordering.
|
||||
vlayout->addWidget(main_w);
|
||||
vlayout->addWidget(main_w, 1);
|
||||
vlayout->addSpacing(10);
|
||||
vlayout->addWidget(extras_w);
|
||||
vlayout->addSpacing(10);
|
||||
|
||||
// Ensure that we cannot make the dialog smaller than it wants to be
|
||||
setMinimumWidth(width());
|
||||
|
||||
show();
|
||||
}
|
||||
|
||||
@@ -1172,6 +1163,24 @@ void SetupDialog::audioInterfaceChanged(const QString & iface)
|
||||
}
|
||||
|
||||
|
||||
void SetupDialog::updateBufferSizeWarning(int value)
|
||||
{
|
||||
QString text = "<ul>";
|
||||
if((value & (value - 1)) != 0) // <=> value is not a power of 2 (for value > 0)
|
||||
{
|
||||
text += "<li>" + tr("The currently selected value is not a power of 2 "
|
||||
"(32, 64, 128, 256, 512, 1024, ...). Some plugins may not be available.") + "</li>";
|
||||
}
|
||||
if(value <= 32)
|
||||
{
|
||||
text += "<li>" + tr("The currently selected value is less than or equal to 32. "
|
||||
"Some plugins may not be available.") + "</li>";
|
||||
}
|
||||
text += "</ul>";
|
||||
m_bufferSizeWarnLbl->setText(text);
|
||||
}
|
||||
|
||||
|
||||
void SetupDialog::setBufferSize(int value)
|
||||
{
|
||||
const int step = DEFAULT_BUFFER_SIZE / BUFFERSIZE_RESOLUTION;
|
||||
@@ -1197,6 +1206,7 @@ void SetupDialog::setBufferSize(int value)
|
||||
m_bufferSize = value * BUFFERSIZE_RESOLUTION;
|
||||
m_bufferSizeLbl->setText(tr("Frames: %1\nLatency: %2 ms").arg(m_bufferSize).arg(
|
||||
1000.0f * m_bufferSize / Engine::audioEngine()->processingSampleRate(), 0, 'f', 1));
|
||||
updateBufferSizeWarning(m_bufferSize);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -195,7 +195,7 @@ bool TrackOperationsWidget::confirmRemoval()
|
||||
QString messageTitleRemoveTrack = tr("Confirm removal");
|
||||
QString askAgainText = tr("Don't ask again");
|
||||
auto askAgainCheckBox = new QCheckBox(askAgainText, nullptr);
|
||||
connect(askAgainCheckBox, &QCheckBox::stateChanged, [this](int state){
|
||||
connect(askAgainCheckBox, &QCheckBox::stateChanged, [](int state){
|
||||
// Invert button state, if it's checked we *shouldn't* ask again
|
||||
ConfigManager::inst()->setValue("ui", "trackdeletionwarning", state ? "0" : "1");
|
||||
});
|
||||
|
||||
@@ -44,7 +44,7 @@ TabBar::TabBar( QWidget * _parent, QBoxLayout::Direction _dir ) :
|
||||
}
|
||||
|
||||
TabButton * TabBar::addTab( QWidget * _w, const QString & _text, int _id,
|
||||
bool _add_stretch, bool _text_is_tooltip )
|
||||
bool _add_stretch, bool _text_is_tooltip, bool fixWidgetToParentSize )
|
||||
{
|
||||
// already tab with id?
|
||||
if( m_tabs.contains( _id ) )
|
||||
@@ -83,10 +83,12 @@ TabButton * TabBar::addTab( QWidget * _w, const QString & _text, int _id,
|
||||
m_layout->addStretch();
|
||||
}
|
||||
|
||||
|
||||
// we assume, parent-widget is a widget acting as widget-stack so all
|
||||
// widgets have the same size and only the one on the top is visible
|
||||
_w->setFixedSize( _w->parentWidget()->size() );
|
||||
if (fixWidgetToParentSize)
|
||||
{
|
||||
// we assume, parent-widget is a widget acting as widget-stack so all
|
||||
// widgets have the same size and only the one on the top is visible
|
||||
_w->setFixedSize( _w->parentWidget()->size() );
|
||||
}
|
||||
|
||||
b->setFont( pointSize<8>( b->font() ) );
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ ADD_EXECUTABLE(tests
|
||||
|
||||
src/core/ArrayVectorTest.cpp
|
||||
src/core/AutomatableModelTest.cpp
|
||||
src/core/MathTest.cpp
|
||||
src/core/ProjectVersionTest.cpp
|
||||
src/core/RelativePathsTest.cpp
|
||||
|
||||
|
||||
53
tests/src/core/MathTest.cpp
Normal file
53
tests/src/core/MathTest.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* MathTest.cpp
|
||||
*
|
||||
* Copyright (c) 2023 Johannes Lorenz <jlsf2013$users.sourceforge.net, $=@>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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 (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "QTestSuite.h"
|
||||
|
||||
#include "lmms_math.h"
|
||||
|
||||
#include <QDir>
|
||||
|
||||
class MathTest : QTestSuite
|
||||
{
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void NumDigitsTest()
|
||||
{
|
||||
using namespace lmms;
|
||||
QCOMPARE(numDigitsAsInt(1.f), 1);
|
||||
QCOMPARE(numDigitsAsInt(9.9f), 2);
|
||||
QCOMPARE(numDigitsAsInt(10.f), 2);
|
||||
QCOMPARE(numDigitsAsInt(0.f), 1);
|
||||
QCOMPARE(numDigitsAsInt(-100.f), 4);
|
||||
QCOMPARE(numDigitsAsInt(-99.f), 3);
|
||||
QCOMPARE(numDigitsAsInt(-0.4f), 1); // there is no "-0" for LED spinbox
|
||||
QCOMPARE(numDigitsAsInt(-0.99f), 2);
|
||||
QCOMPARE(numDigitsAsInt(1000000000), 10);
|
||||
QCOMPARE(numDigitsAsInt(-1000000000), 11);
|
||||
QCOMPARE(numDigitsAsInt(900000000), 9);
|
||||
QCOMPARE(numDigitsAsInt(-900000000), 10);
|
||||
}
|
||||
} MathTests;
|
||||
|
||||
#include "MathTest.moc"
|
||||
Reference in New Issue
Block a user